From 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:44:46 -0500 Subject: Second Life viewer sources 1.13.2.12 --- linden/indra/newview/llselectmgr.cpp | 6761 ++++++++++++++++++++++++++++++++++ 1 file changed, 6761 insertions(+) create mode 100644 linden/indra/newview/llselectmgr.cpp (limited to 'linden/indra/newview/llselectmgr.cpp') diff --git a/linden/indra/newview/llselectmgr.cpp b/linden/indra/newview/llselectmgr.cpp new file mode 100644 index 0000000..9b13441 --- /dev/null +++ b/linden/indra/newview/llselectmgr.cpp @@ -0,0 +1,6761 @@ +/** + * @file llselectmgr.cpp + * @brief A manager for selected objects and faces. + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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. + */ + +#include "llviewerprecompiledheaders.h" + +// file include +#include "llselectmgr.h" + +// library includes +#include "llcachename.h" +#include "lldbstrings.h" +#include "lleconomy.h" +#include "llgl.h" +#include "llpermissions.h" +#include "llpermissionsflags.h" +#include "llptrskiplist.h" +#include "llundo.h" +#include "lluuid.h" +#include "llvolume.h" +#include "message.h" +#include "object_flags.h" +#include "llquaternion.h" + +// viewer includes +#include "llagent.h" +#include "llviewerwindow.h" +#include "lldrawable.h" +#include "llfloaterinspect.h" +#include "llfloaterproperties.h" +#include "llfloaterrate.h" +#include "llfloaterreporter.h" +#include "llfloatertools.h" +#include "llframetimer.h" +#include "llhudeffecttrail.h" +#include "llhudmanager.h" +#include "llinventorymodel.h" +#include "llmenugl.h" +#include "llstatusbar.h" +#include "llsurface.h" +#include "lltool.h" +#include "lltooldraganddrop.h" +#include "lltoolmgr.h" +#include "lltoolpie.h" +#include "llui.h" +#include "llviewercamera.h" +#include "llviewercontrol.h" +#include "llviewerimagelist.h" +#include "llviewermenu.h" +#include "llviewerobject.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" +#include "llviewerstats.h" +#include "llvoavatar.h" +#include "llvovolume.h" +#include "pipeline.h" + +#include "llglheaders.h" + +// +// Consts +// + +const S32 NUM_SELECTION_UNDO_ENTRIES = 200; +const F32 SILHOUETTE_UPDATE_THRESHOLD_SQUARED = 0.02f; +const S32 OWNERSHIP_COST_PER_OBJECT = 10; // Must be the same as economy_constants.price_object_claim in the database. +const S32 MAX_ACTION_QUEUE_SIZE = 20; +const S32 MAX_SILS_PER_FRAME = 50; +const S32 MAX_OBJECTS_PER_PACKET = 254; + +extern LLGlobalEconomy *gGlobalEconomy; +extern LLUUID gLastHitObjectID; +extern LLVector3d gLastHitObjectOffset; + +// +// Globals +// +LLSelectMgr* gSelectMgr = NULL; + +BOOL gDebugSelectMgr = FALSE; + +BOOL gHideSelectedObjects = FALSE; +BOOL gAllowSelectAvatar = FALSE; + +BOOL LLSelectMgr::sRectSelectInclusive = TRUE; +BOOL LLSelectMgr::sRenderHiddenSelections = TRUE; +BOOL LLSelectMgr::sRenderLightRadius = FALSE; +F32 LLSelectMgr::sHighlightThickness = 0.f; +F32 LLSelectMgr::sHighlightUScale = 0.f; +F32 LLSelectMgr::sHighlightVScale = 0.f; +F32 LLSelectMgr::sHighlightAlpha = 0.f; +F32 LLSelectMgr::sHighlightAlphaTest = 0.f; +F32 LLSelectMgr::sHighlightUAnim = 0.f; +F32 LLSelectMgr::sHighlightVAnim = 0.f; +LLColor4 LLSelectMgr::sSilhouetteParentColor; +LLColor4 LLSelectMgr::sSilhouetteChildColor; +LLColor4 LLSelectMgr::sHighlightInspectColor; +LLColor4 LLSelectMgr::sHighlightParentColor; +LLColor4 LLSelectMgr::sHighlightChildColor; +LLColor4 LLSelectMgr::sContextSilhouetteColor; + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// struct LLDeRezInfo +// +// Used to keep track of important derez info. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +struct LLDeRezInfo +{ + EDeRezDestination mDestination; + LLUUID mDestinationID; + LLDeRezInfo(EDeRezDestination dest, const LLUUID& dest_id) : + mDestination(dest), mDestinationID(dest_id) {} +}; + +// +// Imports +// + + +// +// Functions +// + +//----------------------------------------------------------------------------- +// LLSelectMgr() +//----------------------------------------------------------------------------- +LLSelectMgr::LLSelectMgr() +{ + mTEMode = FALSE; + mLastCameraPos.clearVec(); + + sHighlightThickness = gSavedSettings.getF32("SelectionHighlightThickness"); + sHighlightUScale = gSavedSettings.getF32("SelectionHighlightUScale"); + sHighlightVScale = gSavedSettings.getF32("SelectionHighlightVScale"); + sHighlightAlpha = gSavedSettings.getF32("SelectionHighlightAlpha"); + sHighlightAlphaTest = gSavedSettings.getF32("SelectionHighlightAlphaTest"); + sHighlightUAnim = gSavedSettings.getF32("SelectionHighlightUAnim"); + sHighlightVAnim = gSavedSettings.getF32("SelectionHighlightVAnim"); + + sSilhouetteParentColor = gColors.getColor("SilhouetteParentColor"); + sSilhouetteChildColor = gColors.getColor("SilhouetteChildColor"); + sHighlightParentColor = gColors.getColor("HighlightParentColor"); + sHighlightChildColor = gColors.getColor("HighlightChildColor"); + sHighlightInspectColor = gColors.getColor("HighlightInspectColor"); + sContextSilhouetteColor = gColors.getColor("ContextSilhouetteColor")*0.5f; + + sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius"); + + mRenderSilhouettes = TRUE; + + mGridMode = GRID_MODE_WORLD; + gSavedSettings.setS32("GridMode", (S32)GRID_MODE_WORLD); + mGridValid = FALSE; + + mSelectType = SELECT_TYPE_WORLD; +} + + +//----------------------------------------------------------------------------- +// ~LLSelectMgr() +//----------------------------------------------------------------------------- +LLSelectMgr::~LLSelectMgr() +{ + mHoverObjects.deleteAllNodes(); + mSelectedObjects.deleteAllNodes(); + mHighlightedObjects.deleteAllNodes(); + mRectSelectedObjects.clear(); + mGridObjects.deleteAllNodes(); + mUndoQueue.clear(); + mRedoQueue.clear(); +} + +bool LLSelectMgr::applyToObjects(LLSelectedObjectFunctor* func) +{ + bool result = true; + LLViewerObject* object; + for (object = getFirstObject(); object != NULL; object = getNextObject()) + { + result = result && func->apply(object); + } + return result; +} + +bool LLSelectMgr::applyToRootObjects(LLSelectedObjectFunctor* func) +{ + bool result = true; + LLViewerObject* object; + for (object = getFirstRootObject(); + object != NULL; + object = getNextRootObject()) + { + result = result && func->apply(object); + } + return result; +} + +bool LLSelectMgr::applyToNodes(LLSelectedNodeFunctor *func) +{ + bool result = true; + LLSelectNode* node; + for (node = getFirstNode(); node != NULL; node = getNextNode()) + { + result = result && func->apply(node); + } + return result; +} + +void LLSelectMgr::updateEffects() +{ + if (mEffectsTimer.getElapsedTimeF32() > 1.f) + { + mSelectedObjects.updateEffects(); + mEffectsTimer.reset(); + } +} + +//----------------------------------------------------------------------------- +// Select just the object, not any other group members. +//----------------------------------------------------------------------------- +void LLSelectMgr::selectObjectOnly(LLViewerObject* object, S32 face) +{ + llassert( object ); + + // Don't add an object that is already in the list + if (object->isSelected() ) { + // make sure point at position is updated + updatePointAt(); + gEditMenuHandler = this; + return; + } + + if (!canSelectObject(object)) + { + //make_ui_sound("UISndInvalidOp"); + return; + } + + // llinfos << "Adding object to selected object list" << llendl; + + // Place it in the list and tag it. + // This will refresh dialogs. + addAsIndividual(object, face); + + // Stop the object from moving (this anticipates changes on the + // simulator in LLTask::userSelect) + // *FIX: shouldn't zero out these either + object->setVelocity(LLVector3::zero); + object->setAcceleration(LLVector3::zero); + //object->setAngularVelocity(LLVector3::zero); + object->resetRot(); + + // Always send to simulator, so you get a copy of the + // permissions structure back. + gMessageSystem->newMessageFast(_PREHASH_ObjectSelect); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); + LLViewerRegion* regionp = object->getRegion(); + gMessageSystem->sendReliable( regionp->getHost()); + + updatePointAt(); + updateSelectionCenter(); + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + + // have selection manager handle edit menu immediately after + // user selects an object + if (getObjectCount()) + { + gEditMenuHandler = this; + } +} + +//----------------------------------------------------------------------------- +// Select the object, parents and children. +//----------------------------------------------------------------------------- +void LLSelectMgr::selectObjectAndFamily(LLViewerObject* obj, BOOL add_to_end) +{ + llassert( obj ); + + // This may be incorrect if things weren't family selected before... - djs 07/08/02 + // Don't add an object that is already in the list + if (obj->isSelected() ) + { + // make sure pointat position is updated + updatePointAt(); + gEditMenuHandler = this; + return; + } + + if (!canSelectObject(obj)) + { + //make_ui_sound("UISndInvalidOp"); + return; + } + + // Since we're selecting a family, start at the root, but + // don't include an avatar. + LLViewerObject* root = obj; + + while(!root->isAvatar() && root->getParent() && !root->isJointChild()) + { + LLViewerObject* parent = (LLViewerObject*)root->getParent(); + if (parent->isAvatar()) + { + break; + } + root = parent; + } + + // Collect all of the objects + LLDynamicArray objects; + + root->addThisAndNonJointChildren(objects); + addAsFamily(objects, add_to_end); + + updateSelectionCenter(); + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + updatePointAt(); + + dialog_refresh_all(); + + // Always send to simulator, so you get a copy of the permissions + // structure back. + sendSelect(); + + // Stop the object from moving (this anticipates changes on the + // simulator in LLTask::userSelect) + root->setVelocity(LLVector3::zero); + root->setAcceleration(LLVector3::zero); + //root->setAngularVelocity(LLVector3::zero); + root->resetRot(); + + // leave component mode + if (!gSavedSettings.getBOOL("SelectLinkedSet")) + { + gSavedSettings.setBOOL("SelectLinkedSet", TRUE); + promoteSelectionToRoot(); + } + + // have selection manager handle edit menu immediately after + // user selects an object + if (getObjectCount()) + { + gEditMenuHandler = this; + } +} + +//----------------------------------------------------------------------------- +// Select the object, parents and children. +//----------------------------------------------------------------------------- +void LLSelectMgr::selectObjectAndFamily(const LLDynamicArray& object_list, + BOOL send_to_sim) +{ + // Collect all of the objects, children included + LLDynamicArray objects; + LLViewerObject *object; + S32 i; + + if (object_list.count() < 1) return; + + // NOTE -- we add the objects in REVERSE ORDER + // to preserve the order in the mSelectedObjects list + for (i = object_list.count() - 1; i >= 0; i--) + { + object = object_list.get(i); + + llassert( object ); + + if (!canSelectObject(object)) continue; + + object->addThisAndNonJointChildren(objects); + addAsFamily(objects); + + // Stop the object from moving (this anticipates changes on the + // simulator in LLTask::userSelect) + object->setVelocity(LLVector3::zero); + object->setAcceleration(LLVector3::zero); + //object->setAngularVelocity(LLVector3::zero); + object->resetRot(); + } + + updateSelectionCenter(); + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + updatePointAt(); + dialog_refresh_all(); + + // Almost always send to simulator, so you get a copy of the permissions + // structure back. + // JC: The one case where you don't want to do this is if you're selecting + // all the objects on a sim. + if (send_to_sim) + { + sendSelect(); + } + + // leave component mode + if (!gSavedSettings.getBOOL("SelectLinkedSet")) + { + gSavedSettings.setBOOL("SelectLinkedSet", TRUE); + promoteSelectionToRoot(); + } + + // have selection manager handle edit menu immediately after + // user selects an object + if (getObjectCount()) + { + gEditMenuHandler = this; + } +} + +// Use for when the simulator kills an object. This version also +// handles informing the current tool of the object's deletion. +// +// Caller needs to call dialog_refresh_all if necessary. +BOOL LLSelectMgr::selectionRemoveObject(const LLUUID &id) +{ + + + BOOL object_found = FALSE; + LLTool *tool = NULL; + if (!gNoRender) + { + tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) ); + + // It's possible that the tool is editing an object that is not selected + LLViewerObject* tool_editing_object = tool->getEditingObject(); + if( tool_editing_object && tool_editing_object->mID == id) + { + tool->stopEditing(); + object_found = TRUE; + } + } + + // Iterate through selected objects list and kill the object + if( !object_found ) + { + LLViewerObject* prevobjp = NULL; + for( LLViewerObject* tobjp = getFirstObject(); tobjp != NULL; tobjp = getNextObject() ) + { + if (tobjp == prevobjp) + { + // Somehow we got stuck in an infinite loop... (DaveP) + // this logic is kind of twisted, not sure how this is happening, so... + llwarns << "Detected infinite loop #1 in LLSelectMgr::selectionRemoveObject:|" << llendl; + //MikeS. adding warning and comment... + //These infinite loops happen because the LLSelectMgr iteration routines are non-reentrant. + //deselectObjectAndFamily uses getFirstObject and getNextObject to mess with the array, + //resetting the arrays internal iterator state. This needs fixing BAD. + continue; + } + // It's possible the item being removed has an avatar sitting on it + // So remove the avatar that is sitting on the object. + if (tobjp->mID == id || tobjp->isAvatar()) + { + if (!gNoRender) + { + tool->stopEditing(); + } + + // lose the selection, don't tell simulator, it knows + deselectObjectAndFamily(tobjp, FALSE); + + if (tobjp->mID == id) + { + if(object_found == TRUE){ + //MikeS. adding warning... This happens when removing a linked attachment while sitting on an object.. + //I think the selection manager needs to be rewritten. BAD. + llwarns << "Detected infinite loop #2 in LLSelectMgr::selectionRemoveObject:|" << llendl; + break; + } + object_found = TRUE; + } + } + prevobjp = tobjp; + } + } + + return object_found; +} + +void LLSelectMgr::deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_sim) +{ + // bail if nothing selected or if object wasn't selected in the first place + if(!object) return; + if(!object->isSelected()) return; + + // Collect all of the objects, and remove them + LLDynamicArray objects; + object = (LLViewerObject*)object->getRoot(); + object->addThisAndAllChildren(objects); + remove(objects); + + if (!send_to_sim) return; + + //----------------------------------------------------------- + // Inform simulator of deselection + //----------------------------------------------------------- + LLViewerRegion* regionp = object->getRegion(); + + BOOL start_new_message = TRUE; + S32 select_count = 0; + + LLMessageSystem* msg = gMessageSystem; + for (S32 i = 0; i < objects.count(); i++) + { + if (start_new_message) + { + msg->newMessageFast(_PREHASH_ObjectDeselect); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + select_count++; + start_new_message = FALSE; + } + + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addU32Fast(_PREHASH_ObjectLocalID, (objects[i])->getLocalID()); + select_count++; + + if(msg->mCurrentSendTotal >= MTUBYTES || select_count >= MAX_OBJECTS_PER_PACKET) + { + msg->sendReliable(regionp->getHost() ); + select_count = 0; + start_new_message = TRUE; + } + } + + if (!start_new_message) + { + msg->sendReliable(regionp->getHost() ); + } + + updatePointAt(); + updateSelectionCenter(); +} + +void LLSelectMgr::deselectObjectOnly(LLViewerObject* object, BOOL send_to_sim) +{ + // bail if nothing selected or if object wasn't selected in the first place + if (!object) return; + if (!object->isSelected() ) return; + + if (send_to_sim) + { + LLViewerRegion* region = object->getRegion(); + gMessageSystem->newMessageFast(_PREHASH_ObjectDeselect); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); + gMessageSystem->sendReliable(region->getHost()); + } + + // This will refresh dialogs. + remove( object ); + + updatePointAt(); + updateSelectionCenter(); +} + + +//----------------------------------------------------------------------------- +// addAsFamily +//----------------------------------------------------------------------------- + +void LLSelectMgr::addAsFamily(LLDynamicArray& objects, BOOL add_to_end) +{ + S32 count = objects.count(); + LLViewerObject *objectp = NULL; + + LLSelectNode *nodep = NULL; + for (S32 i = 0; i < count; i++) + { + objectp = objects.get(i); + + // Can't select yourself + if (objectp->mID == gAgentID + && !gAllowSelectAvatar) + { + continue; + } + + if (!objectp->isSelected()) + { + nodep = new LLSelectNode(objectp, TRUE); + if (add_to_end) + { + mSelectedObjects.addNodeAtEnd(nodep); + } + else + { + mSelectedObjects.addNode(nodep); + } + objectp->setSelected(TRUE); + + if (objectp->getNumTEs() > 0) + { + nodep->selectAllTEs(TRUE); + } + else + { + // object has no faces, so don't mess with faces + } + } + else + { + // we want this object to be selected for real + // so clear transient flag + LLSelectNode* select_node = findSelectNode(objectp); + if (select_node) + { + select_node->setTransient(FALSE); + } + } + } + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); +} + +//----------------------------------------------------------------------------- +// addAsIndividual() - a single object, face, etc +//----------------------------------------------------------------------------- +void LLSelectMgr::addAsIndividual(LLViewerObject *objectp, S32 face, BOOL undoable) +{ + // check to see if object is already in list + LLSelectNode *nodep = findSelectNode(objectp); + + // if not in list, add it + if (!nodep) + { + nodep = new LLSelectNode(objectp, TRUE); + mSelectedObjects.addNode(nodep); + } + else + { + // make this a full-fledged selection + nodep->setTransient(FALSE); + // Move it to the front of the list + mSelectedObjects.removeNode(nodep); + mSelectedObjects.addNode(nodep); + } + + // Make sure the object is tagged as selected + objectp->setSelected( TRUE ); + + // And make sure we don't consider it as part of a family + nodep->mIndividualSelection = TRUE; + + // Handle face selection + if (objectp->getNumTEs() <= 0) + { + // object has no faces, so don't do anything + } + else if (face == SELECT_ALL_TES) + { + nodep->selectAllTEs(TRUE); + } + else if (0 <= face && face < SELECT_MAX_TES) + { + nodep->selectTE(face, TRUE); + } + else + { + llerrs << "LLSelectMgr::add face " << face << " out-of-range" << llendl; + return; + } + + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + updateSelectionCenter(); + dialog_refresh_all(); +} + + +void LLSelectMgr::setHoverObject(LLViewerObject *objectp) +{ + // Always blitz hover list when setting + mHoverObjects.deleteAllNodes(); + + if (!objectp) + { + return; + } + + // Can't select yourself + if (objectp->mID == gAgentID) + { + return; + } + + // Can't select land + if (objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) + { + return; + } + + // Collect all of the objects + LLDynamicArray objects; + objectp = objectp->getRootEdit(); + objectp->addThisAndNonJointChildren(objects); + + + S32 count = objects.count(); + LLViewerObject* cur_objectp = NULL; + LLSelectNode* nodep = NULL; + for(S32 i = 0; i < count; i++) + { + cur_objectp = objects[i]; + nodep = new LLSelectNode(cur_objectp, FALSE); + mHoverObjects.addNodeAtEnd(nodep); + } + + requestObjectPropertiesFamily(objectp); +} + +LLSelectNode *LLSelectMgr::getHoverNode() +{ + return getHoverObjects().getFirstRootNode(); +} + +void LLSelectMgr::highlightObjectOnly(LLViewerObject* objectp) +{ + if (!objectp) + { + return; + } + + if (objectp->getPCode() != LL_PCODE_VOLUME) + { + return; + } + + if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !objectp->permYouOwner()) || + (gSavedSettings.getBOOL("SelectMovableOnly") && !objectp->permMove())) + { + // only select my own objects + return; + } + + mRectSelectedObjects.insert(objectp); +} + +void LLSelectMgr::highlightObjectAndFamily(LLViewerObject* objectp) +{ + if (!objectp) + { + return; + } + + LLViewerObject* root_obj = (LLViewerObject*)objectp->getRoot(); + + highlightObjectOnly(root_obj); + + for(U32 i = 0; i < root_obj->mChildList.size(); i++) + { + highlightObjectOnly(root_obj->mChildList[i]); + } +} + +// Note that this ignores the "select owned only" flag +// It's also more efficient than calling the single-object version over and +// over. +void LLSelectMgr::highlightObjectAndFamily(const LLDynamicArray& list) +{ + S32 i; + S32 count = list.count(); + + for (i = 0; i < count; i++) + { + LLViewerObject* object = list.get(i); + + if (!object) continue; + if (object->getPCode() != LL_PCODE_VOLUME) continue; + + LLViewerObject* root = (LLViewerObject*)object->getRoot(); + mRectSelectedObjects.insert(root); + + S32 j; + S32 child_count = root->mChildList.size(); + for (j = 0; j < child_count; j++) + { + LLViewerObject* child = root->mChildList[j]; + mRectSelectedObjects.insert(child); + } + } +} + +void LLSelectMgr::unhighlightObjectOnly(LLViewerObject* objectp) +{ + if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME)) + { + return; + } + + mRectSelectedObjects.erase(objectp); +} + +void LLSelectMgr::unhighlightObjectAndFamily(LLViewerObject* objectp) +{ + if (!objectp) + { + return; + } + + LLViewerObject* root_obj = (LLViewerObject*)objectp->getRoot(); + + unhighlightObjectOnly(root_obj); + + for(U32 i = 0; i < root_obj->mChildList.size(); i++) + { + unhighlightObjectOnly(root_obj->mChildList[i]); + } +} + + +void LLSelectMgr::unhighlightAll() +{ + mRectSelectedObjects.clear(); + mHighlightedObjects.deleteAllNodes(); +} + +void LLSelectMgr::selectHighlightedObjects() +{ + if (!mHighlightedObjects.getNumNodes()) + { + return; + } + + LLSelectNode *nodep; + for (nodep = mHighlightedObjects.getFirstNode(); + nodep; + nodep = mHighlightedObjects.getNextNode()) + { + LLViewerObject* objectp = nodep->getObject(); + + if (!canSelectObject(objectp)) + { + continue; + } + + // already selected + if (objectp->isSelected()) + { + continue; + } + + LLSelectNode* new_nodep = new LLSelectNode(*nodep); + mSelectedObjects.addNode(new_nodep); + + // flag this object as selected + objectp->setSelected(TRUE); + + mSelectType = getSelectTypeForObject(objectp); + + // request properties on root objects + if (objectp->isRootEdit()) + { + requestObjectPropertiesFamily(objectp); + } + } + + // pack up messages to let sim know these objects are selected + sendSelect(); + unhighlightAll(); + updateSelectionCenter(); + saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); + updatePointAt(); + + if (getObjectCount()) + { + gEditMenuHandler = this; + } +} + +void LLSelectMgr::deselectHighlightedObjects() +{ + BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet"); + for (std::set >::iterator iter = mRectSelectedObjects.begin(); + iter != mRectSelectedObjects.end(); iter++) + { + LLViewerObject *objectp = *iter; + if (!select_linked_set) + { + deselectObjectOnly(objectp); + } + else + { + LLViewerObject* root_object = (LLViewerObject*)objectp->getRoot(); + if (root_object->isSelected()) + { + deselectObjectAndFamily(root_object); + } + } + } + + unhighlightAll(); +} + +void LLSelectMgr::addGridObject(LLViewerObject* objectp) +{ + LLSelectNode* nodep = new LLSelectNode(objectp, FALSE); + mGridObjects.addNodeAtEnd(nodep); + for (U32 i = 0; i < objectp->mChildList.size(); i++) + { + nodep = new LLSelectNode(objectp->mChildList[i], FALSE); + mGridObjects.addNodeAtEnd(nodep); + } +} + +void LLSelectMgr::clearGridObjects() +{ + mGridObjects.deleteAllNodes(); +} + +void LLSelectMgr::setGridMode(EGridMode mode) +{ + mGridMode = mode; + gSavedSettings.setS32("GridMode", mode); + updateSelectionCenter(); + mGridValid = FALSE; +} + +void LLSelectMgr::getGrid(LLVector3& origin, LLQuaternion &rotation, LLVector3 &scale) +{ + LLSelectNode* grid_node = mGridObjects.getFirstNode(); + LLViewerObject* grid_object = mGridObjects.getFirstObject(); + // *TODO: get to work with multiple grid objects + if (grid_node && grid_node->getObject()->isDead()) + { + mGridObjects.removeNode(grid_node); + grid_object = NULL; + } + + if (mGridMode == GRID_MODE_LOCAL && gSelectMgr->getObjectCount()) + { + LLBBox bbox = mSavedSelectionBBox; + mGridOrigin = mSavedSelectionBBox.getCenterAgent(); + mGridRotation = mSavedSelectionBBox.getRotation(); + mGridScale = mSavedSelectionBBox.getExtentLocal() * 0.5f; + } + else if (mGridMode == GRID_MODE_REF_OBJECT && grid_object && grid_object->mDrawable.notNull()) + { + mGridRotation = grid_object->getRenderRotation(); + LLVector3 first_grid_obj_pos = grid_object->getRenderPosition(); + + LLVector3 min_extents(F32_MAX, F32_MAX, F32_MAX); + LLVector3 max_extents(F32_MIN, F32_MIN, F32_MIN); + BOOL grid_changed = FALSE; + LLSelectNode* grid_nodep; + for (grid_nodep = mGridObjects.getFirstNode(); + grid_nodep; + grid_nodep = mGridObjects.getNextNode()) + { + grid_object = grid_nodep->getObject(); + + LLVector3 local_min_extents(F32_MAX, F32_MAX, F32_MAX); + LLVector3 local_max_extents(F32_MIN, F32_MIN, F32_MIN); + + // *FIX: silhouette flag is insufficient as it gets + // cleared by view update. + if (!mGridValid || + grid_object->isChanged(LLXform::SILHOUETTE) + || (grid_object->getParent() && grid_object->getParent()->isChanged(LLXform::SILHOUETTE))) + { + getSilhouetteExtents(grid_nodep, mGridRotation, local_min_extents, local_max_extents); + grid_changed = TRUE; + LLVector3 object_offset = (grid_object->getRenderPosition() - first_grid_obj_pos) * ~mGridRotation; + local_min_extents += object_offset; + local_max_extents += object_offset; + } + min_extents.mV[VX] = llmin(min_extents.mV[VX], local_min_extents.mV[VX]); + min_extents.mV[VY] = llmin(min_extents.mV[VY], local_min_extents.mV[VY]); + min_extents.mV[VZ] = llmin(min_extents.mV[VZ], local_min_extents.mV[VZ]); + max_extents.mV[VX] = llmax(max_extents.mV[VX], local_max_extents.mV[VX]); + max_extents.mV[VY] = llmax(max_extents.mV[VY], local_max_extents.mV[VY]); + max_extents.mV[VZ] = llmax(max_extents.mV[VZ], local_max_extents.mV[VZ]); + } + if (grid_changed) + { + mGridOrigin = lerp(min_extents, max_extents, 0.5f); + mGridOrigin = mGridOrigin * ~mGridRotation; + mGridOrigin += first_grid_obj_pos; + mGridScale = (max_extents - min_extents) * 0.5f; + } + } + else // GRID_MODE_WORLD or just plain default + { + LLViewerObject* first_object = gSelectMgr->getFirstRootObject(); + if (!first_object) + { + first_object = gSelectMgr->getFirstObject(); + } + + mGridOrigin.clearVec(); + mGridRotation.loadIdentity(); + + mSelectType = getSelectTypeForObject( first_object ); + + switch (mSelectType) + { + case SELECT_TYPE_ATTACHMENT: + if (first_object) + { + // this means this object *has* to be an attachment + LLXform* attachment_point_xform = first_object->getRootEdit()->mDrawable->mXform.getParent(); + mGridOrigin = attachment_point_xform->getWorldPosition(); + mGridRotation = attachment_point_xform->getWorldRotation(); + mGridScale = LLVector3(1.f, 1.f, 1.f) * gSavedSettings.getF32("GridResolution"); + } + break; + case SELECT_TYPE_HUD: + // use HUD-scaled grid + mGridScale = LLVector3(0.25f, 0.25f, 0.25f); + break; + case SELECT_TYPE_WORLD: + mGridScale = LLVector3(1.f, 1.f, 1.f) * gSavedSettings.getF32("GridResolution"); + break; + } + } + llassert(mGridOrigin.isFinite()); + + origin = mGridOrigin; + rotation = mGridRotation; + scale = mGridScale; + mGridValid = TRUE; +} + + + +LLSelectNode* LLSelectMgr::findSelectNode(LLViewerObject *object) +{ + return mSelectedObjects.findNode(object); +} + +//----------------------------------------------------------------------------- +// contains() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::contains(LLViewerObject* object) +{ + return mSelectedObjects.findNode(object) != NULL; +} + + +//----------------------------------------------------------------------------- +// contains() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::contains(LLViewerObject* object, S32 te) +{ + LLSelectNode *nodep; + if (te == SELECT_ALL_TES) + { + // ...all faces + for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() ) + { + if (nodep->getObject() == object) + { + BOOL all_selected = TRUE; + for (S32 i = 0; i < SELECT_MAX_TES; i++) + { + all_selected = all_selected && nodep->isTESelected(i); + } + return all_selected; + } + } + return FALSE; + } + else + { + // ...one face + for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() ) + { + if (nodep->getObject() == object && nodep->isTESelected(te)) + { + return TRUE; + } + } + return FALSE; + } +} + +//----------------------------------------------------------------------------- +// remove() - an array of objects +//----------------------------------------------------------------------------- + +void LLSelectMgr::remove(LLDynamicArray& objects) +{ + S32 count = objects.count(); + LLViewerObject *objectp = NULL; + LLSelectNode *nodep = NULL; + for(S32 i = 0; i < count; i++) + { + objectp = objects.get(i); + for(nodep = mSelectedObjects.getFirstNode(); + nodep != NULL; + nodep = mSelectedObjects.getNextNode()) + { + if(nodep->getObject() == objectp) + { + objectp->setSelected(FALSE); + mSelectedObjects.removeNode(nodep); + break; + } + } + } + updateSelectionCenter(); + dialog_refresh_all(); +} + + +//----------------------------------------------------------------------------- +// remove() - a single object +//----------------------------------------------------------------------------- +void LLSelectMgr::remove(LLViewerObject *objectp, S32 te, BOOL undoable) +{ + // check if object already in list + // *FIX: can we just check isSelected()? + LLSelectNode *nodep = findSelectNode(objectp); + + if (!nodep) + { + return; + } + + + // if face = all, remove object from list + if (objectp->getNumTEs() <= 0) + { + // object doesn't have faces, so blow it away + mSelectedObjects.removeNode(nodep); + objectp->setSelected( FALSE ); + } + else if (te == SELECT_ALL_TES) + { + mSelectedObjects.removeNode(nodep); + objectp->setSelected( FALSE ); + } + else if (0 <= te && te < SELECT_MAX_TES) + { + // ...valid face, check to see if it was on + if (nodep->isTESelected(te)) + { + nodep->selectTE(te, FALSE); + } + else + { + llerrs << "LLSelectMgr::remove - tried to remove TE " << te << " that wasn't selected" << llendl; + return; + } + + // ...check to see if this operation turned off all faces + BOOL found = FALSE; + for (S32 i = 0; i < nodep->getObject()->getNumTEs(); i++) + { + found = found || nodep->isTESelected(i); + } + + // ...all faces now turned off, so remove + if (!found) + { + mSelectedObjects.removeNode(nodep); + objectp->setSelected( FALSE ); + + // BUG: Doesn't update simulator that object is gone. + } + } + else + { + // ...out of range face + llerrs << "LLSelectMgr::remove - TE " << te << " out of range" << llendl; + } + + updateSelectionCenter(); + dialog_refresh_all(); +} + + +//----------------------------------------------------------------------------- +// removeAll() +//----------------------------------------------------------------------------- +void LLSelectMgr::removeAll() +{ + LLViewerObject *objectp; + for (objectp = mSelectedObjects.getFirstObject(); objectp; objectp = mSelectedObjects.getNextObject() ) + { + objectp->setSelected( FALSE ); + } + + mSelectedObjects.deleteAllNodes(); + + updateSelectionCenter(); + dialog_refresh_all(); +} + +//----------------------------------------------------------------------------- +// promoteSelectionToRoot() +//----------------------------------------------------------------------------- +void LLSelectMgr::promoteSelectionToRoot() +{ + std::set selection_set; + + BOOL selection_changed = FALSE; + + LLSelectNode* nodep; + LLViewerObject *objectp; + for (nodep = mSelectedObjects.getFirstNode(); + nodep; + nodep = mSelectedObjects.getNextNode() ) + { + if (nodep->mIndividualSelection) + { + selection_changed = TRUE; + } + + objectp = nodep->getObject(); + LLViewerObject* parentp = objectp; + while(parentp->getParent() && !(parentp->isRootEdit() || parentp->isJointChild())) + { + parentp = (LLViewerObject*)parentp->getParent(); + } + + selection_set.insert(parentp); + } + + if (selection_changed) + { + deselectAll(); + + std::set::iterator set_iter; + for (set_iter = selection_set.begin(); set_iter != selection_set.end(); ++set_iter) + { + selectObjectAndFamily(*set_iter); + } + } +} + +//----------------------------------------------------------------------------- +// demoteSelectionToIndividuals() +//----------------------------------------------------------------------------- +void LLSelectMgr::demoteSelectionToIndividuals() +{ + LLDynamicArray objects; + + for (LLViewerObject* root_objectp = mSelectedObjects.getFirstRootObject(); + root_objectp; + root_objectp = mSelectedObjects.getNextRootObject()) + { + root_objectp->addThisAndNonJointChildren(objects); + } + + if (objects.getLength()) + { + deselectAll(); + for(S32 i = 0; i < objects.count(); i++) + { + selectObjectOnly(objects[i]); + } + } +} + +//----------------------------------------------------------------------------- +// getObjectCount() +//----------------------------------------------------------------------------- +S32 LLSelectMgr::getObjectCount() +{ + return mSelectedObjects.getNumNodes(); +} + + +//----------------------------------------------------------------------------- +// getTECount() +//----------------------------------------------------------------------------- +S32 LLSelectMgr::getTECount() +{ + S32 count = 0; + + LLSelectNode* nodep; + for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() ) + { + if (nodep->getObject()) + { + S32 num_tes = nodep->getObject()->getNumTEs(); + for (S32 te = 0; te < num_tes; te++) + { + if (nodep->isTESelected(te)) + { + count++; + } + } + } + } + + return count; +} + +//----------------------------------------------------------------------------- +// getRootObjectCount() +//----------------------------------------------------------------------------- +S32 LLSelectMgr::getRootObjectCount() +{ + LLSelectNode *nodep; + + S32 count = 0; + for(nodep = mSelectedObjects.getFirstRootNode(); nodep; nodep = mSelectedObjects.getNextRootNode()) + { + ++count; + } + return count; +} + + +//----------------------------------------------------------------------------- +// dump() +//----------------------------------------------------------------------------- +void LLSelectMgr::dump() +{ + llinfos << "Selection Manager: " << mSelectedObjects.getNumNodes() << " items" << llendl; + + llinfos << "TE mode " << mTEMode << llendl; + + S32 i = 0; + + LLViewerObject *objectp; + for (objectp = mSelectedObjects.getFirstObject(); + objectp; + objectp = mSelectedObjects.getNextObject()) + { + llinfos << "Object " << i << " type " << LLPrimitive::pCodeToString(objectp->getPCode()) << llendl; + llinfos << " hasLSL " << objectp->flagScripted() << llendl; + llinfos << " hasTouch " << objectp->flagHandleTouch() << llendl; + llinfos << " hasMoney " << objectp->flagTakesMoney() << llendl; + llinfos << " getposition " << objectp->getPosition() << llendl; + llinfos << " getpositionAgent " << objectp->getPositionAgent() << llendl; + llinfos << " getpositionRegion " << objectp->getPositionRegion() << llendl; + llinfos << " getpositionGlobal " << objectp->getPositionGlobal() << llendl; + LLDrawable* drawablep = objectp->mDrawable; + llinfos << " " << (drawablep&& drawablep->isVisible() ? "visible" : "invisible") << llendl; + llinfos << " " << (drawablep&& drawablep->isState(LLDrawable::FORCE_INVISIBLE) ? "force_invisible" : "") << llendl; + i++; + } + + // Face iterator + S32 te; + for (mSelectedObjects.getFirstTE(&objectp, &te); + objectp; + mSelectedObjects.getNextTE(&objectp, &te)) + { + llinfos << "Object " << objectp << " te " << te << llendl; + } + + llinfos << mHighlightedObjects.getNumNodes() << " objects currently highlighted." << llendl; + + llinfos << "Center global " << mSelectionCenterGlobal << llendl; +} + +//----------------------------------------------------------------------------- +// cleanup() +//----------------------------------------------------------------------------- +void LLSelectMgr::cleanup() +{ + mSilhouetteImagep = NULL; +} + + +//--------------------------------------------------------------------------- +// Manipulate properties of selected objects +//--------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// selectionSetImage() +//----------------------------------------------------------------------------- +// *TODO: re-arch texture applying out of lltooldraganddrop +void LLSelectMgr::selectionSetImage(const LLUUID& imageid) +{ + // First for (no copy) textures and multiple object selection + LLViewerInventoryItem* item = gInventory.getItem(imageid); + + if(item + && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()) + && (mSelectedObjects.getNumNodes() > 1) ) + { + llwarns << "Attempted to apply no-copy texture to multiple objects" + << llendl; + return; + } + + LLViewerObject* objectp; + S32 te; + + // Apply the texture to each side + for (mSelectedObjects.getFirstTE(&objectp, &te); objectp; mSelectedObjects.getNextTE(&objectp, &te)) + { + + if (item) + { + LLToolDragAndDrop::dropTextureOneFace(objectp,te,item,LLToolDragAndDrop::SOURCE_AGENT,LLUUID::null); + + // HACK! HACK! ARG! + // *TODO: Replace mSelectedObjects with a REAL container class! + LLViewerObject* tmp_object; + S32 tmp_te; + mSelectedObjects.getCurrentTE(&tmp_object,&tmp_te); + if ((tmp_object != objectp) || (tmp_te != te) ) + { + //AAARG someone has moved our list around! + mSelectedObjects.getFirstTE(&tmp_object, &tmp_te); + while ((tmp_object != objectp) || (tmp_te != te)) + { + mSelectedObjects.getNextTE(&tmp_object, &tmp_te); + } + } + } + else + { + // Texture picker defaults aren't inventory items + // * Don't need to worry about permissions for them + // * Can just apply the texture and be done with it. + objectp->setTEImage(te, gImageList.getImage(imageid)); + objectp->sendTEUpdate(); + } + } + + // 1 particle effect per object + if (mSelectType != SELECT_TYPE_HUD) + { + for (objectp = mSelectedObjects.getFirstObject(); objectp; objectp = mSelectedObjects.getNextObject()) + { + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE); + effectp->setSourceObject(gAgent.getAvatarObject()); + effectp->setTargetObject(objectp); + effectp->setDuration(LL_HUD_DUR_SHORT); + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + } + } +} + +//----------------------------------------------------------------------------- +// selectionSetColor() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionSetColor(const LLColor4 &color) +{ + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) ) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTEColor(te, color); + } + } + + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + } +} + +//----------------------------------------------------------------------------- +// selectionSetColorOnly() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionSetColorOnly(const LLColor4 &color) +{ + LLViewerObject* object; + LLColor4 new_color = color; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) ) + { + if (object->permModify()) + { + LLColor4 prev_color = object->getTE(te)->getColor(); + new_color.mV[VALPHA] = prev_color.mV[VALPHA]; + // update viewer side color in anticipation of update from simulator + object->setTEColor(te, new_color); + } + } + + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + } +} + +//----------------------------------------------------------------------------- +// selectionSetAlphaOnly() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionSetAlphaOnly(const F32 alpha) +{ + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) ) + { + if (object->permModify()) + { + LLColor4 prev_color = object->getTE(te)->getColor(); + prev_color.mV[VALPHA] = alpha; + // update viewer side color in anticipation of update from simulator + object->setTEColor(te, prev_color); + } + } + + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + } +} + +void LLSelectMgr::selectionRevertColors() +{ + LLViewerObject* object; + S32 te; + + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) ) + { + if (object->permModify()) + { + LLSelectNode* nodep = mSelectedObjects.findNode(object); + if (nodep && te < (S32)nodep->mSavedColors.size()) + { + LLColor4 color = nodep->mSavedColors[te]; + // update viewer side color in anticipation of update from simulator + object->setTEColor(te, color); + } + } + } + + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + } +} + +BOOL LLSelectMgr::selectionRevertTextures() +{ + LLViewerObject* object; + S32 te; + + BOOL revert_successful = TRUE; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) ) + { + if (object->permModify()) + { + LLSelectNode* nodep = mSelectedObjects.findNode(object); + if (nodep && te < (S32)nodep->mSavedTextures.size()) + { + LLUUID id = nodep->mSavedTextures[te]; + // update textures on viewer side + if (id.isNull()) + { + // this was probably a no-copy texture, leave image as-is + revert_successful = FALSE; + } + else + { + object->setTEImage(te, gImageList.getImage(id)); + } + } + } + } + + // propagate texture changes to server + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + } + + return revert_successful; +} + +void LLSelectMgr::selectionSetBumpmap(U8 bumpmap) +{ + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) ) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTEBumpmap(te, bumpmap); + } + } + + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + } +} + +void LLSelectMgr::selectionSetTexGen(U8 texgen) +{ + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) ) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTETexGen(te, texgen); + } + } + + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + } +} + + +void LLSelectMgr::selectionSetShiny(U8 shiny) +{ + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) ) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTEShiny(te, shiny); + } + } + + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + } +} + +void LLSelectMgr::selectionSetFullbright(U8 fullbright) +{ + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) ) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTEFullbright(te, fullbright); + } + } + + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + object->sendTEUpdate(); + if (fullbright) + { + U8 material = object->getMaterial(); + U8 mcode = material & LL_MCODE_MASK; + if (mcode == LL_MCODE_LIGHT) + { + mcode = LL_MCODE_GLASS; + material = (material & ~LL_MCODE_MASK) | mcode; + object->setMaterial(material); + object->sendMaterialUpdate(); + } + } + } + } +} + +void LLSelectMgr::selectionSetMediaTypeAndURL(U8 media_type, const std::string& media_url) +{ + LLViewerObject* object; + S32 te; + U8 media_flags = LLTextureEntry::MF_NONE; + if (media_type == LLViewerObject::MEDIA_TYPE_WEB_PAGE) + { + media_flags = LLTextureEntry::MF_WEB_PAGE; + } + + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) ) + { + if (object->permModify()) + { + // update viewer side color in anticipation of update from simulator + object->setTEMediaFlags(te, media_flags); + } + } + + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + // JAMESDEBUG TODO set object media type + object->setMediaType(media_type); + object->setMediaURL(media_url); + + object->sendTEUpdate(); + } + } +} + + + +//----------------------------------------------------------------------------- +// findObjectPermissions() +//----------------------------------------------------------------------------- +LLPermissions* LLSelectMgr::findObjectPermissions(const LLViewerObject* object) +{ + LLSelectNode* nodep; + + for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() ) + { + if((nodep->getObject() == object) && nodep->mValid) + { + return nodep->mPermissions; + } + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// selectionGetTexUUID() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectionGetTexUUID(LLUUID& id) +{ + LLViewerObject* first_objectp; + S32 first_te; + mSelectedObjects.getPrimaryTE(&first_objectp, &first_te); + + // nothing selected + if (!first_objectp) + { + return FALSE; + } + + LLViewerImage* first_imagep = first_objectp->getTEImage(first_te); + + if (!first_imagep) + { + return FALSE; + } + + BOOL identical = TRUE; + LLViewerObject *objectp; + S32 te; + for (mSelectedObjects.getFirstTE(&objectp, &te); objectp; mSelectedObjects.getNextTE(&objectp, &te) ) + { + if (objectp->getTEImage(te) != first_imagep) + { + identical = FALSE; + break; + } + } + + id = first_imagep->getID(); + return identical; +} + +//----------------------------------------------------------------------------- +// selectionGetColor() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectionGetColor(LLColor4 &color) +{ + LLViewerObject* first_object; + S32 first_te; + mSelectedObjects.getPrimaryTE(&first_object, &first_te); + + // nothing selected + if (!first_object) + { + return FALSE; + } + + LLColor4 first_color; + if (!first_object->getTE(first_te)) + { + return FALSE; + } + else + { + first_color = first_object->getTE(first_te)->getColor(); + } + + BOOL identical = TRUE; + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te)) + { + if (!object->getTE(te) || (object->getTE(te)->getColor() != first_color)) + { + identical = FALSE; + break; + } + } + + color = first_color; + return identical; +} + + +//----------------------------------------------------------------------------- +// selectionGetBumpmap() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectionGetBumpmap(U8 *bumpmap) +{ + LLViewerObject* first_object; + S32 first_te; + mSelectedObjects.getPrimaryTE(&first_object, &first_te); + + // nothing selected + if (!first_object) + { + return FALSE; + } + + U8 first_value; + if (!first_object->getTE(first_te)) + { + return FALSE; + } + else + { + first_value = first_object->getTE(first_te)->getBumpmap(); + } + + BOOL identical = TRUE; + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te)) + { + if (!object->getTE(te) || (object->getTE(te)->getBumpmap() != first_value)) + { + identical = FALSE; + break; + } + } + + *bumpmap = first_value; + return identical; +} + +//----------------------------------------------------------------------------- +// selectionGetShiny() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectionGetShiny(U8 *shiny) +{ + LLViewerObject* first_object; + S32 first_te; + mSelectedObjects.getPrimaryTE(&first_object, &first_te); + + // nothing selected + if (!first_object) + { + return FALSE; + } + + U8 first_value; + if (!first_object->getTE(first_te)) + { + return FALSE; + } + else + { + first_value = first_object->getTE(first_te)->getShiny(); + } + + BOOL identical = TRUE; + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te)) + { + if (!object->getTE(te) || (object->getTE(te)->getShiny() != first_value)) + { + identical = FALSE; + break; + } + } + + *shiny = first_value; + return identical; +} + +//----------------------------------------------------------------------------- +// selectionGetFullbright() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectionGetFullbright(U8 *fullbright) +{ + LLViewerObject* first_object; + S32 first_te; + mSelectedObjects.getPrimaryTE(&first_object, &first_te); + + // nothing selected + if (!first_object) + { + return FALSE; + } + + U8 first_value; + if (!first_object->getTE(first_te)) + { + return FALSE; + } + else + { + first_value = first_object->getTE(first_te)->getFullbright(); + } + + BOOL identical = TRUE; + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te)) + { + if (!object->getTE(te) || (object->getTE(te)->getFullbright() != first_value)) + { + identical = FALSE; + break; + } + } + + *fullbright = first_value; + return identical; +} + +// JAMESDEBUG TODO make this return mediatype off viewer object +bool LLSelectMgr::selectionGetMediaType(U8 *media_type) +{ + LLViewerObject* first_object; + S32 first_te; + mSelectedObjects.getPrimaryTE(&first_object, &first_te); + + // nothing selected + if (!first_object) + { + return false; + } + + U8 first_value; + if (!first_object->getTE(first_te)) + { + return false; + } + else + { + first_value = first_object->getTE(first_te)->getMediaFlags(); + } + + bool identical = true; + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te)) + { + if (!object->getTE(te) || (object->getTE(te)->getMediaFlags() != first_value)) + { + identical = false; + break; + } + } + + *media_type = first_value; + return identical; +} + + + +//----------------------------------------------------------------------------- +// selectionSetMaterial() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionSetMaterial(U8 material) +{ + LLViewerObject* object; + for (object = mSelectedObjects.getFirstObject(); object != NULL; object = mSelectedObjects.getNextObject() ) + { + if (object->permModify()) + { + U8 cur_material = object->getMaterial(); + material |= (cur_material & ~LL_MCODE_MASK); + object->setMaterial(material); + object->sendMaterialUpdate(); + } + } +} + +// True if all selected objects have this PCode +BOOL LLSelectMgr::selectionAllPCode(LLPCode code) +{ + LLViewerObject *object; + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if (object->getPCode() != code) + { + return FALSE; + } + } + return TRUE; +} + +//----------------------------------------------------------------------------- +// selectionGetMaterial() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectionGetMaterial(U8 *out_material) +{ + LLViewerObject *object = mSelectedObjects.getFirstObject(); + if (!object) return FALSE; + + U8 material = object->getMaterial(); + + BOOL identical = TRUE; + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if ( material != object->getMaterial()) + { + identical = FALSE; + break; + } + } + + *out_material = material; + return identical; +} + +BOOL LLSelectMgr::selectionGetClickAction(U8 *out_action) +{ + LLViewerObject *object = mSelectedObjects.getFirstObject(); + if (!object) return FALSE; + + U8 action = object->getClickAction(); + + BOOL identical = TRUE; + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + if ( action != object->getClickAction()) + { + identical = FALSE; + break; + } + } + + *out_action = action; + return identical; +} + +void LLSelectMgr::selectionSetClickAction(U8 action) +{ + LLViewerObject* object = NULL; + for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + object->setClickAction(action); + } + sendListToRegions( + "ObjectClickAction", + packAgentAndSessionID, + packObjectClickAction, + &action, + SEND_INDIVIDUALS); +} + + +//----------------------------------------------------------------------------- +// godlike requests +//----------------------------------------------------------------------------- + +typedef std::pair godlike_request_t; +void LLSelectMgr::sendGodlikeRequest(const LLString& request, const LLString& param) +{ + // If the agent is neither godlike nor an estate owner, the server + // will reject the request. + LLString message_type; + if (gAgent.isGodlike()) + { + message_type = "GodlikeMessage"; + } + else + { + message_type = "EstateOwnerMessage"; + } + + godlike_request_t data(request, param); + if(!getRootObjectCount()) + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessage(message_type.c_str()); + LLSelectMgr::packGodlikeHead(&data); + gAgent.sendReliableMessage(); + } + else + { + sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, &data, SEND_ONLY_ROOTS); + } +} + +void LLSelectMgr::packGodlikeHead(void* user_data) +{ + LLMessageSystem* msg = gMessageSystem; + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addUUID("TransactionID", LLUUID::null); + godlike_request_t* data = (godlike_request_t*)user_data; + msg->nextBlock("MethodData"); + msg->addString("Method", data->first.c_str()); + msg->addUUID("Invoice", LLUUID::null); + + // The parameters used to be restricted to either string or + // integer. This mimics that behavior under the new 'string-only' + // parameter list by not packing a string if there wasn't one + // specified. The object ids will be packed in the + // packObjectIDAsParam() method. + if(data->second.size() > 0) + { + msg->nextBlock("ParamList"); + msg->addString("Parameter", data->second); + } +} + +// static +void LLSelectMgr::packObjectIDAsParam(LLSelectNode* node, void *) +{ + char buf [MAX_STRING]; + sprintf(buf, "%u", node->getObject()->getLocalID()); + gMessageSystem->nextBlock("ParamList"); + gMessageSystem->addString("Parameter", buf); +} + +//----------------------------------------------------------------------------- +// Rotation options +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionResetRotation() +{ + LLQuaternion identity(0.f, 0.f, 0.f, 1.f); + + LLViewerObject* object; + for (object = mSelectedObjects.getFirstRootObject(); object; object = mSelectedObjects.getNextRootObject() ) + { + object->setRotation(identity); + if (object->mDrawable.notNull()) + { + gPipeline.markMoved(object->mDrawable, TRUE); + } + object->sendRotationUpdate(); + } +} + +void LLSelectMgr::selectionRotateAroundZ(F32 degrees) +{ + LLQuaternion rot( degrees * DEG_TO_RAD, LLVector3(0,0,1) ); + + LLViewerObject* object; + for (object = mSelectedObjects.getFirstRootObject(); object; object = mSelectedObjects.getNextRootObject() ) + { + object->setRotation( object->getRotationEdit() * rot ); + if (object->mDrawable.notNull()) + { + gPipeline.markMoved(object->mDrawable, TRUE); + } + object->sendRotationUpdate(); + } +} + + +//----------------------------------------------------------------------------- +// selectionTexScaleAutofit() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionTexScaleAutofit(F32 repeats_per_meter) +{ + LLViewerObject* object; + S32 te; + for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te)) + { + if (!object->permModify()) + { + continue; + } + + if (object->getNumTEs() == 0) + { + continue; + } + + // Compute S,T to axis mapping + U32 s_axis, t_axis; + if (!getTESTAxes(object, te, &s_axis, &t_axis)) + { + continue; + } + + F32 new_s = object->getScale().mV[s_axis] * repeats_per_meter; + F32 new_t = object->getScale().mV[t_axis] * repeats_per_meter; + + object->setTEScale(te, new_s, new_t); + } + + for (object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject()) + { + if (object->permModify()) + { + object->sendTEUpdate(); + } + } +} + + +// BUG: Only works for boxes. +// Face numbering for flex boxes as of 1.14.2002 +//----------------------------------------------------------------------------- +// getFaceSTAxes() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::getTESTAxes(const LLViewerObject* object, const U8 face, U32* s_axis, U32* t_axis) +{ + if (face == 0) + { + *s_axis = VX; *t_axis = VY; + return TRUE; + } + else if (face == 1) + { + *s_axis = VX; *t_axis = VZ; + return TRUE; + } + else if (face == 2) + { + *s_axis = VY; *t_axis = VZ; + return TRUE; + } + else if (face == 3) + { + *s_axis = VX; *t_axis = VZ; + return TRUE; + } + else if (face == 4) + { + *s_axis = VY; *t_axis = VZ; + return TRUE; + } + else if (face == 5) + { + *s_axis = VX; *t_axis = VY; + return TRUE; + } + else + { + // unknown face + return FALSE; + } +} + +// Called at the end of a scale operation, this adjusts the textures to attempt to +// maintain a constant repeats per meter. +// BUG: Only works for flex boxes. +//----------------------------------------------------------------------------- +// adjustTexturesByScale() +//----------------------------------------------------------------------------- +void LLSelectMgr::adjustTexturesByScale(BOOL send_to_sim, BOOL stretch) +{ + LLViewerObject* object; + LLSelectNode* selectNode; + + BOOL send = FALSE; + + for (selectNode = mSelectedObjects.getFirstNode(); selectNode; selectNode = mSelectedObjects.getNextNode()) + { + object = selectNode->getObject(); + if (!object->permModify()) + { + continue; + } + + if (object->getNumTEs() == 0) + { + continue; + } + + for (U8 te_num = 0; te_num < object->getNumTEs(); te_num++) + { + const LLTextureEntry* tep = object->getTE(te_num); + + BOOL planar = tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR; + if (planar == stretch) + { + // Figure out how S,T changed with scale operation + U32 s_axis, t_axis; + if (!getTESTAxes(object, te_num, &s_axis, &t_axis)) continue; + + LLVector3 scale_ratio = selectNode->mTextureScaleRatios[te_num]; + LLVector3 object_scale = object->getScale(); + + // Apply new scale to face + if (planar) + { + object->setTEScale(te_num, 1.f/object_scale.mV[s_axis]*scale_ratio.mV[s_axis], + 1.f/object_scale.mV[t_axis]*scale_ratio.mV[t_axis]); + } + else + { + object->setTEScale(te_num, scale_ratio.mV[s_axis]*object_scale.mV[s_axis], + scale_ratio.mV[t_axis]*object_scale.mV[t_axis]); + } + send = send_to_sim; + } + } + + if (send) + { + object->sendTEUpdate(); + } + } +} + + +//----------------------------------------------------------------------------- +// selectionResetTexInfo() +//----------------------------------------------------------------------------- +void LLSelectMgr::selectionResetTexInfo(S32 selected_face) +{ + S32 start_face, end_face; + + LLViewerObject* object; + for (object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject()) + { + if (!object->permModify()) + { + continue; + } + if (object->getNumTEs() == 0) + { + continue; + } + + if (selected_face == -1) + { + start_face = 0; + end_face = object->getNumTEs() - 1; + } + else + { + start_face = selected_face; + end_face = selected_face; + } + + for (S32 face = start_face; face <= end_face; face++) + { + // Actually, each object should reset to its appropriate value. + object->setTEScale(face, 1.f, 1.f); + object->setTEOffset(face, 0.f, 0.f); + object->setTERotation(face, 0.f); + } + + object->sendTEUpdate(); + } +} + +//----------------------------------------------------------------------------- +// getFirstEditableObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLSelectMgr::getFirstEditableObject(BOOL get_root) +{ + LLViewerObject* object = NULL; + for(LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject()) + { + if( cur->permModify() ) + { + object = cur; + break; + } + } + + if (get_root && object) + { + LLViewerObject *parent; + while ((parent = (LLViewerObject*)object->getParent())) + { + if (parent->isSelected()) + { + object = parent; + } + else + { + break; + } + } + } + + return object; +} + +//----------------------------------------------------------------------------- +// getFirstMoveableObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLSelectMgr::getFirstMoveableObject(BOOL get_root) +{ + LLViewerObject* object = NULL; + for(LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject()) + { + if( cur->permMove() ) + { + object = cur; + break; + } + } + + if (get_root && object && !object->isJointChild()) + { + LLViewerObject *parent; + while ((parent = (LLViewerObject*)object->getParent())) + { + if (parent->isSelected()) + { + object = parent; + } + else + { + break; + } + } + } + + return object; +} + +//----------------------------------------------------------------------------- +// getFirstEditableNode() +//----------------------------------------------------------------------------- +LLSelectNode* LLSelectMgr::getFirstEditableNode(BOOL get_root) +{ + LLSelectNode* selectNode = NULL; + + if (get_root) + { + for(selectNode = mSelectedObjects.getFirstRootNode(); selectNode; selectNode = mSelectedObjects.getNextRootNode()) + { + if( selectNode->getObject()->permModify() ) + { + return selectNode; + break; + } + } + } + for(selectNode = mSelectedObjects.getFirstNode(); selectNode; selectNode = mSelectedObjects.getNextNode()) + { + if( selectNode->getObject()->permModify() ) + { + return selectNode; + break; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// getFirstMoveableNode() +//----------------------------------------------------------------------------- +LLSelectNode* LLSelectMgr::getFirstMoveableNode(BOOL get_root) +{ + LLSelectNode* selectNode = NULL; + + if (get_root) + { + for(selectNode = mSelectedObjects.getFirstRootNode(); selectNode; selectNode = mSelectedObjects.getNextRootNode()) + { + if( selectNode->getObject()->permMove() ) + { + return selectNode; + break; + } + } + } + for(selectNode = mSelectedObjects.getFirstNode(); selectNode; selectNode = mSelectedObjects.getNextNode()) + { + if( selectNode->getObject()->permMove() ) + { + return selectNode; + break; + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// getFirstDeleteableObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLSelectMgr::getFirstDeleteableObject(BOOL get_root) +{ + //RN: don't currently support deletion of child objects, as that requires separating them first + // then derezzing to trash + get_root = TRUE; + + LLViewerObject* object = NULL; + if (get_root) + { + for(LLViewerObject* current = getFirstRootObject(); + current != NULL; + current = getNextRootObject()) + { + // you can delete an object if permissions allow it, you are + // the owner, you are an officer in the group that owns the + // object, or you are not the owner but it is on land you own + // or land owned by your group. (whew!) + if( (current->permModify()) + || (current->permYouOwner()) + || (!current->permAnyOwner()) // public + || (current->isOverAgentOwnedLand()) + || (current->isOverGroupOwnedLand()) + ) + { + + if( !current->isAttachment() ) + { + object = current; + break; + } + } + } + } + else + { + for(LLViewerObject* current = getFirstObject(); + current != NULL; + current = getNextObject()) + { + // you can delete an object if permissions allow it, you are + // the owner, you are an officer in the group that owns the + // object, or you are not the owner but it is on land you own + // or land owned by your group. (whew!) + if( (current->permModify()) + || (current->permYouOwner()) + || (!current->permAnyOwner()) // public + || (current->isOverAgentOwnedLand()) + || (current->isOverGroupOwnedLand()) + ) + { + if( !current->isAttachment() ) + { + object = current; + break; + } + } + } + } + + return object; +} + +//----------------------------------------------------------------------------- +// getFirstCopyableObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLSelectMgr::getFirstCopyableObject(BOOL get_root) +{ + LLViewerObject* object = NULL; + for(LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject()) + { + if( cur->permCopy() && !cur->isAttachment()) + { + object = cur; + break; + } + } + + if (get_root && object) + { + LLViewerObject *parent; + while ((parent = (LLViewerObject*)object->getParent())) + { + if (parent->isSelected()) + { + object = parent; + } + else + { + break; + } + } + } + + return object; +} + +//----------------------------------------------------------------------------- +// areMultpleEditableObjectsSelected() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::areMultpleEditableObjectsSelected() +{ + S32 count = 0; + for( LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject() ) + { + if( cur->permModify() ) + { + count++; + if( count > 1 ) + { + return TRUE; + } + } + } + return FALSE; +} + +//----------------------------------------------------------------------------- +// selectGetAllRootsValid() +// Returns true if the viewer has information on all selected objects +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetAllRootsValid() +{ + for( LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode() ) + { + + if( !node->mValid ) + { + return FALSE; + } + + if( !node->getObject() ) + { + return FALSE; + } + } + return TRUE; +} + + +//----------------------------------------------------------------------------- +// selectGetAllValid() +// Returns true if the viewer has information on all selected objects +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetAllValid() +{ + for( LLSelectNode* node = getFirstNode(); node; node = getNextNode() ) + { + + if( !node->mValid ) + { + return FALSE; + } + + if( !node->getObject() ) + { + return FALSE; + } + } + return TRUE; +} + + +//----------------------------------------------------------------------------- +// selectGetModify() - return true if current agent can modify all +// selected objects. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetModify() +{ + for( LLSelectNode* node = getFirstNode(); node; node = getNextNode() ) + { + if( !node->mValid ) + { + return FALSE; + } + LLViewerObject* object = node->getObject(); + if( !object || !object->permModify() ) + { + return FALSE; + } + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// selectGetRootsModify() - return true if current agent can modify all +// selected root objects. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetRootsModify() +{ + for( LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode() ) + { + if( !node->mValid ) + { + return FALSE; + } + LLViewerObject* object = node->getObject(); + if( !object || !object->permModify() ) + { + return FALSE; + } + } + + return TRUE; +} + + +//----------------------------------------------------------------------------- +// selectGetRootsTransfer() - return true if current agent can transfer all +// selected root objects. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetRootsTransfer() +{ + for(LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode()) + { + if(!node->mValid) + { + return FALSE; + } + LLViewerObject* object = node->getObject(); + if(!object || !object->permTransfer()) + { + return FALSE; + } + } + return TRUE; +} + +//----------------------------------------------------------------------------- +// selectGetRootsCopy() - return true if current agent can copy all +// selected root objects. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetRootsCopy() +{ + for(LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode()) + { + if(!node->mValid) + { + return FALSE; + } + LLViewerObject* object = node->getObject(); + if(!object || !object->permCopy()) + { + return FALSE; + } + } + return TRUE; +} + +//----------------------------------------------------------------------------- +// selectGetCreator() +// Creator information only applies to root objects. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetCreator(LLUUID& id, LLString& name) +{ + LLSelectNode* node = getFirstRootNode(); + if(!node) node = getFirstNode(); + if(!node) return FALSE; + if(!node->mValid) return FALSE; + LLViewerObject* obj = node->getObject(); + if(!obj) return FALSE; + if(!(obj->isRoot() || obj->isJointChild())) return FALSE; + + id = node->mPermissions->getCreator(); + + BOOL identical = TRUE; + for ( node = getNextRootNode(); node; node = getNextRootNode() ) + { + if (!node->mValid) + { + identical = FALSE; + break; + } + + if ( !(id == node->mPermissions->getCreator() ) ) + { + identical = FALSE; + break; + } + } + + if (identical) + { + char firstname[DB_FIRST_NAME_BUF_SIZE]; + char lastname[DB_LAST_NAME_BUF_SIZE]; + gCacheName->getName(id, firstname, lastname); + name.assign( firstname ); + name.append( " " ); + name.append( lastname ); + } + else + { + name.assign( "(multiple)" ); + } + + return identical; +} + + +//----------------------------------------------------------------------------- +// selectGetOwner() +// Owner information only applies to roots. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetOwner(LLUUID& id, LLString& name) +{ + LLSelectNode* node = getFirstRootNode(); + if(!node) node = getFirstNode(); + if(!node) return FALSE; + if(!node->mValid) return FALSE; + LLViewerObject* obj = node->getObject(); + if(!obj) return FALSE; + if(!(obj->isRootEdit() || obj->isRoot() || obj->isJointChild())) return FALSE; + + BOOL group_owner = FALSE; + id.setNull(); + node->mPermissions->getOwnership(id, group_owner); + + BOOL identical = TRUE; + for ( node = getNextRootNode(); node; node = getNextRootNode() ) + { + if (!node->mValid) + { + identical = FALSE; + break; + } + + LLUUID owner_id; + BOOL is_group_owned = FALSE; + if (!(node->mPermissions->getOwnership(owner_id, is_group_owned)) + || owner_id != id ) + { + identical = FALSE; + break; + } + } + + BOOL public_owner = (id.isNull() && !group_owner); + + if (identical) + { + if (group_owner) + { + name.assign( "(Group Owned)"); + } + else if(!public_owner) + { + char firstname[DB_FIRST_NAME_BUF_SIZE]; + char lastname[DB_LAST_NAME_BUF_SIZE]; + gCacheName->getName(id, firstname, lastname); + name.assign( firstname ); + name.append( " " ); + name.append( lastname ); + } + else + { + name.assign("Public"); + } + } + else + { + name.assign( "(multiple)" ); + } + + return identical; +} + + +//----------------------------------------------------------------------------- +// selectGetLastOwner() +// Owner information only applies to roots. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetLastOwner(LLUUID& id, LLString& name) +{ + LLSelectNode* node = getFirstRootNode(); + if(!node) node = getFirstNode(); + if(!node) return FALSE; + if(!node->mValid) return FALSE; + LLViewerObject* obj = node->getObject(); + if(!obj) return FALSE; + if(!(obj->isRoot() || obj->isJointChild())) return FALSE; + + id = node->mPermissions->getLastOwner(); + + BOOL identical = TRUE; + for ( node = getNextRootNode(); node; node = getNextRootNode() ) + { + if (!node->mValid) + { + identical = FALSE; + break; + } + + if ( !(id == node->mPermissions->getLastOwner() ) ) + { + identical = FALSE; + break; + } + } + + BOOL public_owner = (id.isNull()); + + if (identical) + { + if(!public_owner) + { + char firstname[DB_FIRST_NAME_BUF_SIZE]; + char lastname[DB_LAST_NAME_BUF_SIZE]; + gCacheName->getName(id, firstname, lastname); + name.assign( firstname ); + name.append( " " ); + name.append( lastname ); + } + else + { + name.assign("Public or Group"); + } + } + else + { + name.assign( "" ); + } + + return identical; +} + + +//----------------------------------------------------------------------------- +// selectGetGroup() +// Group information only applies to roots. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetGroup(LLUUID& id) +{ + LLSelectNode* node = getFirstRootNode(); + if(!node) node = getFirstNode(); + if(!node) return FALSE; + if(!node->mValid) return FALSE; + LLViewerObject* obj = node->getObject(); + if(!obj) return FALSE; + if(!(obj->isRoot() || obj->isJointChild())) return FALSE; + + id = node->mPermissions->getGroup(); + + BOOL identical = TRUE; + for ( node = getNextRootNode(); node; node = getNextRootNode() ) + { + if (!node->mValid) + { + identical = FALSE; + break; + } + + if ( !(id == node->mPermissions->getGroup() ) ) + { + identical = FALSE; + break; + } + } + + return identical; +} + +//----------------------------------------------------------------------------- +// selectIsGroupOwned() +// Only operates on root nodes. +// Returns TRUE if all have valid data and they are all group owned. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectIsGroupOwned() +{ + LLSelectNode* node = getFirstRootNode(); + if(!node) node = getFirstNode(); + if(!node) return FALSE; + if(!node->mValid) return FALSE; + LLViewerObject* obj = node->getObject(); + if(!obj) return FALSE; + if(!(obj->isRoot() || obj->isJointChild())) return FALSE; + + BOOL is_group_owned = node->mPermissions->isGroupOwned(); + + if(is_group_owned) + { + for ( node = getNextRootNode(); node; node = getNextRootNode() ) + { + if (!node->mValid) + { + is_group_owned = FALSE; + break; + } + + if ( !( node->mPermissions->isGroupOwned() ) ) + { + is_group_owned = FALSE; + break; + } + } + } + return is_group_owned; +} + +//----------------------------------------------------------------------------- +// selectGetPerm() +// Only operates on root nodes. +// Returns TRUE if all have valid data. +// mask_on has bits set to true where all permissions are true +// mask_off has bits set to true where all permissions are false +// if a bit is off both in mask_on and mask_off, the values differ within +// the selection. +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::selectGetPerm(U8 which_perm, U32* mask_on, U32* mask_off) +{ + LLSelectNode* node = getFirstRootNode(); + if (!node) return FALSE; + if (!node->mValid) return FALSE; + + U32 mask; + U32 mask_and = 0xffffffff; + U32 mask_or = 0x00000000; + BOOL all_valid = TRUE; + + for ( node = getFirstRootNode(); node; node = getNextRootNode() ) + { + if (!node->mValid) + { + all_valid = FALSE; + break; + } + + switch( which_perm ) + { + case PERM_BASE: + mask = node->mPermissions->getMaskBase(); + break; + case PERM_OWNER: + mask = node->mPermissions->getMaskOwner(); + break; + case PERM_GROUP: + mask = node->mPermissions->getMaskGroup(); + break; + case PERM_EVERYONE: + mask = node->mPermissions->getMaskEveryone(); + break; + case PERM_NEXT_OWNER: + mask = node->mPermissions->getMaskNextOwner(); + break; + default: + mask = 0x0; + break; + } + mask_and &= mask; + mask_or |= mask; + } + + if (all_valid) + { + // ...true through all ANDs means all true + *mask_on = mask_and; + + // ...false through all ORs means all false + *mask_off = ~mask_or; + return TRUE; + } + else + { + *mask_on = 0; + *mask_off = 0; + return FALSE; + } +} + + + +BOOL LLSelectMgr::selectGetOwnershipCost(S32* out_cost) +{ + return mSelectedObjects.getOwnershipCost(*out_cost); +} + +BOOL LLSelectMgr::selectGetPermissions(LLPermissions& perm) +{ + LLSelectNode* node = getFirstRootNode(); + if (!node) return FALSE; + if (!node->mValid) return FALSE; + BOOL valid = TRUE; + perm = *(node->mPermissions); + for(node = getNextRootNode(); node != NULL; node = getNextRootNode()) + { + if(!node->mValid) + { + valid = FALSE; + break; + } + perm.accumulate(*(node->mPermissions)); + } + return valid; +} + + +void LLSelectMgr::selectDelete() +{ + S32 deleteable_count = 0; + + BOOL locked_but_deleteable_object = FALSE; + BOOL no_copy_but_deleteable_object = FALSE; + BOOL all_owned_by_you = TRUE; + for(LLViewerObject* obj = getFirstObject(); + obj != NULL; + obj = getNextObject()) + { + if( obj->isAttachment() ) + { + continue; + } + + deleteable_count++; + + // Check to see if you can delete objects which are locked. + if(!obj->permMove()) + { + locked_but_deleteable_object = TRUE; + } + if(!obj->permCopy()) + { + no_copy_but_deleteable_object = TRUE; + } + if(!obj->permYouOwner()) + { + all_owned_by_you = FALSE; + } + } + + if( 0 == deleteable_count ) + { + make_ui_sound("UISndInvalidOp"); + return; + } + + if(locked_but_deleteable_object || + no_copy_but_deleteable_object || + !all_owned_by_you) + { + // convert any transient pie-menu selections to full selection so this operation + // has some context + // NOTE: if user cancels delete operation, this will potentially leave objects selected outside of build mode + // but this is ok, if not ideal + convertTransient(); + + //This is messy, but needed to get all english our of the UI. + if(locked_but_deleteable_object && !no_copy_but_deleteable_object && all_owned_by_you) + { + //Locked only + gViewerWindow->alertXml( "ConfirmObjectDeleteLock", + &LLSelectMgr::confirmDelete, + this); + } + else if(!locked_but_deleteable_object && no_copy_but_deleteable_object && all_owned_by_you) + { + //No Copy only + gViewerWindow->alertXml( "ConfirmObjectDeleteNoCopy", + &LLSelectMgr::confirmDelete, + this); + } + else if(!locked_but_deleteable_object && !no_copy_but_deleteable_object && !all_owned_by_you) + { + //not owned only + gViewerWindow->alertXml( "ConfirmObjectDeleteNoOwn", + &LLSelectMgr::confirmDelete, + this); + } + else if(locked_but_deleteable_object && no_copy_but_deleteable_object && all_owned_by_you) + { + //locked and no copy + gViewerWindow->alertXml( "ConfirmObjectDeleteLockNoCopy", + &LLSelectMgr::confirmDelete, + this); + } + else if(locked_but_deleteable_object && !no_copy_but_deleteable_object && !all_owned_by_you) + { + //locked and not owned + gViewerWindow->alertXml( "ConfirmObjectDeleteLockNoOwn", + &LLSelectMgr::confirmDelete, + this); + } + else if(!locked_but_deleteable_object && no_copy_but_deleteable_object && !all_owned_by_you) + { + //no copy and not owned + gViewerWindow->alertXml( "ConfirmObjectDeleteNoCopyNoOwn", + &LLSelectMgr::confirmDelete, + this); + } + else + { + //locked, no copy and not owned + gViewerWindow->alertXml( "ConfirmObjectDeleteLockNoCopyNoOwn", + &LLSelectMgr::confirmDelete, + this); + } + + + + } + else + { + confirmDelete(0, (void*)this); + } +} + +// static +void LLSelectMgr::confirmDelete(S32 option, void* data) +{ + LLSelectMgr* self = (LLSelectMgr*)data; + if(!self) return; + switch(option) + { + case 0: + { + // TODO: Make sure you have delete permissions on all of them. + LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH); + // attempt to derez into the trash. + LLDeRezInfo* info = new LLDeRezInfo(DRD_TRASH, trash_id); + self->sendListToRegions("DeRezObject", + packDeRezHeader, + packObjectLocalID, + (void*)info, + SEND_ONLY_ROOTS); + // VEFFECT: Delete Object - one effect for all deletes + if (self->mSelectType != SELECT_TYPE_HUD) + { + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + effectp->setPositionGlobal( self->getSelectionCenterGlobal() ); + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + F32 duration = 0.5f; + duration += self->getObjectCount() / 64.f; + effectp->setDuration(duration); + } + + gAgent.setLookAt(LOOKAT_TARGET_CLEAR); + + // Keep track of how many objects have been deleted. + F64 obj_delete_count = gViewerStats->getStat(LLViewerStats::ST_OBJECT_DELETE_COUNT); + obj_delete_count += self->getObjectCount(); + gViewerStats->setStat(LLViewerStats::ST_OBJECT_DELETE_COUNT, obj_delete_count ); + } + break; + case 1: + default: + break; + } +} + + +void LLSelectMgr::selectForceDelete() +{ + sendListToRegions( + "ObjectDelete", + packDeleteHeader, + packObjectLocalID, + (void*)TRUE, + SEND_ONLY_ROOTS); +} + + +// returns TRUE if anything is for sale. calculates the total price +// and stores that value in price. +BOOL LLSelectMgr::selectIsForSale(S32& price) +{ + BOOL any_for_sale = FALSE; + price = 0; + + LLSelectNode *node; + for (node = getFirstRootNode(); node; node = getNextRootNode() ) + { + if (node->mSaleInfo.isForSale()) + { + price += node->mSaleInfo.getSalePrice(); + any_for_sale = TRUE; + } + } + + return any_for_sale; + +} + +// returns TRUE if all nodes are valid. method also stores an +// accumulated sale info. +BOOL LLSelectMgr::selectGetSaleInfo(LLSaleInfo& sale_info) +{ + LLSelectNode* node = getFirstRootNode(); + if (!node) return FALSE; + if (!node->mValid) return FALSE; + BOOL valid = TRUE; + sale_info = node->mSaleInfo; + for(node = getNextRootNode(); node != NULL; node = getNextRootNode()) + { + if(!node->mValid) + { + valid = FALSE; + break; + } + sale_info.accumulate(node->mSaleInfo); + } + return valid; +} + +BOOL LLSelectMgr::selectGetAggregatePermissions(LLAggregatePermissions& ag_perm) +{ + LLSelectNode* node = getFirstNode(); + if (!node) return FALSE; + if (!node->mValid) return FALSE; + BOOL valid = TRUE; + ag_perm = node->mAggregatePerm; + for(node = getNextNode(); node != NULL; node = getNextNode()) + { + if(!node->mValid) + { + valid = FALSE; + break; + } + ag_perm.aggregate(node->mAggregatePerm); + } + return valid; +} + +BOOL LLSelectMgr::selectGetAggregateTexturePermissions(LLAggregatePermissions& ag_perm) +{ + LLSelectNode* node = getFirstNode(); + if (!node) return FALSE; + if (!node->mValid) return FALSE; + BOOL valid = TRUE; + ag_perm = node->getObject()->permYouOwner() ? node->mAggregateTexturePermOwner : node->mAggregateTexturePerm; + for(node = getNextNode(); node != NULL; node = getNextNode()) + { + if(!node->mValid) + { + valid = FALSE; + break; + } + ag_perm.aggregate(node->getObject()->permYouOwner() ? node->mAggregateTexturePermOwner : node->mAggregateTexturePerm); + } + return valid; +} + + +// returns TRUE is any node is currenly worn as an attachment +BOOL LLSelectMgr::selectionIsAttachment() +{ + return (mSelectType == SELECT_TYPE_ATTACHMENT || mSelectType == SELECT_TYPE_HUD); +} + +//-------------------------------------------------------------------- +// Duplicate objects +//-------------------------------------------------------------------- + +// JC - If this doesn't work right, duplicate the selection list +// before doing anything, do a deselect, then send the duplicate +// messages. +struct LLDuplicateData +{ + LLVector3 offset; + U32 flags; +}; + +void LLSelectMgr::selectDuplicate(const LLVector3& offset, BOOL select_copy) +{ + if (selectionIsAttachment()) + { + //RN: do not duplicate attachments + make_ui_sound("UISndInvalidOp"); + return; + } + LLDuplicateData data; + + data.offset = offset; + data.flags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0); + + sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS); + + if (select_copy) + { + // the new copy will be coming in selected + deselectAll(); + } + else + { + for (LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode()) + { + node->mDuplicated = TRUE; + node->mDuplicatePos = node->getObject()->getPositionGlobal(); + node->mDuplicateRot = node->getObject()->getRotation(); + } + } +} + +void LLSelectMgr::repeatDuplicate() +{ + if (selectionIsAttachment()) + { + //RN: do not duplicate attachments + make_ui_sound("UISndInvalidOp"); + return; + } + + LLSelectNode* node; + LLDynamicArray non_duplicated_objects; + + for (node = getFirstRootNode(); node; node = getNextRootNode()) + { + if (!node->mDuplicated) + { + non_duplicated_objects.put(node->getObject()); + } + } + + // make sure only previously duplicated objects are selected + for (S32 i = 0; i < non_duplicated_objects.count(); i++) + { + deselectObjectAndFamily(non_duplicated_objects[i]); + } + + // duplicate objects in place + LLDuplicateData data; + + data.offset = LLVector3::zero; + data.flags = 0x0; + + sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS); + + // move current selection based on delta from duplication position and update duplication position + for (node = getFirstRootNode(); node; node = getNextRootNode()) + { + if (node->mDuplicated) + { + LLQuaternion cur_rot = node->getObject()->getRotation(); + LLQuaternion rot_delta = (~node->mDuplicateRot * cur_rot); + LLQuaternion new_rot = cur_rot * rot_delta; + LLVector3d cur_pos = node->getObject()->getPositionGlobal(); + LLVector3d new_pos = cur_pos + ((cur_pos - node->mDuplicatePos) * rot_delta); + + node->mDuplicatePos = node->getObject()->getPositionGlobal(); + node->mDuplicateRot = node->getObject()->getRotation(); + node->getObject()->setPositionGlobal(new_pos); + node->getObject()->setRotation(new_rot); + } + } + + sendMultipleUpdate(UPD_ROTATION | UPD_POSITION); +} + +// static +void LLSelectMgr::packDuplicate( LLSelectNode* node, void *duplicate_data ) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID()); +} + + +//-------------------------------------------------------------------- +// Duplicate On Ray +//-------------------------------------------------------------------- + +// Duplicates the selected objects, but places the copy along a cast +// ray. +struct LLDuplicateOnRayData +{ + LLVector3 mRayStartRegion; + LLVector3 mRayEndRegion; + BOOL mBypassRaycast; + BOOL mRayEndIsIntersection; + LLUUID mRayTargetID; + BOOL mCopyCenters; + BOOL mCopyRotates; + U32 mFlags; +}; + +void LLSelectMgr::selectDuplicateOnRay(const LLVector3 &ray_start_region, + const LLVector3 &ray_end_region, + BOOL bypass_raycast, + BOOL ray_end_is_intersection, + const LLUUID &ray_target_id, + BOOL copy_centers, + BOOL copy_rotates, + BOOL select_copy) +{ + if (selectionIsAttachment()) + { + // do not duplicate attachments + make_ui_sound("UISndInvalidOp"); + return; + } + + LLDuplicateOnRayData data; + + data.mRayStartRegion = ray_start_region; + data.mRayEndRegion = ray_end_region; + data.mBypassRaycast = bypass_raycast; + data.mRayEndIsIntersection = ray_end_is_intersection; + data.mRayTargetID = ray_target_id; + data.mCopyCenters = copy_centers; + data.mCopyRotates = copy_rotates; + data.mFlags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0); + + sendListToRegions("ObjectDuplicateOnRay", + packDuplicateOnRayHead, packObjectLocalID, &data, SEND_ONLY_ROOTS); + + if (select_copy) + { + // the new copy will be coming in selected + deselectAll(); + } +} + +// static +void LLSelectMgr::packDuplicateOnRayHead(void *user_data) +{ + LLMessageSystem *msg = gMessageSystem; + LLDuplicateOnRayData *data = (LLDuplicateOnRayData *)user_data; + + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); + msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID() ); + msg->addVector3Fast(_PREHASH_RayStart, data->mRayStartRegion ); + msg->addVector3Fast(_PREHASH_RayEnd, data->mRayEndRegion ); + msg->addBOOLFast(_PREHASH_BypassRaycast, data->mBypassRaycast ); + msg->addBOOLFast(_PREHASH_RayEndIsIntersection, data->mRayEndIsIntersection ); + msg->addBOOLFast(_PREHASH_CopyCenters, data->mCopyCenters ); + msg->addBOOLFast(_PREHASH_CopyRotates, data->mCopyRotates ); + msg->addUUIDFast(_PREHASH_RayTargetID, data->mRayTargetID ); + msg->addU32Fast(_PREHASH_DuplicateFlags, data->mFlags ); +} + + + +//------------------------------------------------------------------------ +// Object position, scale, rotation update, all-in-one +//------------------------------------------------------------------------ + +void LLSelectMgr::sendMultipleUpdate(U32 type) +{ + if (type == UPD_NONE) return; + // send individual updates when selecting textures or individual objects + ESendType send_type = (gSavedSettings.getBOOL("SelectLinkedSet") && !getTEMode()) ? SEND_ONLY_ROOTS : SEND_ROOTS_FIRST; + if (send_type == SEND_ONLY_ROOTS) + { + // tell simulator to apply to whole linked sets + type |= UPD_LINKED_SETS; + } + + sendListToRegions( + "MultipleObjectUpdate", + packAgentAndSessionID, + packMultipleUpdate, + &type, + send_type); +} + +// static +void LLSelectMgr::packMultipleUpdate(LLSelectNode* node, void *user_data) +{ + LLViewerObject* object = node->getObject(); + U32 *type32 = (U32 *)user_data; + U8 type = (U8)*type32; + U8 data[256]; + + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() ); + gMessageSystem->addU8Fast(_PREHASH_Type, type ); + + S32 offset = 0; + + // JC: You MUST pack the data in this order. The receiving + // routine process_multiple_update_message on simulator will + // extract them in this order. + + if (type & UPD_POSITION) + { + htonmemcpy(&data[offset], &(object->getPosition().mV), MVT_LLVector3, 12); + offset += 12; + } + if (type & UPD_ROTATION) + { + LLQuaternion quat = object->getRotation(); + LLVector3 vec = quat.packToVector3(); + htonmemcpy(&data[offset], &(vec.mV), MVT_LLQuaternion, 12); + offset += 12; + } + if (type & UPD_SCALE) + { + //llinfos << "Sending object scale " << object->getScale() << llendl; + htonmemcpy(&data[offset], &(object->getScale().mV), MVT_LLVector3, 12); + offset += 12; + } + gMessageSystem->addBinaryDataFast(_PREHASH_Data, data, offset); +} + +//------------------------------------------------------------------------ +// Ownership +//------------------------------------------------------------------------ +struct LLOwnerData +{ + LLUUID owner_id; + LLUUID group_id; + BOOL override; +}; + +void LLSelectMgr::sendOwner(const LLUUID& owner_id, + const LLUUID& group_id, + BOOL override) +{ + LLOwnerData data; + + data.owner_id = owner_id; + data.group_id = group_id; + data.override = override; + + sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, &data, SEND_ONLY_ROOTS); +} + +// static +void LLSelectMgr::packOwnerHead(void *user_data) +{ + LLOwnerData *data = (LLOwnerData *)user_data; + + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); + gMessageSystem->nextBlockFast(_PREHASH_HeaderData); + gMessageSystem->addBOOLFast(_PREHASH_Override, data->override); + gMessageSystem->addUUIDFast(_PREHASH_OwnerID, data->owner_id); + gMessageSystem->addUUIDFast(_PREHASH_GroupID, data->group_id); +} + +//------------------------------------------------------------------------ +// Group +//------------------------------------------------------------------------ + +void LLSelectMgr::sendGroup(const LLUUID& group_id) +{ + LLUUID local_group_id(group_id); + sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, &local_group_id, SEND_ONLY_ROOTS); +} + + +//------------------------------------------------------------------------ +// Buy +//------------------------------------------------------------------------ + +struct LLBuyData +{ + LLDynamicArray mObjectsSent; + LLUUID mCategoryID; + LLSaleInfo mSaleInfo; +}; + +// *NOTE: does not work for multiple object buy, which UI does not +// currently support sale info is used for verification only, if it +// doesn't match region info then sale is canceled Need to get sale +// info -as displayed in the UI- for every item. +void LLSelectMgr::sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, const LLSaleInfo sale_info) +{ + LLBuyData buy; + buy.mCategoryID = category_id; + buy.mSaleInfo = sale_info; + sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, &buy, SEND_ONLY_ROOTS); +} + +// static +void LLSelectMgr::packBuyObjectIDs(LLSelectNode* node, void* data) +{ + LLBuyData* buy = (LLBuyData*)data; + + LLViewerObject* object = node->getObject(); + if(buy->mObjectsSent.find(object) == LLDynamicArray::FAIL) + { + buy->mObjectsSent.put(object); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() ); + gMessageSystem->addU8Fast(_PREHASH_SaleType, buy->mSaleInfo.getSaleType()); + gMessageSystem->addS32Fast(_PREHASH_SalePrice, buy->mSaleInfo.getSalePrice()); + } +} + +//------------------------------------------------------------------------ +// Permissions +//------------------------------------------------------------------------ + +struct LLPermData +{ + U8 mField; + BOOL mSet; + U32 mMask; + BOOL mOverride; +}; + +// TODO: Make this able to fail elegantly. +void LLSelectMgr::setObjectPermissions(U8 field, + BOOL set, + U32 mask, + BOOL override) +{ + LLPermData data; + + data.mField = field; + data.mSet = set; + data.mMask = mask; + data.mOverride = override; + + sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, &data, SEND_ONLY_ROOTS); +} + +void LLSelectMgr::packPermissionsHead(void* user_data) +{ + LLPermData* data = (LLPermData*)user_data; + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_HeaderData); + gMessageSystem->addBOOLFast(_PREHASH_Override, data->mOverride); +} + + +// Now that you've added a bunch of objects, send a select message +// on the entire list for efficiency. +/* +void LLSelectMgr::sendSelect() +{ + llerrs << "Not implemented" << llendl; +} +*/ + +void LLSelectMgr::deselectAll() +{ + if (!mSelectedObjects.getNumNodes()) + { + return; + } + + sendListToRegions( + "ObjectDeselect", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_INDIVIDUALS); + + removeAll(); + + mLastSentSelectionCenterGlobal.clearVec(); + + updatePointAt(); + gHUDManager->clearJoints(); + updateSelectionCenter(); +} + +void LLSelectMgr::deselectTransient() +{ + std::set objects_to_deselect; + LLSelectNode *nodep; + for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode()) + { + if (nodep->isTransient()) + { + objects_to_deselect.insert(nodep->getObject()); + } + } + + std::set::iterator iter; + for (iter = objects_to_deselect.begin(); + iter != objects_to_deselect.end(); + ++iter) + { + deselectObjectOnly(*iter); + } + + gHUDManager->clearJoints(); + updateSelectionCenter(); +} + +void LLSelectMgr::convertTransient() +{ + LLSelectNode *nodep; + for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode()) + { + nodep->setTransient(FALSE); + } +} + +void LLSelectMgr::deselectAllIfTooFar() +{ + if (isEmpty() || mSelectType == SELECT_TYPE_HUD) + { + return; + } + + // HACK: Don't deselect when we're navigating to rate an object's + // owner or creator. JC + if (gPieObject->getVisible() || gPieRate->getVisible() ) + { + return; + } + + LLVector3d selectionCenter = getSelectionCenterGlobal(); + if (gSavedSettings.getBOOL("LimitSelectDistance") + && !selectionCenter.isExactlyZero()) + { + F32 deselect_dist = gSavedSettings.getF32("MaxSelectDistance"); + F32 deselect_dist_sq = deselect_dist * deselect_dist; + + LLVector3d select_delta = gAgent.getPositionGlobal() - selectionCenter; + F32 select_dist_sq = (F32) select_delta.magVecSquared(); + + if (select_dist_sq > deselect_dist_sq) + { + if (gDebugSelectMgr) + { + llinfos << "Selection manager: auto-deselecting, select_dist = " << fsqrtf(select_dist_sq) << llendl; + llinfos << "agent pos global = " << gAgent.getPositionGlobal() << llendl; + llinfos << "selection pos global = " << selectionCenter << llendl; + } + + deselectAll(); + } + } +} + + +void LLSelectMgr::setObjectName(const LLString& name) +{ + // we only work correctly if 1 object is selected. + if(getRootObjectCount() == 1) + { + sendListToRegions("ObjectName", + packAgentAndSessionID, + packObjectName, + (void*)name.c_str(), + SEND_ONLY_ROOTS); + } + else if(getObjectCount() == 1) + { + sendListToRegions("ObjectName", + packAgentAndSessionID, + packObjectName, + (void*)name.c_str(), + SEND_INDIVIDUALS); + } +} + +void LLSelectMgr::setObjectDescription(const LLString& desc) +{ + // we only work correctly if 1 object is selected. + if(getRootObjectCount() == 1) + { + sendListToRegions("ObjectDescription", + packAgentAndSessionID, + packObjectDescription, + (void*)desc.c_str(), + SEND_ONLY_ROOTS); + } + else if(getObjectCount() == 1) + { + sendListToRegions("ObjectDescription", + packAgentAndSessionID, + packObjectDescription, + (void*)desc.c_str(), + SEND_INDIVIDUALS); + } +} + +void LLSelectMgr::setObjectCategory(const LLCategory& category) +{ + // for now, we only want to be able to set one root category at + // a time. + if(getRootObjectCount() != 1) return; + sendListToRegions("ObjectCategory", + packAgentAndSessionID, + packObjectCategory, + (void*)(&category), + SEND_ONLY_ROOTS); +} + +void LLSelectMgr::setObjectSaleInfo(const LLSaleInfo& sale_info) +{ + // Only one sale info at a time for now + if(getRootObjectCount() != 1) return; + sendListToRegions("ObjectSaleInfo", + packAgentAndSessionID, + packObjectSaleInfo, + (void*)(&sale_info), + SEND_ONLY_ROOTS); +} + +//---------------------------------------------------------------------- +// Attachments +//---------------------------------------------------------------------- + +void LLSelectMgr::sendAttach(U8 attachment_point) +{ + LLViewerObject* attach_object = mSelectedObjects.getFirstRootObject(); + + if (!attach_object || !gAgent.getAvatarObject() || mSelectType != SELECT_TYPE_WORLD) + { + return; + } + + BOOL build_mode = gToolMgr->inEdit(); + // Special case: Attach to default location for this object. + if (0 == attachment_point) + { + sendListToRegions( + "ObjectAttach", + packAgentIDAndSessionAndAttachment, + packObjectIDAndRotation, + &attachment_point, + SEND_ONLY_ROOTS ); + if (!build_mode) + { + deselectAll(); + } + } + else + { + LLViewerJointAttachment* attachment = gAgent.getAvatarObject()->mAttachmentPoints.getIfThere(attachment_point); + if (attachment) + { + LLQuaternion object_world_rot = attach_object->getRenderRotation(); + LLQuaternion attachment_pt__world_rot = attachment->getWorldRotation(); + LLQuaternion local_rot = object_world_rot * ~attachment_pt__world_rot; + + F32 x,y,z; + local_rot.getEulerAngles(&x, &y, &z); + // snap to nearest 90 degree rotation + // make sure all euler angles are positive + if (x < F_PI_BY_TWO) x += F_TWO_PI; + if (y < F_PI_BY_TWO) y += F_TWO_PI; + if (z < F_PI_BY_TWO) z += F_TWO_PI; + + // add 45 degrees so that rounding down becomes rounding off + x += F_PI_BY_TWO / 2.f; + y += F_PI_BY_TWO / 2.f; + z += F_PI_BY_TWO / 2.f; + // round down to nearest multiple of 90 degrees + x -= fmodf(x, F_PI_BY_TWO); + y -= fmodf(y, F_PI_BY_TWO); + z -= fmodf(z, F_PI_BY_TWO); + + // pass the requested rotation on to the simulator + local_rot.setQuat(x, y, z); + attach_object->setRotation(local_rot); + + sendListToRegions( + "ObjectAttach", + packAgentIDAndSessionAndAttachment, + packObjectIDAndRotation, + &attachment_point, + SEND_ONLY_ROOTS ); + if (!build_mode) + { + deselectAll(); + } + } + } +} + +void LLSelectMgr::sendDetach() +{ + if (!mSelectedObjects.getNumNodes() || mSelectType == SELECT_TYPE_WORLD) + { + return; + } + + sendListToRegions( + "ObjectDetach", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_ONLY_ROOTS ); +} + + +void LLSelectMgr::sendDropAttachment() +{ + if (!mSelectedObjects.getNumNodes() || mSelectType == SELECT_TYPE_WORLD) + { + return; + } + + sendListToRegions( + "ObjectDrop", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_ONLY_ROOTS); +} + +//---------------------------------------------------------------------- +// Links +//---------------------------------------------------------------------- + +void LLSelectMgr::sendLink() +{ + if (!mSelectedObjects.getNumNodes()) + { + return; + } + + sendListToRegions( + "ObjectLink", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_ONLY_ROOTS); +} + +void LLSelectMgr::sendDelink() +{ + if (!mSelectedObjects.getNumNodes()) + { + return; + } + + // Delink needs to send individuals so you can unlink a single object from + // a linked set. + sendListToRegions( + "ObjectDelink", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_INDIVIDUALS); +} + + +//---------------------------------------------------------------------- +// Hinges +//---------------------------------------------------------------------- + +void LLSelectMgr::sendHinge(U8 type) +{ + if (!mSelectedObjects.getNumNodes()) + { + return; + } + + sendListToRegions( + "ObjectHinge", + packHingeHead, + packObjectLocalID, + &type, + SEND_ONLY_ROOTS); +} + + +void LLSelectMgr::sendDehinge() +{ + if (!mSelectedObjects.getNumNodes()) + { + return; + } + + sendListToRegions( + "ObjectDehinge", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_ONLY_ROOTS); +} + +void LLSelectMgr::sendSelect() +{ + if (!mSelectedObjects.getNumNodes()) + { + return; + } + + sendListToRegions( + "ObjectSelect", + packAgentAndSessionID, + packObjectLocalID, + NULL, + SEND_INDIVIDUALS); +} + +// static +void LLSelectMgr::packHingeHead(void *user_data) +{ + U8 *type = (U8 *)user_data; + + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() ); + gMessageSystem->nextBlockFast(_PREHASH_JointType); + gMessageSystem->addU8Fast(_PREHASH_Type, *type ); +} + + +void LLSelectMgr::selectionDump() +{ + LLViewerObject *object; + + for (object = getFirstObject(); object; object = getNextObject() ) + { + object->dump(); + } +} + +void LLSelectMgr::saveSelectedObjectColors() +{ + LLSelectNode* selectNode; + for (selectNode = getFirstNode(); selectNode; selectNode = getNextNode() ) + { + selectNode->saveColors(); + } +} + +void LLSelectMgr::saveSelectedObjectTextures() +{ + LLSelectNode* selectNode; + + // invalidate current selection so we update saved textures + for (selectNode = getFirstNode(); selectNode; selectNode = getNextNode() ) + { + selectNode->mValid = FALSE; + } + + // request object properties message to get updated permissions data + sendSelect(); +} + + +// This routine should be called whenever a drag is initiated. +// also need to know to which simulator to send update message +void LLSelectMgr::saveSelectedObjectTransform(EActionType action_type) +{ + LLSelectNode* selectNode; + + if (isEmpty()) + { + // nothing selected, so nothing to save + return; + } + + for (selectNode = getFirstNode(); selectNode; selectNode = getNextNode() ) + { + LLViewerObject* object; + object = selectNode->getObject(); + selectNode->mSavedPositionLocal = object->getPosition(); + if (object->isAttachment()) + { + if (object->isRootEdit()) + { + LLXform* parent_xform = object->mDrawable->getXform()->getParent(); + selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition()); + } + else + { + LLViewerObject* attachment_root = (LLViewerObject*)object->getParent(); + LLXform* parent_xform = attachment_root->mDrawable->getXform()->getParent(); + LLVector3 root_pos = (attachment_root->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition(); + LLQuaternion root_rot = (attachment_root->getRotation() * parent_xform->getWorldRotation()); + selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * root_rot) + root_pos); + } + selectNode->mSavedRotation = object->getRenderRotation(); + } + else + { + selectNode->mSavedPositionGlobal = object->getPositionGlobal(); + selectNode->mSavedRotation = object->getRotationRegion(); + } + + selectNode->mSavedScale = object->getScale(); + selectNode->saveTextureScaleRatios(); + + if (object->isAttachment() && + action_type != SELECT_ACTION_TYPE_PICK) + { + LLSelectAction* selectAction = new LLSelectAction(); + selectAction->mActionType = action_type; + selectAction->mPosition = object->getPosition(); + selectAction->mRotation = object->getRotation(); + selectAction->mScale = object->getScale(); + selectAction->mObjectID = object->getID(); + selectAction->mIndividualSelection = selectNode->mIndividualSelection; + + mUndoQueue.push_back(selectAction); + + while ((mUndoQueue.size() > (U32)MAX_ACTION_QUEUE_SIZE)) + { + LLSelectAction* action = mUndoQueue.front(); + mUndoQueue.pop_front(); + delete action; + } + + // remove this object from the redo queue + std::deque::iterator it; + for (it = mRedoQueue.begin(); it != mRedoQueue.end();) + { + if ((*it)->mObjectID == object->getID()) + { + LLSelectAction* actionp = *it; + it = mRedoQueue.erase(it); + delete actionp; + } + else + { + ++it; + } + } + } + } + mSavedSelectionBBox = getBBoxOfSelection(); +} + +void LLSelectMgr::selectionUpdatePhysics(BOOL physics) +{ + LLViewerObject *object; + + for (object = getFirstObject(); object; object = getNextObject() ) + { + if ( !object->permModify() // preemptive permissions check + || !(object->isRoot() // don't send for child objects + || object->isJointChild())) + { + continue; + } + object->setFlags( FLAGS_USE_PHYSICS, physics); + } +} + +void LLSelectMgr::selectionUpdateTemporary(BOOL is_temporary) +{ + LLViewerObject *object; + + for (object = getFirstObject(); object; object = getNextObject() ) + { + if ( !object->permModify() // preemptive permissions check + || !(object->isRoot() // don't send for child objects + || object->isJointChild())) + { + continue; + } + object->setFlags( FLAGS_TEMPORARY_ON_REZ, is_temporary); + } +} + +void LLSelectMgr::selectionUpdatePhantom(BOOL is_phantom) +{ + LLViewerObject *object; + + for (object = getFirstObject(); object; object = getNextObject() ) + { + if ( !object->permModify() // preemptive permissions check + || !(object->isRoot() // don't send for child objects + || object->isJointChild())) + { + continue; + } + object->setFlags( FLAGS_PHANTOM, is_phantom); + } +} + +void LLSelectMgr::selectionUpdateCastShadows(BOOL cast_shadows) +{ + LLViewerObject *object; + + for (object = getFirstObject(); object; object = getNextObject() ) + { + if ( !object->permModify() // preemptive permissions check + || object->isJointChild()) + { + continue; + } + object->setFlags( FLAGS_CAST_SHADOWS, cast_shadows); + } +} + + +//---------------------------------------------------------------------- +// Helpful packing functions for sendObjectMessage() +//---------------------------------------------------------------------- + +// static +void LLSelectMgr::packAgentIDAndSessionAndAttachment( void *user_data) +{ + U8 *attachment_point = (U8*)user_data; + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->addU8Fast(_PREHASH_AttachmentPoint, *attachment_point); +} + +// static +void LLSelectMgr::packAgentID( void *user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); +} + +// static +void LLSelectMgr::packAgentAndSessionID(void* user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); +} + +// static +void LLSelectMgr::packAgentAndGroupID(void* user_data) +{ + LLOwnerData *data = (LLOwnerData *)user_data; + + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, data->owner_id ); + gMessageSystem->addUUIDFast(_PREHASH_GroupID, data->group_id ); +} + +// static +void LLSelectMgr::packAgentAndSessionAndGroupID(void* user_data) +{ + LLUUID* group_idp = (LLUUID*) user_data; + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->addUUIDFast(_PREHASH_GroupID, *group_idp); +} + +// static +void LLSelectMgr::packDuplicateHeader(void* data) +{ + LLUUID group_id(gAgent.getGroupID()); + packAgentAndSessionAndGroupID(&group_id); + + LLDuplicateData* dup_data = (LLDuplicateData*) data; + + gMessageSystem->nextBlockFast(_PREHASH_SharedData); + gMessageSystem->addVector3Fast(_PREHASH_Offset, dup_data->offset); + gMessageSystem->addU32Fast(_PREHASH_DuplicateFlags, dup_data->flags); +} + +// static +void LLSelectMgr::packDeleteHeader(void* userdata) +{ + BOOL force = (BOOL)(intptr_t)userdata; + + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->addBOOLFast(_PREHASH_Force, force); +} + +// static +void LLSelectMgr::packAgentGroupAndCatID(void* user_data) +{ + LLBuyData* buy = (LLBuyData*)user_data; + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); + gMessageSystem->addUUIDFast(_PREHASH_CategoryID, buy->mCategoryID); +} + +//static +void LLSelectMgr::packDeRezHeader(void* user_data) +{ + LLDeRezInfo* info = (LLDeRezInfo*)user_data; + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_AgentBlock); + gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); + gMessageSystem->addU8Fast(_PREHASH_Destination, (U8)info->mDestination); + gMessageSystem->addUUIDFast(_PREHASH_DestinationID, info->mDestinationID); + LLUUID tid; + tid.generate(); + gMessageSystem->addUUIDFast(_PREHASH_TransactionID, tid); + const U8 PACKET = 1; + gMessageSystem->addU8Fast(_PREHASH_PacketCount, PACKET); + gMessageSystem->addU8Fast(_PREHASH_PacketNumber, PACKET); +} + +// static +void LLSelectMgr::packObjectID(LLSelectNode* node, void *user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addUUIDFast(_PREHASH_ObjectID, node->getObject()->mID ); +} + +void LLSelectMgr::packObjectIDAndRotation(LLSelectNode* node, void *user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() ); + gMessageSystem->addQuatFast(_PREHASH_Rotation, node->getObject()->getRotation()); +} + +void LLSelectMgr::packObjectClickAction(LLSelectNode* node, void *user_data) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() ); + gMessageSystem->addU8("ClickAction", node->getObject()->getClickAction()); +} + +// static +void LLSelectMgr::packObjectLocalID(LLSelectNode* node, void *) +{ + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID()); +} + +// static +void LLSelectMgr::packObjectName(LLSelectNode* node, void* user_data) +{ + char* name = (char*)user_data; + if(!name) return; + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); + gMessageSystem->addStringFast(_PREHASH_Name, name); +} + +// static +void LLSelectMgr::packObjectDescription(LLSelectNode* node, + void* user_data) +{ + char* desc = (char*)user_data; + if(!desc) return; + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); + gMessageSystem->addStringFast(_PREHASH_Description, desc); +} + +// static +void LLSelectMgr::packObjectCategory(LLSelectNode* node, void* user_data) +{ + LLCategory* category = (LLCategory*)user_data; + if(!category) return; + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); + category->packMessage(gMessageSystem); +} + +// static +void LLSelectMgr::packObjectSaleInfo(LLSelectNode* node, void* user_data) +{ + LLSaleInfo* sale_info = (LLSaleInfo*)user_data; + if(!sale_info) return; + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID()); + sale_info->packMessage(gMessageSystem); +} + +// static +void LLSelectMgr::packPhysics(LLSelectNode* node, void *user_data) +{ +} + +// static +void LLSelectMgr::packShape(LLSelectNode* node, void *user_data) +{ +} + +// static +void LLSelectMgr::packPermissions(LLSelectNode* node, void *user_data) +{ + LLPermData *data = (LLPermData *)user_data; + + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID()); + + gMessageSystem->addU8Fast(_PREHASH_Field, data->mField); + gMessageSystem->addBOOLFast(_PREHASH_Set, data->mSet); + gMessageSystem->addU32Fast(_PREHASH_Mask, data->mMask); +} + +// Utility function to send some information to every region containing +// an object on the selection list. We want to do this to reduce the total +// number of packets sent by the viewer. +void LLSelectMgr::sendListToRegions(const LLString& message_name, + void (*pack_header)(void *user_data), + void (*pack_body)(LLSelectNode* node, void *user_data), + void *user_data, + ESendType send_type) +{ + LLSelectNode* node; + LLViewerRegion* last_region; + LLViewerRegion* current_region; + + S32 objects_sent = 0; + S32 packets_sent = 0; + S32 objects_in_this_packet = 0; + + std::queue nodes_to_send; + + switch(send_type) + { + case SEND_ONLY_ROOTS: + node = mSelectedObjects.getFirstRootNode(); + while(node) + { + nodes_to_send.push(node); + node = mSelectedObjects.getNextRootNode(); + } + break; + case SEND_INDIVIDUALS: + node = mSelectedObjects.getFirstNode(); + while(node) + { + nodes_to_send.push(node); + node = mSelectedObjects.getNextNode(); + } + break; + case SEND_ROOTS_FIRST: + // first roots... + node = mSelectedObjects.getFirstNode(); + while(node) + { + if (node->getObject()->isRootEdit()) + { + nodes_to_send.push(node); + } + node = mSelectedObjects.getNextNode(); + } + + // then children... + node = mSelectedObjects.getFirstNode(); + while(node) + { + if (!node->getObject()->isRootEdit()) + { + nodes_to_send.push(node); + } + node = mSelectedObjects.getNextNode(); + } + break; + case SEND_CHILDREN_FIRST: + // first children... + node = mSelectedObjects.getFirstNode(); + while(node) + { + if (!node->getObject()->isRootEdit()) + { + nodes_to_send.push(node); + } + node = mSelectedObjects.getNextNode(); + } + + // ...then roots + node = mSelectedObjects.getFirstNode(); + while(node) + { + if (node->getObject()->isRootEdit()) + { + nodes_to_send.push(node); + } + node = mSelectedObjects.getNextNode(); + } + break; + + default: + llerrs << "Bad send type " << send_type << " passed to SendListToRegions()" << llendl; + } + + // bail if nothing selected + if (nodes_to_send.empty()) return; + + node = nodes_to_send.front(); + nodes_to_send.pop(); + + // cache last region information + current_region = node->getObject()->getRegion(); + + // Start duplicate message + // CRO: this isn't + gMessageSystem->newMessage(message_name.c_str()); + (*pack_header)(user_data); + + // For each object + while (node != NULL) + { + // remember the last region, look up the current one + last_region = current_region; + current_region = node->getObject()->getRegion(); + + // if to same simulator and message not too big + if ((current_region == last_region) + && (gMessageSystem->mCurrentSendTotal < MTUBYTES) + && (objects_in_this_packet < MAX_OBJECTS_PER_PACKET)) + { + // add another instance of the body of the data + (*pack_body)(node, user_data); + ++objects_sent; + ++objects_in_this_packet; + + // and on to the next object + if(nodes_to_send.empty()) + { + node = NULL; + } + else + { + node = nodes_to_send.front(); + nodes_to_send.pop(); + } + } + else + { + // otherwise send current message and start new one + gMessageSystem->sendReliable( last_region->getHost()); + packets_sent++; + objects_in_this_packet = 0; + + gMessageSystem->newMessage(message_name.c_str()); + (*pack_header)(user_data); + + // don't move to the next object, we still need to add the + // body data. + } + } + + // flush messages + if (gMessageSystem->mCurrentSendTotal > 0) + { + gMessageSystem->sendReliable( current_region->getHost()); + packets_sent++; + } + else + { + gMessageSystem->clearMessage(); + } + + // llinfos << "sendListToRegions " << message_name << " obj " << objects_sent << " pkt " << packets_sent << llendl; +} + + +// +// Network communications +// + +void LLSelectMgr::requestObjectPropertiesFamily(LLViewerObject* object) +{ + LLMessageSystem* msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addU32Fast(_PREHASH_RequestFlags, 0x0 ); + msg->addUUIDFast(_PREHASH_ObjectID, object->mID ); + + LLViewerRegion* regionp = object->getRegion(); + msg->sendReliable( regionp->getHost() ); +} + + +// static +void LLSelectMgr::processObjectProperties(LLMessageSystem* msg, void** user_data) +{ + S32 i; + S32 count = msg->getNumberOfBlocksFast(_PREHASH_ObjectData); + for (i = 0; i < count; i++) + { + LLUUID id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id, i); + + LLUUID creator_id; + LLUUID owner_id; + LLUUID group_id; + LLUUID last_owner_id; + U64 creation_date; + LLUUID extra_id; + U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask; + LLSaleInfo sale_info; + LLCategory category; + LLAggregatePermissions ag_perms; + LLAggregatePermissions ag_texture_perms; + LLAggregatePermissions ag_texture_perms_owner; + + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_CreatorID, creator_id, i); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id, i); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, group_id, i); + msg->getU64Fast(_PREHASH_ObjectData, _PREHASH_CreationDate, creation_date, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, base_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, owner_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_GroupMask, group_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, everyone_mask, i); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, next_owner_mask, i); + sale_info.unpackMultiMessage(msg, _PREHASH_ObjectData, i); + + ag_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePerms, i); + ag_texture_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTextures, i); + ag_texture_perms_owner.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTexturesOwner, i); + category.unpackMultiMessage(msg, _PREHASH_ObjectData, i); + + S16 inv_serial = 0; + msg->getS16Fast(_PREHASH_ObjectData, _PREHASH_InventorySerial, inv_serial, i); + + LLUUID item_id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ItemID, item_id, i); + LLUUID folder_id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FolderID, folder_id, i); + LLUUID from_task_id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FromTaskID, from_task_id, i); + + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, last_owner_id, i); + + char name[DB_INV_ITEM_NAME_BUF_SIZE]; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, DB_INV_ITEM_NAME_BUF_SIZE, name, i); + char desc[DB_INV_ITEM_DESC_BUF_SIZE]; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, DB_INV_ITEM_DESC_BUF_SIZE, desc, i); + + char touch_name[DB_INV_ITEM_NAME_BUF_SIZE]; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_TouchName, DB_INV_ITEM_NAME_BUF_SIZE, touch_name, i); + char sit_name[DB_INV_ITEM_DESC_BUF_SIZE]; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_SitName, DB_INV_ITEM_DESC_BUF_SIZE, sit_name, i); + + //unpack TE IDs + std::vector texture_ids; + S32 size = msg->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_TextureID); + if (size > 0) + { + S8 packed_buffer[SELECT_MAX_TES * UUID_BYTES]; + msg->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureID, packed_buffer, 0, i, SELECT_MAX_TES * UUID_BYTES); + + for (S32 buf_offset = 0; buf_offset < size; buf_offset += UUID_BYTES) + { + LLUUID id; + memcpy(id.mData, packed_buffer + buf_offset, UUID_BYTES); + texture_ids.push_back(id); + } + } + + + // Iterate through nodes at end, since it can be on both the regular AND hover list + BOOL found = FALSE; + LLSelectNode* node; + for (node = gSelectMgr->mSelectedObjects.getFirstNode(); + node; + node = gSelectMgr->mSelectedObjects.getNextNode()) + { + if (node->getObject()->mID == id) + { + found = TRUE; + break; + } + } + + + if (node) + { + if (node->mInventorySerial != inv_serial) + { + node->getObject()->dirtyInventory(); + } + + // save texture data as soon as we get texture perms first time + if (!node->mValid) + { + BOOL can_copy = FALSE; + BOOL can_transfer = FALSE; + + LLAggregatePermissions::EValue value = LLAggregatePermissions::AP_NONE; + if(node->getObject()->permYouOwner()) + { + value = ag_texture_perms_owner.getValue(PERM_COPY); + if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) + { + can_copy = TRUE; + } + value = ag_texture_perms_owner.getValue(PERM_TRANSFER); + if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) + { + can_transfer = TRUE; + } + } + else + { + value = ag_texture_perms.getValue(PERM_COPY); + if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) + { + can_copy = TRUE; + } + value = ag_texture_perms.getValue(PERM_TRANSFER); + if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL) + { + can_transfer = TRUE; + } + } + + if (can_copy && can_transfer) + { + // this should be the only place that saved textures is called + node->saveTextures(texture_ids); + } + } + + node->mValid = TRUE; + node->mPermissions->init(creator_id, owner_id, + last_owner_id, group_id); + node->mPermissions->initMasks(base_mask, owner_mask, everyone_mask, group_mask, next_owner_mask); + node->mCreationDate = creation_date; + node->mItemID = item_id; + node->mFolderID = folder_id; + node->mFromTaskID = from_task_id; + node->mName.assign(name); + node->mDescription.assign(desc); + node->mSaleInfo = sale_info; + node->mAggregatePerm = ag_perms; + node->mAggregateTexturePerm = ag_texture_perms; + node->mAggregateTexturePermOwner = ag_texture_perms_owner; + node->mCategory = category; + node->mInventorySerial = inv_serial; + node->mSitName.assign(sit_name); + node->mTouchName.assign(touch_name); + } + } + + dialog_refresh_all(); + + // silly hack to allow 'save into inventory' + if(gPopupMenuView->getVisible()) + { + gPopupMenuView->setItemEnabled(SAVE_INTO_INVENTORY, + enable_save_into_inventory(NULL)); + } + + // hack for left-click buy object + LLToolPie::selectionPropertiesReceived(); +} + +// static +void LLSelectMgr::processObjectPropertiesFamily(LLMessageSystem* msg, void** user_data) +{ + LLUUID id; + + U32 request_flags; + LLUUID creator_id; + LLUUID owner_id; + LLUUID group_id; + LLUUID extra_id; + U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask; + LLSaleInfo sale_info; + LLCategory category; + + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_RequestFlags, request_flags ); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id ); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id ); + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, group_id ); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, base_mask ); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, owner_mask ); + msg->getU32Fast(_PREHASH_ObjectData,_PREHASH_GroupMask, group_mask ); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, everyone_mask ); + msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, next_owner_mask); + sale_info.unpackMessage(msg, _PREHASH_ObjectData); + category.unpackMessage(msg, _PREHASH_ObjectData); + + LLUUID last_owner_id; + msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, last_owner_id ); + + // unpack name & desc + char name[DB_INV_ITEM_NAME_BUF_SIZE]; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, DB_INV_ITEM_NAME_BUF_SIZE, name); + + char desc[DB_INV_ITEM_DESC_BUF_SIZE]; + msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, DB_INV_ITEM_DESC_BUF_SIZE, desc); + + // the reporter widget askes the server for info about picked objects + if (request_flags & (COMPLAINT_REPORT_REQUEST | BUG_REPORT_REQUEST)) + { + EReportType report_type = (COMPLAINT_REPORT_REQUEST & request_flags) ? COMPLAINT_REPORT : BUG_REPORT; + LLFloaterReporter *reporterp = LLFloaterReporter::getReporter(report_type); + if (reporterp) + { + char first_name[DB_FIRST_NAME_BUF_SIZE]; + char last_name[DB_LAST_NAME_BUF_SIZE]; + gCacheName->getName(owner_id, first_name, last_name); + LLString fullname(first_name); + fullname.append(" "); + fullname.append(last_name); + reporterp->setPickedObjectProperties(name, fullname.c_str()); + } + } + + // Now look through all of the hovered nodes + BOOL found = FALSE; + LLSelectNode* node; + for (node = gSelectMgr->mHoverObjects.getFirstNode(); + node; + node = gSelectMgr->mHoverObjects.getNextNode()) + { + if (node->getObject()->mID == id) + { + found = TRUE; + break; + } + } + + if (node) + { + node->mValid = TRUE; + node->mPermissions->init(LLUUID::null, owner_id, + last_owner_id, group_id); + node->mPermissions->initMasks(base_mask, owner_mask, everyone_mask, group_mask, next_owner_mask); + node->mSaleInfo = sale_info; + node->mCategory = category; + node->mName.assign(name); + node->mDescription.assign(desc); + } + + dialog_refresh_all(); +} + + +// static +void LLSelectMgr::processForceObjectSelect(LLMessageSystem* msg, void**) +{ + BOOL reset_list; + msg->getBOOL("Header", "ResetList", reset_list); + + if (reset_list) + { + gSelectMgr->deselectAll(); + } + + LLUUID full_id; + S32 local_id; + LLViewerObject* object; + LLDynamicArray objects; + S32 i; + S32 block_count = msg->getNumberOfBlocks("Data"); + + for (i = 0; i < block_count; i++) + { + msg->getS32("Data", "LocalID", local_id, i); + + gObjectList.getUUIDFromLocal(full_id, + local_id, + msg->getSenderIP(), + msg->getSenderPort()); + object = gObjectList.findObject(full_id); + if (object) + { + objects.put(object); + } + } + + // Don't select, just highlight + gSelectMgr->highlightObjectAndFamily(objects); +} + + +extern LLGLdouble gGLModelView[16]; + +void LLSelectMgr::updateSilhouettes() +{ + LLSelectNode *node; + S32 num_sils_genned = 0; + + LLVector3d cameraPos = gAgent.getCameraPositionGlobal(); + F32 currentCameraZoom = gAgent.getCurrentCameraBuildOffset(); + + if (!mSilhouetteImagep) + { + LLUUID id; + id.set( gViewerArt.getString("silhouette.tga") ); + mSilhouetteImagep = gImageList.getImage(id, TRUE, TRUE); + } + + + if((cameraPos - mLastCameraPos).magVecSquared() > SILHOUETTE_UPDATE_THRESHOLD_SQUARED * currentCameraZoom * currentCameraZoom) + { + for (node = mSelectedObjects.getFirstNode(); node; node = mSelectedObjects.getNextNode() ) + { + if (node->getObject()) + { + node->getObject()->setChanged(LLXform::SILHOUETTE); + } + } + + mLastCameraPos = gAgent.getCameraPositionGlobal(); + } + + LLDynamicArray changed_objects; + + if (mSelectedObjects.getNumNodes()) + { + //gGLSPipelineSelection.set(); + + //mSilhouetteImagep->bindTexture(); + //glAlphaFunc(GL_GREATER, sHighlightAlphaTest); + + for (S32 pass = 0; pass < 2; pass++) + { + for (node = mSelectedObjects.getFirstNode(); node; node = mSelectedObjects.getNextNode() ) + { + LLViewerObject* objectp = node->getObject(); + + // do roots first, then children so that root flags are cleared ASAP + BOOL roots_only = (pass == 0); + BOOL is_root = (objectp->isRootEdit()); + if (roots_only != is_root || objectp->mDrawable.isNull()) + { + continue; + } + + if (!node->mSilhouetteExists + || objectp->isChanged(LLXform::SILHOUETTE) + || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE))) + { + if (num_sils_genned++ < MAX_SILS_PER_FRAME && objectp->mDrawable->isVisible()) + { + generateSilhouette(node, gCamera->getOrigin()); + changed_objects.put(objectp); + } + else if (objectp->isAttachment()) + { + //RN: hack for orthogonal projection of HUD attachments + LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent(); + if (attachment_pt && attachment_pt->getIsHUDAttachment()) + { + LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f); + generateSilhouette(node, camera_pos); + } + } + } + } + } + } + + if (mRectSelectedObjects.size() > 0) + { + //gGLSPipelineSelection.set(); + + //mSilhouetteImagep->bindTexture(); + //glAlphaFunc(GL_GREATER, sHighlightAlphaTest); + + std::set roots; + + // sync mHighlightedObjects with mRectSelectedObjects since the latter is rebuilt every frame and former + // persists from frame to frame to avoid regenerating object silhouettes + // mHighlightedObjects includes all siblings of rect selected objects + + BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet"); + + // generate list of roots from current object selection + for (std::set >::iterator iter = mRectSelectedObjects.begin(); + iter != mRectSelectedObjects.end(); iter++) + { + LLViewerObject *objectp = *iter; + if (select_linked_set) + { + LLViewerObject *rootp = (LLViewerObject*)objectp->getRoot(); + roots.insert(rootp); + } + else + { + roots.insert(objectp); + } + } + + // remove highlight nodes not in roots list + LLDynamicArray remove_these_nodes; + LLDynamicArray remove_these_roots; + for (LLSelectNode* nodep = mHighlightedObjects.getFirstNode(); nodep; nodep = mHighlightedObjects.getNextNode()) + { + LLViewerObject* objectp = nodep->getObject(); + if (objectp->isRoot() || !select_linked_set) + { + if (roots.count(objectp) == 0) + { + remove_these_nodes.put(nodep); + } + else + { + remove_these_roots.put(objectp); + } + } + else + { + LLViewerObject* rootp = (LLViewerObject*)objectp->getRoot(); + + if (roots.count(rootp) == 0) + { + remove_these_nodes.put(nodep); + } + } + } + + // remove all highlight nodes no longer in rectangle selection + S32 i; + for (i = 0; i < remove_these_nodes.count(); i++) + { + mHighlightedObjects.removeNode(remove_these_nodes[i]); + } + + // remove all root objects already being highlighted + for (i = 0; i < remove_these_roots.count(); i++) + { + roots.erase(remove_these_roots[i]); + } + + // add all new objects in rectangle selection + for (std::set::iterator iter = roots.begin(); + iter != roots.end(); iter++) + { + LLViewerObject* objectp = *iter; + LLSelectNode* rect_select_node = new LLSelectNode(objectp, TRUE); + rect_select_node->selectAllTEs(TRUE); + + if (!canSelectObject(objectp)) + { + continue; + } + + mHighlightedObjects.addNode(rect_select_node); + + if (!select_linked_set) + { + rect_select_node->mIndividualSelection = TRUE; + } + else + { + for (U32 i = 0; i < objectp->mChildList.size(); i++) + { + LLViewerObject* child_objectp = objectp->mChildList[i]; + + if (!canSelectObject(child_objectp)) + { + continue; + } + + rect_select_node = new LLSelectNode(objectp->mChildList[i], TRUE); + rect_select_node->selectAllTEs(TRUE); + mHighlightedObjects.addNode(rect_select_node); + } + } + } + + num_sils_genned = 0; + + // render silhouettes for highlighted objects + //BOOL subtracting_from_selection = (gKeyboard->currentMask(TRUE) == MASK_CONTROL); + for (S32 pass = 0; pass < 2; pass++) + { + for (node = mHighlightedObjects.getFirstNode(); node; node = mHighlightedObjects.getNextNode() ) + { + LLViewerObject* objectp = node->getObject(); + + // do roots first, then children so that root flags are cleared ASAP + BOOL roots_only = (pass == 0); + BOOL is_root = objectp->isRootEdit(); + if (roots_only != is_root) + { + continue; + } + + if (!node->mSilhouetteExists + || objectp->isChanged(LLXform::SILHOUETTE) + || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE))) + { + if (num_sils_genned++ < MAX_SILS_PER_FRAME) + { + generateSilhouette(node, gCamera->getOrigin()); + changed_objects.put(objectp); + } + else if (objectp->isAttachment() && objectp->getRootEdit()->mDrawable.notNull()) + { + //RN: hack for orthogonal projection of HUD attachments + LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent(); + if (attachment_pt && attachment_pt->getIsHUDAttachment()) + { + LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f); + generateSilhouette(node, camera_pos); + } + } + } + //LLColor4 highlight_color; + // + //if (subtracting_from_selection) + //{ + // node->renderOneSilhouette(LLColor4::red); + //} + //else if (!objectp->isSelected()) + //{ + // highlight_color = objectp->isRoot() ? sHighlightParentColor : sHighlightChildColor; + // node->renderOneSilhouette(highlight_color); + //} + } + } + //mSilhouetteImagep->unbindTexture(0, GL_TEXTURE_2D); + } + else + { + mHighlightedObjects.deleteAllNodes(); + } + + for (S32 i = 0; i < changed_objects.count(); i++) + { + // clear flags after traversing node list (as child objects need to refer to parent flags, etc) + changed_objects[i]->clearChanged(LLXform::MOVED | LLXform::SILHOUETTE); + } + + //glAlphaFunc(GL_GREATER, 0.01f); +} + +void LLSelectMgr::renderSilhouettes(BOOL for_hud) +{ + if (!mRenderSilhouettes) + { + return; + } + + LLSelectNode *node; + LLViewerImage::bindTexture(gSelectMgr->mSilhouetteImagep); + LLGLSPipelineSelection gls_select; + glAlphaFunc(GL_GREATER, 0.0f); + LLGLEnable blend(GL_BLEND); + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); + + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if (for_hud && avatar) + { + LLBBox hud_bbox = avatar->getHUDBBox(); + + F32 cur_zoom = avatar->mHUDCurZoom; + + // set up transform to encompass bounding box of HUD + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f); + glOrtho(-0.5f * gCamera->getAspect(), 0.5f * gCamera->getAspect(), -0.5f, 0.5f, 0.f, depth); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame + glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f); + glScalef(cur_zoom, cur_zoom, cur_zoom); + } + if (mSelectedObjects.getNumNodes()) + { + glPushAttrib(GL_FOG_BIT); + LLUUID inspect_item_id = LLFloaterInspect::getSelectedUUID(); + for (S32 pass = 0; pass < 2; pass++) + { + for (node = mSelectedObjects.getFirstNode(); node; node = mSelectedObjects.getNextNode() ) + { + LLViewerObject* objectp = node->getObject(); + if (objectp->isHUDAttachment() != for_hud) + { + continue; + } + if(objectp->getID() == inspect_item_id) + { + node->renderOneSilhouette(sHighlightInspectColor); + } + else if (node->isTransient()) + { + BOOL oldHidden = LLSelectMgr::sRenderHiddenSelections; + LLSelectMgr::sRenderHiddenSelections = FALSE; + node->renderOneSilhouette(sContextSilhouetteColor); + LLSelectMgr::sRenderHiddenSelections = oldHidden; + } + else if (objectp->isRootEdit()) + { + node->renderOneSilhouette(sSilhouetteParentColor); + } + else + { + node->renderOneSilhouette(sSilhouetteChildColor); + } + } + } + glPopAttrib(); + } + + if (mHighlightedObjects.getNumNodes()) + { + // render silhouettes for highlighted objects + BOOL subtracting_from_selection = (gKeyboard->currentMask(TRUE) == MASK_CONTROL); + for (S32 pass = 0; pass < 2; pass++) + { + for (node = mHighlightedObjects.getFirstNode(); node; node = mHighlightedObjects.getNextNode() ) + { + LLViewerObject* objectp = node->getObject(); + if (objectp->isHUDAttachment() != for_hud) + { + continue; + } + + if (subtracting_from_selection) + { + node->renderOneSilhouette(LLColor4::red); + } + else if (!objectp->isSelected()) + { + LLColor4 highlight_color = objectp->isRoot() ? sHighlightParentColor : sHighlightChildColor; + node->renderOneSilhouette(highlight_color); + } + } + } + } + + if (for_hud && avatar) + { + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + stop_glerror(); + } + + gSelectMgr->mSilhouetteImagep->unbindTexture(0, GL_TEXTURE_2D); + glAlphaFunc(GL_GREATER, 0.01f); +} + +void LLSelectMgr::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point) +{ + LLViewerObject* objectp = nodep->getObject(); + + if (objectp && objectp->getPCode() == LL_PCODE_VOLUME) + { + ((LLVOVolume*)objectp)->generateSilhouette(nodep, view_point); + } +} + +void LLSelectMgr::getSilhouetteExtents(LLSelectNode* nodep, const LLQuaternion& orientation, LLVector3& min_extents, LLVector3& max_extents) +{ + LLViewerObject* objectp = nodep->getObject(); + + if (objectp->mDrawable.isNull()) + { + return; + } + + LLQuaternion test_rot = orientation * ~objectp->getRenderRotation(); + LLVector3 x_axis_rot = LLVector3::x_axis * test_rot; + LLVector3 y_axis_rot = LLVector3::y_axis * test_rot; + LLVector3 z_axis_rot = LLVector3::z_axis * test_rot; + + x_axis_rot.scaleVec(objectp->mDrawable->getScale()); + y_axis_rot.scaleVec(objectp->mDrawable->getScale()); + z_axis_rot.scaleVec(objectp->mDrawable->getScale()); + + generateSilhouette(nodep, objectp->mDrawable->getPositionAgent() + x_axis_rot * 100.f); + + S32 num_vertices = nodep->mSilhouetteVertices.size(); + if (num_vertices) + { + min_extents.mV[VY] = llmin(min_extents.mV[VY], nodep->mSilhouetteVertices[0] * y_axis_rot); + max_extents.mV[VY] = llmax(max_extents.mV[VY], nodep->mSilhouetteVertices[0] * y_axis_rot); + + min_extents.mV[VZ] = llmin(min_extents.mV[VZ], nodep->mSilhouetteVertices[0] * z_axis_rot); + max_extents.mV[VZ] = llmax(min_extents.mV[VZ], nodep->mSilhouetteVertices[0] * z_axis_rot); + + for (S32 vert = 1; vert < num_vertices; vert++) + { + F32 y_pos = nodep->mSilhouetteVertices[vert] * y_axis_rot; + F32 z_pos = nodep->mSilhouetteVertices[vert] * z_axis_rot; + min_extents.mV[VY] = llmin(y_pos, min_extents.mV[VY]); + max_extents.mV[VY] = llmax(y_pos, max_extents.mV[VY]); + min_extents.mV[VZ] = llmin(z_pos, min_extents.mV[VZ]); + max_extents.mV[VZ] = llmax(z_pos, max_extents.mV[VZ]); + } + } + + generateSilhouette(nodep, objectp->mDrawable->getPositionAgent() + y_axis_rot * 100.f); + + num_vertices = nodep->mSilhouetteVertices.size(); + if (num_vertices) + { + min_extents.mV[VX] = llmin(min_extents.mV[VX], nodep->mSilhouetteVertices[0] * x_axis_rot); + max_extents.mV[VX] = llmax(max_extents.mV[VX], nodep->mSilhouetteVertices[0] * x_axis_rot); + + for (S32 vert = 1; vert < num_vertices; vert++) + { + F32 x_pos = nodep->mSilhouetteVertices[vert] * x_axis_rot; + min_extents.mV[VX] = llmin(x_pos, min_extents.mV[VX]); + max_extents.mV[VX] = llmax(x_pos, max_extents.mV[VX]); + } + } + + generateSilhouette(nodep, gCamera->getOrigin()); +} + + +// +// Utility classes +// +LLSelectNode::LLSelectNode(LLViewerObject* object, BOOL glow) +{ + mObject = object; + selectAllTEs(FALSE); + mIndividualSelection = FALSE; + mTransient = FALSE; + mValid = FALSE; + mPermissions = new LLPermissions(); + mInventorySerial = 0; + mName = LLString::null; + mDescription = LLString::null; + mTouchName = LLString::null; + mSitName = LLString::null; + mSilhouetteExists = FALSE; + mDuplicated = FALSE; + + saveColors(); +} + +LLSelectNode::LLSelectNode(const LLSelectNode& nodep) +{ + S32 i; + for (i = 0; i < SELECT_MAX_TES; i++) + { + mTESelected[i] = nodep.mTESelected[i]; + } + mLastTESelected = nodep.mLastTESelected; + + mIndividualSelection = nodep.mIndividualSelection; + + mValid = nodep.mValid; + mTransient = nodep.mTransient; + mPermissions = new LLPermissions(*nodep.mPermissions); + mSaleInfo = nodep.mSaleInfo;; + mAggregatePerm = nodep.mAggregatePerm; + mAggregateTexturePerm = nodep.mAggregateTexturePerm; + mAggregateTexturePermOwner = nodep.mAggregateTexturePermOwner; + mName = nodep.mName; + mDescription = nodep.mDescription; + mCategory = nodep.mCategory; + mSavedPositionLocal = nodep.mSavedPositionLocal; + mSavedPositionGlobal = nodep.mSavedPositionGlobal; + mSavedScale = nodep.mSavedScale; + mSavedRotation = nodep.mSavedRotation; + mDuplicated = nodep.mDuplicated; + mDuplicatePos = nodep.mDuplicatePos; + mDuplicateRot = nodep.mDuplicateRot; + mItemID = nodep.mItemID; + mFolderID = nodep.mFolderID; + mFromTaskID = nodep.mFromTaskID; + mTouchName = nodep.mTouchName; + mSitName = nodep.mSitName; + + mSilhouetteVertices = nodep.mSilhouetteVertices; + mSilhouetteNormals = nodep.mSilhouetteNormals; + mSilhouetteSegments = nodep.mSilhouetteSegments; + mSilhouetteExists = nodep.mSilhouetteExists; + mObject = nodep.mObject; + + std::vector::const_iterator color_iter; + mSavedColors.clear(); + for (color_iter = nodep.mSavedColors.begin(); color_iter != nodep.mSavedColors.end(); ++color_iter) + { + mSavedColors.push_back(*color_iter); + } + + saveTextures(nodep.mSavedTextures); +} + +LLSelectNode::~LLSelectNode() +{ + delete mPermissions; + mPermissions = NULL; +} + +void LLSelectNode::selectAllTEs(BOOL b) +{ + for (S32 i = 0; i < SELECT_MAX_TES; i++) + { + mTESelected[i] = b; + } + mLastTESelected = 0; +} + +void LLSelectNode::selectTE(S32 te_index, BOOL selected) +{ + if (te_index < 0 || te_index >= SELECT_MAX_TES) + { + return; + } + mTESelected[te_index] = selected; + mLastTESelected = te_index; +} + +BOOL LLSelectNode::isTESelected(S32 te_index) +{ + if (te_index < 0 || te_index >= mObject->getNumTEs()) + { + return FALSE; + } + return mTESelected[te_index]; +} + +S32 LLSelectNode::getLastSelectedTE() +{ + if (!isTESelected(mLastTESelected)) + { + return -1; + } + return mLastTESelected; +} + +LLViewerObject *LLSelectNode::getObject() +{ + if (!mObject) + { + return NULL; + } + else if (mObject->isDead()) + { + mObject = NULL; + } + return mObject; +} + +void LLSelectNode::saveColors() +{ + if (mObject.notNull()) + { + mSavedColors.clear(); + for (S32 i = 0; i < mObject->getNumTEs(); i++) + { + const LLTextureEntry* tep = mObject->getTE(i); + mSavedColors.push_back(tep->getColor()); + } + } +} + +void LLSelectNode::saveTextures(const std::vector& textures) +{ + if (mObject.notNull()) + { + mSavedTextures.clear(); + + std::vector::const_iterator texture_it; + for (texture_it = textures.begin(); texture_it != textures.end(); ++texture_it) + { + mSavedTextures.push_back(*texture_it); + } + } +} + +void LLSelectNode::saveTextureScaleRatios() +{ + mTextureScaleRatios.clear(); + if (mObject.notNull()) + { + for (U8 i = 0; i < mObject->getNumTEs(); i++) + { + F32 s,t; + const LLTextureEntry* tep = mObject->getTE(i); + tep->getScale(&s,&t); + U32 s_axis, t_axis; + + gSelectMgr->getTESTAxes(mObject, i, &s_axis, &t_axis); + + LLVector3 v; + LLVector3 scale = mObject->getScale(); + + if (tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR) + { + v.mV[s_axis] = s*scale.mV[s_axis]; + v.mV[t_axis] = t*scale.mV[t_axis]; + } + else + { + v.mV[s_axis] = s/scale.mV[s_axis]; + v.mV[t_axis] = t/scale.mV[t_axis]; + } + + mTextureScaleRatios.push_back(v); + } + } +} + + +// This implementation should be similar to LLTask::allowOperationOnTask +BOOL LLSelectNode::allowOperationOnNode(PermissionBit op, U64 group_proxy_power) const +{ + // Extract ownership. + BOOL object_is_group_owned = FALSE; + LLUUID object_owner_id; + mPermissions->getOwnership(object_owner_id, object_is_group_owned); + + // Operations on invalid or public objects is not allowed. + if (!mObject || (mObject->isDead()) || !mPermissions->isOwned()) + { + return FALSE; + } + + // The transfer permissions can never be given through proxy. + if (PERM_TRANSFER == op) + { + // The owner of an agent-owned object can transfer to themselves. + if ( !object_is_group_owned + && (gAgent.getID() == object_owner_id) ) + { + return TRUE; + } + else + { + // Otherwise check aggregate permissions. + return mObject->permTransfer(); + } + } + + if (PERM_MOVE == op + || PERM_MODIFY == op) + { + // only owners can move or modify their attachments + // no proxy allowed. + if (mObject->isAttachment() && object_owner_id != gAgent.getID()) + { + return FALSE; + } + } + + // Calculate proxy_agent_id and group_id to use for permissions checks. + // proxy_agent_id may be set to the object owner through group powers. + // group_id can only be set to the object's group, if the agent is in that group. + LLUUID group_id = LLUUID::null; + LLUUID proxy_agent_id = gAgent.getID(); + + // Gods can always operate. + if (gAgent.isGodlike()) + { + return TRUE; + } + + // Check if the agent is in the same group as the object. + LLUUID object_group_id = mPermissions->getGroup(); + if (object_group_id.notNull() && + gAgent.isInGroup(object_group_id)) + { + // Assume the object's group during this operation. + group_id = object_group_id; + } + + // Only allow proxy powers for PERM_COPY if the actual agent can + // receive the item (ie has PERM_TRANSFER permissions). + // NOTE: op == PERM_TRANSFER has already been handled, but if + // that ever changes we need to BLOCK proxy powers for PERM_TRANSFER. DK 03/28/06 + if (PERM_COPY != op || mPermissions->allowTransferTo(gAgent.getID())) + { + // Check if the agent can assume ownership through group proxy or agent-granted proxy. + if ( ( object_is_group_owned + && gAgent.hasPowerInGroup(object_owner_id, group_proxy_power)) + // Only allow proxy for move, modify, and copy. + || ( (PERM_MOVE == op || PERM_MODIFY == op || PERM_COPY == op) + && (!object_is_group_owned + && gAgent.isGrantedProxy(*mPermissions)))) + { + // This agent is able to assume the ownership role for this operation. + proxy_agent_id = object_owner_id; + } + } + + // We now have max ownership information. + if (PERM_OWNER == op) + { + // This this was just a check for ownership, we can now return the answer. + return (proxy_agent_id == object_owner_id ? TRUE : FALSE); + } + + // check permissions to see if the agent can operate + return (mPermissions->allowOperationBy(op, proxy_agent_id, group_id)); +} + +//----------------------------------------------------------------------------- +// renderOneSilhouette() +//----------------------------------------------------------------------------- +void LLSelectNode::renderOneSilhouette(const LLColor4 &color) +{ + LLViewerObject* objectp = getObject(); + if (!objectp) + { + return; + } + + LLDrawable* drawable = objectp->mDrawable; + if(!drawable) + { + return; + } + + if (!mSilhouetteExists) + { + return; + } + + BOOL is_hud_object = objectp->isHUDAttachment(); + + if (!drawable->isVisible() && !is_hud_object) + { + return; + } + + if (mSilhouetteVertices.size() == 0 || mSilhouetteNormals.size() != mSilhouetteVertices.size()) + { + return; + } + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if (!is_hud_object) + { + glLoadIdentity(); + glMultMatrixd(gGLModelView); + } + + + if (drawable->isActive()) + { + glMultMatrixf((F32*) objectp->getRenderMatrix().mMatrix); + } + + LLVolume *volume = objectp->getVolume(); + if (volume) + { + F32 silhouette_thickness; + if (is_hud_object && gAgent.getAvatarObject()) + { + silhouette_thickness = LLSelectMgr::sHighlightThickness / gAgent.getAvatarObject()->mHUDCurZoom; + } + else + { + LLVector3 view_vector = gCamera->getOrigin() - objectp->getRenderPosition(); + silhouette_thickness = drawable->mDistanceWRTCamera * LLSelectMgr::sHighlightThickness * (gCamera->getView() / gCamera->getDefaultFOV()); + } + F32 animationTime = (F32)LLFrameTimer::getElapsedSeconds(); + + F32 u_coord = fmod(animationTime * LLSelectMgr::sHighlightUAnim, 1.f); + F32 v_coord = 1.f - fmod(animationTime * LLSelectMgr::sHighlightVAnim, 1.f); + F32 u_divisor = 1.f / ((F32)(mSilhouetteVertices.size() - 1)); + + if (LLSelectMgr::sRenderHiddenSelections) // && gFloaterTools && gFloaterTools->getVisible()) + { + glBlendFunc(GL_SRC_COLOR, GL_ONE); + LLGLEnable fog(GL_FOG); + glFogi(GL_FOG_MODE, GL_LINEAR); + float d = (gCamera->getPointOfInterest()-gCamera->getOrigin()).magVec(); + LLColor4 fogCol = color * (F32)llclamp((gSelectMgr->getSelectionCenterGlobal()-gAgent.getCameraPositionGlobal()).magVec()/(gSelectMgr->getBBoxOfSelection().getExtentLocal().magVec()*4), 0.0, 1.0); + glFogf(GL_FOG_START, d); + glFogf(GL_FOG_END, d*(1 + (gCamera->getView() / gCamera->getDefaultFOV()))); + glFogfv(GL_FOG_COLOR, fogCol.mV); + + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL); + glAlphaFunc(GL_GREATER, 0.01f); + glBegin(GL_LINES); + { + S32 i = 0; + for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++) + { +// S32 first_i = i; + for(; i < mSilhouetteSegments[seg_num]; i++) + { + u_coord += u_divisor * LLSelectMgr::sHighlightUScale; + + glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); + glTexCoord2f( u_coord, v_coord ); + glVertex3fv( mSilhouetteVertices[i].mV ); + } + + /*u_coord += u_divisor * LLSelectMgr::sHighlightUScale; + glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f); + glTexCoord2f( u_coord, v_coord ); + glVertex3fv( mSilhouetteVertices[first_i].mV );*/ + } + } + glEnd(); + u_coord = fmod(animationTime * LLSelectMgr::sHighlightUAnim, 1.f); + } + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glAlphaFunc(GL_GREATER, LLSelectMgr::sHighlightAlphaTest); + glBegin(GL_TRIANGLES); + { + S32 i = 0; + for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++) + { + S32 first_i = i; + LLVector3 v; + LLVector2 t; + + for(; i < mSilhouetteSegments[seg_num]; i++) + { + + if (i == first_i) { + LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness; + vert += mSilhouetteVertices[i]; + + glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha); + glTexCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale ); + glVertex3fv( vert.mV ); + + u_coord += u_divisor * LLSelectMgr::sHighlightUScale; + + glColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); + glTexCoord2f( u_coord, v_coord ); + glVertex3fv( mSilhouetteVertices[i].mV ); + + v = mSilhouetteVertices[i]; + t = LLVector2(u_coord, v_coord); + } + else { + LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness; + vert += mSilhouetteVertices[i]; + + glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha); + glTexCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale ); + glVertex3fv( vert.mV ); + glVertex3fv( vert.mV ); + + glTexCoord2fv(t.mV); + u_coord += u_divisor * LLSelectMgr::sHighlightUScale; + glColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2); + glVertex3fv(v.mV); + glTexCoord2f( u_coord, v_coord ); + glVertex3fv( mSilhouetteVertices[i].mV ); + + } + } + } + } + glEnd(); + + } + glPopMatrix(); +} + +// +// Utility Functions +// + +// Update everyone who cares about the selection list +void dialog_refresh_all() +{ + if (gNoRender) + { + return; + } + + //could refresh selected object info in toolbar here + + gFloaterTools->dirty(); + + if( gPieObject->getVisible() ) + { + gPieObject->arrange(); + } + + LLFloaterProperties::dirtyAll(); + LLFloaterInspect::dirty(); +} + +S32 get_family_count(LLViewerObject *parent) +{ + if (!parent) + { + llwarns << "Trying to get_family_count on null parent!" << llendl; + } + S32 count = 1; // for this object + for (U32 i = 0; i < parent->mChildList.size(); i++) + { + LLViewerObject* child = parent->mChildList[i]; + + if (!child) + { + llwarns << "Family object has NULL child! Show Doug." << llendl; + } + else if (child->isDead()) + { + llwarns << "Family object has dead child object. Show Doug." << llendl; + } + else + { + if (gSelectMgr->canSelectObject(child)) + { + count += get_family_count( child ); + } + } + } + return count; +} + +//----------------------------------------------------------------------------- +// updateSelectionCenter +//----------------------------------------------------------------------------- +void LLSelectMgr::updateSelectionCenter() +{ + const F32 MOVE_SELECTION_THRESHOLD = 1.f; // Movement threshold in meters for updating selection + // center (tractor beam) + + LLViewerObject* object = mSelectedObjects.getFirstObject(); + if (!object) + { + // nothing selected, probably grabbing + // Ignore by setting to avatar origin. + mSelectionCenterGlobal.clearVec(); + mShowSelection = FALSE; + mSelectionBBox = LLBBox(); + mPauseRequest = NULL; + if (gAgent.getAvatarObject()) + { + gAgent.getAvatarObject()->mHUDTargetZoom = 1.f; + gAgent.getAvatarObject()->mHUDCurZoom = 1.f; + } + } + else + { + mSelectType = getSelectTypeForObject(object); + + if (mSelectType == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject()) + { + mPauseRequest = gAgent.getAvatarObject()->requestPause(); + } + else + { + mPauseRequest = NULL; + } + + if (mSelectType != SELECT_TYPE_HUD && gAgent.getAvatarObject()) + { + // reset hud ZOOM + gAgent.getAvatarObject()->mHUDTargetZoom = 1.f; + gAgent.getAvatarObject()->mHUDCurZoom = 1.f; + } + + mShowSelection = FALSE; + LLBBox bbox; + + // have stuff selected + LLVector3d select_center; + // keep a list of jointed objects for showing the joint HUDEffects + gHUDManager->clearJoints(); + LLDynamicArray < LLViewerObject *> jointed_objects; + + for (object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() ) + { + LLViewerObject *myAvatar = gAgent.getAvatarObject(); + LLViewerObject *root = object->getRootEdit(); + if (mSelectType == SELECT_TYPE_WORLD && // not an attachment + !root->isChild(myAvatar) && // not the object you're sitting on + !object->isAvatar()) // not another avatar + { + mShowSelection = TRUE; + } + + bbox.addBBoxAgent( object->getBoundingBoxAgent() ); + + if (object->isJointChild()) + { + jointed_objects.put(object); + } + } // end for + + LLVector3 bbox_center_agent = bbox.getCenterAgent(); + mSelectionCenterGlobal = gAgent.getPosGlobalFromAgent(bbox_center_agent); + mSelectionBBox = bbox; + + if (jointed_objects.count()) + { + gHUDManager->showJoints(&jointed_objects); + } + } + + if ( !(gAgentID == LLUUID::null) ) + { + LLTool *tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) ); + if (mShowSelection) + { + LLVector3d select_center_global; + + if( tool->isEditing() ) + { + select_center_global = tool->getEditingPointGlobal(); + } + else + { + select_center_global = mSelectionCenterGlobal; + } + + // Send selection center if moved beyond threshold (used to animate tractor beam) + LLVector3d diff; + diff = select_center_global - mLastSentSelectionCenterGlobal; + + if ( diff.magVecSquared() > MOVE_SELECTION_THRESHOLD*MOVE_SELECTION_THRESHOLD ) + { + // Transmit updated selection center + mLastSentSelectionCenterGlobal = select_center_global; + } + } + } + + // give up edit menu if no objects selected + if (gEditMenuHandler == this && getObjectCount() == 0) + { + gEditMenuHandler = NULL; + } +} + +void LLSelectMgr::updatePointAt() +{ + if (mShowSelection) + { + if (getObjectCount()) + { + LLVector3 select_offset; + LLViewerObject *click_object = gObjectList.findObject(gLastHitObjectID); + if (click_object && click_object->isSelected()) + { + // clicked on another object in our selection group, use that as target + select_offset.setVec(gLastHitObjectOffset); + select_offset.rotVec(~click_object->getRenderRotation()); + + gAgent.setPointAt(POINTAT_TARGET_SELECT, click_object, select_offset); + gAgent.setLookAt(LOOKAT_TARGET_SELECT, click_object, select_offset); + } + else + { + // didn't click on an object this time, revert to pointing at center of first object + gAgent.setPointAt(POINTAT_TARGET_SELECT, getFirstObject()); + gAgent.setLookAt(LOOKAT_TARGET_SELECT, getFirstObject()); + } + } + else + { + gAgent.setPointAt(POINTAT_TARGET_CLEAR); + gAgent.setLookAt(LOOKAT_TARGET_CLEAR); + } + } + else + { + gAgent.setPointAt(POINTAT_TARGET_CLEAR); + gAgent.setLookAt(LOOKAT_TARGET_CLEAR); + } +} + +//----------------------------------------------------------------------------- +// getBBoxOfSelection() +//----------------------------------------------------------------------------- +LLBBox LLSelectMgr::getBBoxOfSelection() const +{ + return mSelectionBBox; +} + + +//----------------------------------------------------------------------------- +// canUndo() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::canUndo() +{ + return getFirstEditableObject() != NULL; +} + +//----------------------------------------------------------------------------- +// undo() +//----------------------------------------------------------------------------- +void LLSelectMgr::undo() +{ + BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet"); + LLUUID group_id(gAgent.getGroupID()); + sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); +} + +//----------------------------------------------------------------------------- +// canRedo() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::canRedo() +{ + return getFirstEditableObject() != NULL; +} + +//----------------------------------------------------------------------------- +// redo() +//----------------------------------------------------------------------------- +void LLSelectMgr::redo() +{ + BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet"); + LLUUID group_id(gAgent.getGroupID()); + sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST); +} + +//----------------------------------------------------------------------------- +// canDoDelete() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::canDoDelete() +{ + return getFirstDeleteableObject() != NULL; +} + +//----------------------------------------------------------------------------- +// doDelete() +//----------------------------------------------------------------------------- +void LLSelectMgr::doDelete() +{ + selectDelete(); +} + +//----------------------------------------------------------------------------- +// canDeselect() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::canDeselect() +{ + return !isEmpty(); +} + +//----------------------------------------------------------------------------- +// deselect() +//----------------------------------------------------------------------------- +void LLSelectMgr::deselect() +{ + deselectAll(); +} +//----------------------------------------------------------------------------- +// canDuplicate() +//----------------------------------------------------------------------------- +BOOL LLSelectMgr::canDuplicate() +{ + return getFirstCopyableObject() != NULL; +} +//----------------------------------------------------------------------------- +// duplicate() +//----------------------------------------------------------------------------- +void LLSelectMgr::duplicate() +{ + LLVector3 offset(0.5f, 0.5f, 0.f); + selectDuplicate(offset, TRUE); +} +//----------------------------------------------------------------------------- +// undoRedo() +//----------------------------------------------------------------------------- +U32 LLSelectMgr::undoRedo(std::deque &queue_src, std::deque &queue_dst, const LLUUID &object_id) +{ + if (queue_src.size() == 0) + { + return 0; + } + + U32 update_type = 0; + std::deque temp_queue; + LLSelectAction* src_actionp = queue_src.back(); + + while (src_actionp->mObjectID != object_id) + { + temp_queue.push_back(src_actionp); + queue_src.pop_back(); + if (!queue_src.size()) + { + // put everything back + LLSelectAction* actionp; + while (temp_queue.size()) + { + actionp = temp_queue.back(); + temp_queue.pop_back(); + queue_src.push_back(actionp); + } + return 0; + } + src_actionp = queue_src.back(); + } + + if(src_actionp) + { + LLSelectAction* dst_actionp = new LLSelectAction(); + dst_actionp->mActionType = src_actionp->mActionType; + dst_actionp->mObjectID = src_actionp->mObjectID; + dst_actionp->mIndividualSelection = src_actionp->mIndividualSelection; + + LLViewerObject* object = gObjectList.findObject(src_actionp->mObjectID); + if (object && object->mDrawable.notNull()) + { + LLVector3 old_position_local = object->getPosition(); + + switch(src_actionp->mActionType) + { + case SELECT_ACTION_TYPE_MOVE: + dst_actionp->mPosition = object->mDrawable->getPosition(); + object->setPosition(src_actionp->mPosition, TRUE); + if (object->isRootEdit() && src_actionp->mIndividualSelection) + { + // counter-translate children + LLVector3 parent_offset = (src_actionp->mPosition - old_position_local) * ~object->getRotation(); + + // counter-translate child objects if we are moving the root as an individual + for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++) + { + LLViewerObject* childp = object->mChildList[child_num]; + childp->setPosition(childp->getPosition() - parent_offset); + } + } + update_type |= UPD_POSITION; + break; + case SELECT_ACTION_TYPE_ROTATE: + dst_actionp->mPosition = object->mDrawable->getPosition(); + dst_actionp->mRotation = object->mDrawable->getRotation(); + object->setRotation(src_actionp->mRotation, TRUE); + object->setPosition(src_actionp->mPosition, TRUE); + if (object->isRootEdit() && src_actionp->mIndividualSelection) + { + // counter-translate and rotate children + LLVector3 parent_offset = (src_actionp->mPosition - old_position_local) * ~object->getRotation(); + + for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++) + { + LLViewerObject* childp = object->mChildList[child_num]; + LLQuaternion delta_rot_inv = dst_actionp->mRotation * ~src_actionp->mRotation; + childp->setPosition((childp->getPosition() * delta_rot_inv) - parent_offset); + childp->setRotation(childp->getRotation() * delta_rot_inv ); + } + } + update_type |= UPD_ROTATION | UPD_POSITION; + break; + case SELECT_ACTION_TYPE_SCALE: + dst_actionp->mPosition = object->mDrawable->getPosition(); + dst_actionp->mScale = object->getScale(); + object->setScale(src_actionp->mScale, TRUE); + object->setPosition(src_actionp->mPosition, TRUE); + update_type |= UPD_SCALE | UPD_POSITION; + break; + default: + // do nothing + break; + } + } + queue_src.pop_back(); + delete src_actionp; + queue_dst.push_back(dst_actionp); + while (queue_dst.size() > (U32)MAX_ACTION_QUEUE_SIZE) + { + LLSelectAction* action = queue_dst.front(); + queue_dst.pop_front(); + delete action; + } + + } + + // put everything back + LLSelectAction* actionp; + while (temp_queue.size()) + { + actionp = temp_queue.back(); + temp_queue.pop_back(); + queue_src.push_back(actionp); + } + + return update_type; +} + +ESelectType LLSelectMgr::getSelectTypeForObject(LLViewerObject* object) +{ + if (!object) + { + return SELECT_TYPE_WORLD; + } + if (object->isHUDAttachment()) + { + return SELECT_TYPE_HUD; + } + else if (object->isAttachment()) + { + return SELECT_TYPE_ATTACHMENT; + } + else + { + return SELECT_TYPE_WORLD; + } +} + +void LLSelectMgr::validateSelection() +{ + LLViewerObject* objectp; + for (objectp = getFirstObject(); objectp; objectp = getNextObject()) + { + if (!canSelectObject(objectp)) + { + deselectObjectOnly(objectp); + } + } +} + +BOOL LLSelectMgr::canSelectObject(LLViewerObject* object) +{ + if (mForceSelection) + { + return TRUE; + } + + if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !object->permYouOwner()) || + (gSavedSettings.getBOOL("SelectMovableOnly") && !object->permMove())) + { + // only select my own objects + return FALSE; + } + + // Can't select dead objects + if (object->isDead()) return FALSE; + + // Can't select orphans + if (object->isOrphaned()) return FALSE; + + // Can't select avatars + if (object->isAvatar()) return FALSE; + + // Can't select land + if (object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) return FALSE; + + ESelectType selection_type = getSelectTypeForObject(object); + if (getObjectCount() > 0 && mSelectType != selection_type) return FALSE; + + return TRUE; +} + +LLSelectNodeList::LLSelectNodeList() : std::list() +{ + mCurrentTE = -1; + mCurrentNode = end(); +} + +LLSelectNodeList::~LLSelectNodeList() +{ + std::for_each(begin(), end(), DeletePointer()); +} + +void LLSelectNodeList::updateEffects() +{ +} + +S32 LLSelectNodeList::getNumNodes() +{ + return size(); +} + +void LLSelectNodeList::addNode(LLSelectNode *nodep) +{ + push_front(nodep); + mSelectNodeMap[nodep->getObject()] = nodep; +} + +void LLSelectNodeList::addNodeAtEnd(LLSelectNode *nodep) +{ + push_back(nodep); + mSelectNodeMap[nodep->getObject()] = nodep; +} + +void LLSelectNodeList::removeNode(LLSelectNode *nodep) +{ + std::list::iterator iter; + for (iter = begin(); iter != end(); ++iter) + { + if ((*iter) == nodep) + { + mSelectNodeMap.erase(nodep->getObject()); + erase(iter++); + } + } +} + +void LLSelectNodeList::deleteAllNodes() +{ + std::for_each(begin(), end(), DeletePointer()); + clear(); + mSelectNodeMap.clear(); +} + +LLSelectNode* LLSelectNodeList::findNode(LLViewerObject* objectp) +{ + std::map::iterator found_it = mSelectNodeMap.find(objectp); + if (found_it != mSelectNodeMap.end()) + { + return found_it->second; + } + return NULL; +} + +//----------------------------------------------------------------------------- +// getFirstNode() +//----------------------------------------------------------------------------- +LLSelectNode *LLSelectNodeList::getFirstNode() +{ + mCurrentNode = begin();//getFirstData(); + + while (mCurrentNode != end() && !(*mCurrentNode)->getObject()) + { + // The object on this was killed at some point, delete it. + erase(mCurrentNode++); + } + + if (mCurrentNode != end()) + { + return *mCurrentNode; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// getCurrentNode() +//----------------------------------------------------------------------------- +LLSelectNode *LLSelectNodeList::getCurrentNode() +{ + while (mCurrentNode != end() && !(*mCurrentNode)->getObject()) + { + // The object on this was killed at some point, delete it. + erase(mCurrentNode++); + } + + if (mCurrentNode != end()) + { + return *mCurrentNode; + } + return NULL; +} + +//----------------------------------------------------------------------------- +// getNextNode() +//----------------------------------------------------------------------------- +LLSelectNode *LLSelectNodeList::getNextNode() +{ + ++mCurrentNode; + + while (mCurrentNode != end() && !(*mCurrentNode)->getObject()) + { + // The object on this was killed at some point, delete it. + erase(mCurrentNode++); + } + + if (mCurrentNode != end()) + { + return *mCurrentNode; + } + return NULL; +} + + + +//----------------------------------------------------------------------------- +// getFirstObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLSelectNodeList::getFirstObject() +{ + mCurrentNode = begin(); + + while (mCurrentNode != end() && !(*mCurrentNode)->getObject()) + { + // The object on this was killed at some point, delete it. + erase(mCurrentNode++); + } + + if (mCurrentNode != end()) + { + return (*mCurrentNode)->getObject(); + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// getNextObject() +//----------------------------------------------------------------------------- +LLViewerObject* LLSelectNodeList::getNextObject() +{ + ++mCurrentNode;// = getNextData(); + + while (mCurrentNode != end() && !(*mCurrentNode)->getObject()) + { + // The object on this was killed at some point, delete it. + erase(mCurrentNode++); + } + + if (mCurrentNode != end()) + { + return (*mCurrentNode)->getObject(); + } + + return NULL; +} + + + +//----------------------------------------------------------------------------- +// getPrimaryTE() +//----------------------------------------------------------------------------- +void LLSelectNodeList::getPrimaryTE(LLViewerObject* *object, S32 *te) +{ + // initialize object and te + *te = 0; + *object = NULL; + + BOOL searching_roots = TRUE; + + // try for root node first, then first child node + LLSelectNode *primary_node = getFirstNode(); //getFirstRootNode(); + if (!primary_node) + { + primary_node = getFirstNode(); + searching_roots = FALSE; + } + + while (primary_node) + { + S32 last_selected_te = primary_node->getLastSelectedTE(); + if (last_selected_te >= 0) + { + *object = primary_node->getObject(); + *te = last_selected_te; + return; + } + for(S32 cur_te = 0; cur_te < primary_node->getObject()->getNumTEs(); cur_te++) + { + // if face selected + if (primary_node->isTESelected(cur_te)) + { + // return this object and face + *object = primary_node->getObject(); + *te = cur_te; + return; + } + } + if (searching_roots) + { + primary_node = getNextRootNode(); + if (!primary_node) + { + primary_node = getFirstNode(); + searching_roots = FALSE; + } + } + else + { + primary_node = getNextNode(); + } + } +} + +//----------------------------------------------------------------------------- +// getFirstTE() +//----------------------------------------------------------------------------- +void LLSelectNodeList::getFirstTE(LLViewerObject* *object, S32 *te) +{ + // start with first face + mCurrentTE = 0; + + LLSelectNode *cur_node = getFirstNode(); + + // repeat over all selection nodes + while (cur_node) + { + // skip objects with no faces + if (cur_node->getObject()->getNumTEs() == 0) + { + mCurrentTE = 0; + cur_node = getNextNode(); + continue; + } + + // repeat over all faces for this object + while (mCurrentTE < cur_node->getObject()->getNumTEs()) + { + // if face selected + if (cur_node->isTESelected(mCurrentTE)) + { + // return this object and face + *object = cur_node->getObject(); + *te = mCurrentTE; + return; + } + + mCurrentTE++; + } + + // Couldn't find a selected face. + // This can happen if an object's volume parameters are changed in such a way + // that texture entries are eliminated. + // + // TODO: Consider selecting all faces in this case? Subscribe the selection + // list to the volume changing code? + + mCurrentTE = 0; + cur_node = getNextNode(); + } + + // The list doesn't contain any nodes. Return NULL. + *object = NULL; + *te = -1; + return; +} + + +//----------------------------------------------------------------------------- +// getNextFace() +//----------------------------------------------------------------------------- +void LLSelectNodeList::getNextTE(LLViewerObject* *object, S32 *te) +{ + // try next face + mCurrentTE++; + + LLSelectNode *cur_node = getCurrentNode(); + // repeat over remaining selection nodes + while ( cur_node ) + { + // skip objects with no faces + if (cur_node->getObject()->getNumTEs() == 0) + { + mCurrentTE = 0; + cur_node = getNextNode(); + continue; + } + + // repeat over all faces for this object + // CRO: getNumTEs() no longer equals mFaces.count(), so use mFaces.count() instead + while ( mCurrentTE < cur_node->getObject()->getNumTEs() ) + { + // if face selected + if (cur_node->isTESelected(mCurrentTE)) + { + // return this object and face + *object = cur_node->getObject(); + *te = mCurrentTE; + return; + } + + mCurrentTE++; + } + + mCurrentTE = 0; + cur_node = getNextNode(); + } + + // The list doesn't contain any nodes. Return NULL. + *object = NULL; + *te = -1; + return; +} + +void LLSelectNodeList::getCurrentTE(LLViewerObject* *object, S32 *te) +{ + if (mCurrentNode != end()) + { + *object = (*mCurrentNode)->getObject(); + *te = mCurrentTE; + } + else + { + *object = NULL; + *te = -1; + } +} +//----------------------------------------------------------------------------- +// getFirstRootNode() +//----------------------------------------------------------------------------- +LLSelectNode *LLSelectNodeList::getFirstRootNode() +{ + LLSelectNode *cur_node = getFirstNode(); + + // scan through child objects and roots set to ignore + while (cur_node && + (!(cur_node->getObject()->isRootEdit() || cur_node->getObject()->isJointChild()) || + cur_node->mIndividualSelection)) + { + cur_node = getNextNode(); + } + + return cur_node; +} + + +//----------------------------------------------------------------------------- +// getNextRootNode() +//----------------------------------------------------------------------------- +LLSelectNode *LLSelectNodeList::getNextRootNode() +{ + LLSelectNode *cur_node = getNextNode(); + + while (cur_node && + (!(cur_node->getObject()->isRootEdit() || cur_node->getObject()->isJointChild()) || + cur_node->mIndividualSelection)) + { + cur_node = getNextNode(); + } + + return cur_node; +} + + +//----------------------------------------------------------------------------- +// getFirstRootObject() +//----------------------------------------------------------------------------- +LLViewerObject *LLSelectNodeList::getFirstRootObject() +{ + LLSelectNode *node = getFirstRootNode(); + + if (node) + { + return node->getObject(); + } + else + { + return NULL; + } +} + + +//----------------------------------------------------------------------------- +// getNextRootObject() +//----------------------------------------------------------------------------- +LLViewerObject *LLSelectNodeList::getNextRootObject() +{ + LLSelectNode *node = getNextRootNode(); + + if (node) + { + return node->getObject(); + } + else + { + return NULL; + } +} + +//----------------------------------------------------------------------------- +// isEmpty() +//----------------------------------------------------------------------------- +BOOL LLSelectNodeList::isEmpty() +{ + return (size() == 0); +} + +//----------------------------------------------------------------------------- +// getOwnershipCost() +//----------------------------------------------------------------------------- +BOOL LLSelectNodeList::getOwnershipCost(S32 &cost) +{ + S32 count = 0; + for( LLSelectNode* nodep = getFirstNode(); nodep; nodep = getNextNode() ) + { + count++; + } + + cost = count * OWNERSHIP_COST_PER_OBJECT; + + return (count > 0); +} -- cgit v1.1