/** * @file llpanelobject.cpp * @brief Object editing (position, scale, etc.) in the tools floater * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" // file include #include "llpanelobject.h" // linden library includes #include "lleconomy.h" #include "llerror.h" #include "llfontgl.h" #include "material_codes.h" // LL_MCODE_MASK #include "llpermissionsflags.h" #include "llstring.h" #include "llvolume.h" #include "m3math.h" // project includes #include "llagent.h" #include "llbutton.h" #include "llcalc.h" #include "llcheckboxctrl.h" #include "llcolorswatch.h" #include "llcombobox.h" #include "llfocusmgr.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "llmanipscale.h" #include "llmenubutton.h" #include "llpreviewscript.h" #include "llresmgr.h" #include "llselectmgr.h" #include "llspinctrl.h" #include "lltexturectrl.h" #include "lltextbox.h" #include "lltool.h" #include "lltoolcomp.h" #include "lltoolmgr.h" #include "llui.h" #include "llviewerobject.h" #include "llviewerregion.h" #include "llviewerwindow.h" #include "llvovolume.h" #include "llworld.h" #include "pipeline.h" #include "llviewercontrol.h" #include "lluictrlfactory.h" //#include "llfirstuse.h" #include "lldrawpool.h" // // Constants // enum { MI_BOX, MI_CYLINDER, MI_PRISM, MI_SPHERE, MI_TORUS, MI_TUBE, MI_RING, MI_SCULPT, MI_NONE, MI_VOLUME_COUNT }; enum { MI_HOLE_SAME, MI_HOLE_CIRCLE, MI_HOLE_SQUARE, MI_HOLE_TRIANGLE, MI_HOLE_COUNT }; //static const std::string LEGACY_FULLBRIGHT_DESC =LLTrans::getString("Fullbright"); BOOL LLPanelObject::postBuild() { setMouseOpaque(FALSE); //-------------------------------------------------------- // Top //-------------------------------------------------------- // Lock checkbox mCheckLock = getChild("checkbox locked"); childSetCommitCallback("checkbox locked",onCommitLock,this); // Physical checkbox mCheckPhysics = getChild("Physical Checkbox Ctrl"); childSetCommitCallback("Physical Checkbox Ctrl",onCommitPhysics,this); // Temporary checkbox mCheckTemporary = getChild("Temporary Checkbox Ctrl"); childSetCommitCallback("Temporary Checkbox Ctrl",onCommitTemporary,this); // Phantom checkbox mCheckPhantom = getChild("Phantom Checkbox Ctrl"); childSetCommitCallback("Phantom Checkbox Ctrl",onCommitPhantom,this); // Position mLabelPosition = getChild("label position"); mCtrlPosX = getChild("Pos X"); childSetCommitCallback("Pos X",onCommitPosition,this); mCtrlPosY = getChild("Pos Y"); childSetCommitCallback("Pos Y",onCommitPosition,this); mCtrlPosZ = getChild("Pos Z"); childSetCommitCallback("Pos Z",onCommitPosition,this); // Scale mLabelSize = getChild("label size"); mCtrlScaleX = getChild("Scale X"); childSetCommitCallback("Scale X",onCommitScale,this); // Scale Y mCtrlScaleY = getChild("Scale Y"); childSetCommitCallback("Scale Y",onCommitScale,this); // Scale Z mCtrlScaleZ = getChild("Scale Z"); childSetCommitCallback("Scale Z",onCommitScale,this); // Rotation mLabelRotation = getChild("label rotation"); mCtrlRotX = getChild("Rot X"); childSetCommitCallback("Rot X",onCommitRotation,this); mCtrlRotY = getChild("Rot Y"); childSetCommitCallback("Rot Y",onCommitRotation,this); mCtrlRotZ = getChild("Rot Z"); childSetCommitCallback("Rot Z",onCommitRotation,this); // Copy/paste pos mBtnCopyPos = getChild("copy_pos_btn"); mBtnCopyPos->setCommitCallback(boost::bind(&LLPanelObject::onCopyPos, this)); mBtnPastePos = getChild("paste_pos_btn"); mBtnPastePos->setCommitCallback(boost::bind(&LLPanelObject::onPastePos, this)); // Copy/paste size mBtnCopySize = getChild("copy_size_btn"); mBtnCopySize->setCommitCallback(boost::bind(&LLPanelObject::onCopySize, this)); mBtnPasteSize = getChild("paste_size_btn"); mBtnPasteSize->setCommitCallback(boost::bind(&LLPanelObject::onPasteSize, this)); // Copy/paste rot mBtnCopyRot = getChild("copy_rot_btn"); mBtnCopyRot->setCommitCallback(boost::bind(&LLPanelObject::onCopyRot, this)); mBtnPasteRot = getChild("paste_rot_btn"); mBtnPasteRot->setCommitCallback(boost::bind(&LLPanelObject::onPasteRot, this));; // Copy/paste obj prams mBtnCopyParams = getChild("copy_params_btn"); mBtnCopyParams->setCommitCallback(boost::bind(&LLPanelObject::onCopyParams, this)); mBtnPasteParams = getChild("paste_params_btn"); mBtnPasteParams->setCommitCallback(boost::bind(&LLPanelObject::onPasteParams, this)); mBtnPasteMenu = getChild("paste_gear_btn"); //-------------------------------------------------------- // Base Type mComboBaseType = getChild("comboBaseType"); childSetCommitCallback("comboBaseType",onCommitParametric,this); // Cut mLabelCut = getChild("text cut"); mSpinCutBegin = getChild("cut begin"); childSetCommitCallback("cut begin",onCommitParametric,this); mSpinCutBegin->setValidateBeforeCommit( precommitValidate ); mSpinCutEnd = getChild("cut end"); childSetCommitCallback("cut end",onCommitParametric,this); mSpinCutEnd->setValidateBeforeCommit( &precommitValidate ); // Hollow / Skew mLabelHollow = getChild("text hollow"); mLabelSkew = getChild("text skew"); mSpinHollow = getChild("Scale 1"); childSetCommitCallback("Scale 1",onCommitParametric,this); mSpinHollow->setValidateBeforeCommit( &precommitValidate ); mSpinSkew = getChild("Skew"); childSetCommitCallback("Skew",onCommitParametric,this); mSpinSkew->setValidateBeforeCommit( &precommitValidate ); mLabelHoleType = getChild("Hollow Shape"); // Hole Type mComboHoleType = getChild("hole"); childSetCommitCallback("hole",onCommitParametric,this); // Twist mLabelTwist = getChild("text twist"); mSpinTwistBegin = getChild("Twist Begin"); childSetCommitCallback("Twist Begin",onCommitParametric,this); mSpinTwistBegin->setValidateBeforeCommit( precommitValidate ); mSpinTwist = getChild("Twist End"); childSetCommitCallback("Twist End",onCommitParametric,this); mSpinTwist->setValidateBeforeCommit( &precommitValidate ); // Scale mSpinScaleX = getChild("Taper Scale X"); childSetCommitCallback("Taper Scale X",onCommitParametric,this); mSpinScaleX->setValidateBeforeCommit( &precommitValidate ); mSpinScaleY = getChild("Taper Scale Y"); childSetCommitCallback("Taper Scale Y",onCommitParametric,this); mSpinScaleY->setValidateBeforeCommit( &precommitValidate ); // Shear mLabelShear = getChild("text topshear"); mSpinShearX = getChild("Shear X"); childSetCommitCallback("Shear X",onCommitParametric,this); mSpinShearX->setValidateBeforeCommit( &precommitValidate ); mSpinShearY = getChild("Shear Y"); childSetCommitCallback("Shear Y",onCommitParametric,this); mSpinShearY->setValidateBeforeCommit( &precommitValidate ); // Path / Profile mCtrlPathBegin = getChild("Path Limit Begin"); childSetCommitCallback("Path Limit Begin",onCommitParametric,this); mCtrlPathBegin->setValidateBeforeCommit( &precommitValidate ); mCtrlPathEnd = getChild("Path Limit End"); childSetCommitCallback("Path Limit End",onCommitParametric,this); mCtrlPathEnd->setValidateBeforeCommit( &precommitValidate ); // Taper mLabelTaper = getChild("text taper2"); mSpinTaperX = getChild("Taper X"); childSetCommitCallback("Taper X",onCommitParametric,this); mSpinTaperX->setValidateBeforeCommit( precommitValidate ); mSpinTaperY = getChild("Taper Y"); childSetCommitCallback("Taper Y",onCommitParametric,this); mSpinTaperY->setValidateBeforeCommit( precommitValidate ); // Radius Offset / Revolutions mLabelRadiusOffset = getChild("text radius delta"); mLabelRevolutions = getChild("text revolutions"); mSpinRadiusOffset = getChild("Radius Offset"); childSetCommitCallback("Radius Offset",onCommitParametric,this); mSpinRadiusOffset->setValidateBeforeCommit( &precommitValidate ); mSpinRevolutions = getChild("Revolutions"); childSetCommitCallback("Revolutions",onCommitParametric,this); mSpinRevolutions->setValidateBeforeCommit( &precommitValidate ); // Sculpt mCtrlSculptTexture = getChild("sculpt texture control"); if (mCtrlSculptTexture) { mCtrlSculptTexture->setDefaultImageAssetID(LLUUID(SCULPT_DEFAULT_TEXTURE)); mCtrlSculptTexture->setCommitCallback( boost::bind(&LLPanelObject::onCommitSculpt, this, _2 )); mCtrlSculptTexture->setOnCancelCallback( boost::bind(&LLPanelObject::onCancelSculpt, this, _2 )); mCtrlSculptTexture->setOnSelectCallback( boost::bind(&LLPanelObject::onSelectSculpt, this, _2 )); mCtrlSculptTexture->setDropCallback( boost::bind(&LLPanelObject::onDropSculpt, this, _2 )); // Don't allow (no copy) or (no transfer) textures to be selected during immediate mode mCtrlSculptTexture->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); mCtrlSculptTexture->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER); // Allow any texture to be used during non-immediate mode. mCtrlSculptTexture->setNonImmediateFilterPermMask(PERM_NONE); LLAggregatePermissions texture_perms; if (LLSelectMgr::getInstance()->selectGetAggregateTexturePermissions(texture_perms)) { BOOL can_copy = texture_perms.getValue(PERM_COPY) == LLAggregatePermissions::AP_EMPTY || texture_perms.getValue(PERM_COPY) == LLAggregatePermissions::AP_ALL; BOOL can_transfer = texture_perms.getValue(PERM_TRANSFER) == LLAggregatePermissions::AP_EMPTY || texture_perms.getValue(PERM_TRANSFER) == LLAggregatePermissions::AP_ALL; mCtrlSculptTexture->setCanApplyImmediately(can_copy && can_transfer); } else { mCtrlSculptTexture->setCanApplyImmediately(FALSE); } } mLabelSculptType = getChild("label sculpt type"); mCtrlSculptType = getChild("sculpt type control"); childSetCommitCallback("sculpt type control", onCommitSculptType, this); mCtrlSculptMirror = getChild("sculpt mirror control"); childSetCommitCallback("sculpt mirror control", onCommitSculptType, this); mCtrlSculptInvert = getChild("sculpt invert control"); childSetCommitCallback("sculpt invert control", onCommitSculptType, this); // Start with everyone disabled clearCtrls(); return TRUE; } LLPanelObject::LLPanelObject() : LLPanel(), mIsPhysical(FALSE), mIsTemporary(FALSE), mIsPhantom(FALSE), mSelectedType(MI_BOX), mSculptTextureRevert(LLUUID::null), mSculptTypeRevert(0), mSizeChanged(FALSE), mHasParamsClipboard(FALSE), mHasPosClipboard(FALSE), mHasSizeClipboard(FALSE), mHasRotClipboard(FALSE), mPasteParametric(TRUE), mPasteFlexible(TRUE), mPastePhysics(TRUE), mPasteLight(TRUE) { mEnableCallbackRegistrar.add("BuildObject.PasteCheckItem", boost::bind(&LLPanelObject::pasteCheckMenuItem, this, _2)); mCommitCallbackRegistrar.add("BuildObject.PasteDoToSelected", boost::bind(&LLPanelObject::pasteDoMenuItem, this, _2)); mEnableCallbackRegistrar.add("BuildObject.PasteEnable", boost::bind(&LLPanelObject::pasteEnabletMenuItem, this, _2)); } LLPanelObject::~LLPanelObject() { // Children all cleaned up by default view destructor. } void LLPanelObject::getState( ) { LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstRootObject(); LLViewerObject* root_objectp = objectp; if(!objectp) { objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); // *FIX: shouldn't we just keep the child? if (objectp) { LLViewerObject* parentp = objectp->getRootEdit(); if (parentp) { root_objectp = parentp; } else { root_objectp = objectp; } } } LLCalc* calcp = LLCalc::getInstance(); LLVOVolume *volobjp = NULL; if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME)) { volobjp = (LLVOVolume *)objectp; } if( !objectp ) { //forfeit focus if (gFocusMgr.childHasKeyboardFocus(this)) { gFocusMgr.setKeyboardFocus(NULL); } // Disable all text input fields clearCtrls(); calcp->clearAllVariables(); return; } S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME )) && (selected_count == 1); bool enable_move; bool enable_modify; LLSelectMgr::getInstance()->selectGetEditMoveLinksetPermissions(enable_move, enable_modify); BOOL enable_scale = enable_modify; BOOL enable_rotate = enable_move; // already accounts for a case of children, which needs permModify() as well LLVector3 vec; if (enable_move) { vec = objectp->getPositionEdit(); mCtrlPosX->set( vec.mV[VX] ); mCtrlPosY->set( vec.mV[VY] ); mCtrlPosZ->set( vec.mV[VZ] ); calcp->setVar(LLCalc::X_POS, vec.mV[VX]); calcp->setVar(LLCalc::Y_POS, vec.mV[VY]); calcp->setVar(LLCalc::Z_POS, vec.mV[VZ]); } else { mCtrlPosX->clear(); mCtrlPosY->clear(); mCtrlPosZ->clear(); calcp->clearVar(LLCalc::X_POS); calcp->clearVar(LLCalc::Y_POS); calcp->clearVar(LLCalc::Z_POS); } mLabelPosition->setEnabled( enable_move ); mCtrlPosX->setEnabled(enable_move); mCtrlPosY->setEnabled(enable_move); mCtrlPosZ->setEnabled(enable_move); mBtnCopyPos->setEnabled(enable_move); mBtnPastePos->setEnabled(enable_move && mHasPosClipboard); if (enable_scale) { vec = objectp->getScale(); mCtrlScaleX->set( vec.mV[VX] ); mCtrlScaleY->set( vec.mV[VY] ); mCtrlScaleZ->set( vec.mV[VZ] ); calcp->setVar(LLCalc::X_SCALE, vec.mV[VX]); calcp->setVar(LLCalc::Y_SCALE, vec.mV[VY]); calcp->setVar(LLCalc::Z_SCALE, vec.mV[VZ]); } else { mCtrlScaleX->clear(); mCtrlScaleY->clear(); mCtrlScaleZ->clear(); calcp->setVar(LLCalc::X_SCALE, 0.f); calcp->setVar(LLCalc::Y_SCALE, 0.f); calcp->setVar(LLCalc::Z_SCALE, 0.f); } mLabelSize->setEnabled( enable_scale ); mCtrlScaleX->setEnabled( enable_scale ); mCtrlScaleY->setEnabled( enable_scale ); mCtrlScaleZ->setEnabled( enable_scale ); mBtnCopySize->setEnabled( enable_scale ); mBtnPasteSize->setEnabled( enable_scale && mHasSizeClipboard ); LLQuaternion object_rot = objectp->getRotationEdit(); object_rot.getEulerAngles(&(mCurEulerDegrees.mV[VX]), &(mCurEulerDegrees.mV[VY]), &(mCurEulerDegrees.mV[VZ])); mCurEulerDegrees *= RAD_TO_DEG; mCurEulerDegrees.mV[VX] = fmod(ll_round(mCurEulerDegrees.mV[VX], OBJECT_ROTATION_PRECISION) + 360.f, 360.f); mCurEulerDegrees.mV[VY] = fmod(ll_round(mCurEulerDegrees.mV[VY], OBJECT_ROTATION_PRECISION) + 360.f, 360.f); mCurEulerDegrees.mV[VZ] = fmod(ll_round(mCurEulerDegrees.mV[VZ], OBJECT_ROTATION_PRECISION) + 360.f, 360.f); if (enable_rotate) { mCtrlRotX->set( mCurEulerDegrees.mV[VX] ); mCtrlRotY->set( mCurEulerDegrees.mV[VY] ); mCtrlRotZ->set( mCurEulerDegrees.mV[VZ] ); calcp->setVar(LLCalc::X_ROT, mCurEulerDegrees.mV[VX]); calcp->setVar(LLCalc::Y_ROT, mCurEulerDegrees.mV[VY]); calcp->setVar(LLCalc::Z_ROT, mCurEulerDegrees.mV[VZ]); } else { mCtrlRotX->clear(); mCtrlRotY->clear(); mCtrlRotZ->clear(); calcp->clearVar(LLCalc::X_ROT); calcp->clearVar(LLCalc::Y_ROT); calcp->clearVar(LLCalc::Z_ROT); } mLabelRotation->setEnabled( enable_rotate ); mCtrlRotX->setEnabled( enable_rotate ); mCtrlRotY->setEnabled( enable_rotate ); mCtrlRotZ->setEnabled( enable_rotate ); mBtnCopyRot->setEnabled( enable_rotate ); mBtnPasteRot->setEnabled( enable_rotate && mHasRotClipboard ); mBtnCopyParams->setEnabled( single_volume && enable_modify ); mBtnPasteParams->setEnabled( single_volume && enable_modify && mHasParamsClipboard ); mBtnPasteMenu->setEnabled( single_volume && enable_modify ); LLUUID owner_id; std::string owner_name; LLSelectMgr::getInstance()->selectGetOwner(owner_id, owner_name); // BUG? Check for all objects being editable? S32 roots_selected = LLSelectMgr::getInstance()->getSelection()->getRootObjectCount(); BOOL editable = root_objectp->permModify(); BOOL is_flexible = volobjp && volobjp->isFlexible(); BOOL is_permanent = root_objectp->flagObjectPermanent(); BOOL is_permanent_enforced = root_objectp->isPermanentEnforced(); BOOL is_character = root_objectp->flagCharacter(); llassert(!is_permanent || !is_character); // should never have a permanent object that is also a character // Lock checkbox - only modifiable if you own the object. BOOL self_owned = (gAgent.getID() == owner_id); mCheckLock->setEnabled( roots_selected > 0 && self_owned && !is_permanent_enforced); // More lock and debit checkbox - get the values BOOL valid; U32 owner_mask_on; U32 owner_mask_off; valid = LLSelectMgr::getInstance()->selectGetPerm(PERM_OWNER, &owner_mask_on, &owner_mask_off); if(valid) { if(owner_mask_on & PERM_MOVE) { // owner can move, so not locked mCheckLock->set(FALSE); mCheckLock->setTentative(FALSE); } else if(owner_mask_off & PERM_MOVE) { // owner can't move, so locked mCheckLock->set(TRUE); mCheckLock->setTentative(FALSE); } else { // some locked, some not locked mCheckLock->set(FALSE); mCheckLock->setTentative(TRUE); } } // Physics checkbox mIsPhysical = root_objectp->flagUsePhysics(); llassert(!is_permanent || !mIsPhysical); // should never have a permanent object that is also physical mCheckPhysics->set( mIsPhysical ); mCheckPhysics->setEnabled( roots_selected>0 && (editable || gAgent.isGodlike()) && !is_flexible && !is_permanent); mIsTemporary = root_objectp->flagTemporaryOnRez(); llassert(!is_permanent || !mIsTemporary); // should never has a permanent object that is also temporary mCheckTemporary->set( mIsTemporary ); mCheckTemporary->setEnabled( roots_selected>0 && editable && !is_permanent); mIsPhantom = root_objectp->flagPhantom(); BOOL is_volume_detect = root_objectp->flagVolumeDetect(); llassert(!is_character || !mIsPhantom); // should never have a character that is also a phantom mCheckPhantom->set( mIsPhantom ); mCheckPhantom->setEnabled( roots_selected>0 && editable && !is_flexible && !is_permanent_enforced && !is_character && !is_volume_detect); //---------------------------------------------------------------------------- S32 selected_item = MI_BOX; S32 selected_hole = MI_HOLE_SAME; BOOL enabled = FALSE; BOOL hole_enabled = FALSE; F32 scale_x=1.f, scale_y=1.f; BOOL isMesh = FALSE; if( !objectp || !objectp->getVolume() || !editable || !single_volume) { // Clear out all geometry fields. mComboBaseType->clear(); mSpinHollow->clear(); mSpinCutBegin->clear(); mSpinCutEnd->clear(); mCtrlPathBegin->clear(); mCtrlPathEnd->clear(); mSpinScaleX->clear(); mSpinScaleY->clear(); mSpinTwist->clear(); mSpinTwistBegin->clear(); mComboHoleType->clear(); mSpinShearX->clear(); mSpinShearY->clear(); mSpinTaperX->clear(); mSpinTaperY->clear(); mSpinRadiusOffset->clear(); mSpinRevolutions->clear(); mSpinSkew->clear(); mSelectedType = MI_NONE; } else { // Only allowed to change these parameters for objects // that you have permissions on AND are not attachments. enabled = root_objectp->permModify() && !root_objectp->isPermanentEnforced(); // Volume type const LLVolumeParams &volume_params = objectp->getVolume()->getParams(); U8 path = volume_params.getPathParams().getCurveType(); U8 profile_and_hole = volume_params.getProfileParams().getCurveType(); U8 profile = profile_and_hole & LL_PCODE_PROFILE_MASK; U8 hole = profile_and_hole & LL_PCODE_HOLE_MASK; // Scale goes first so we can differentiate between a sphere and a torus, // which have the same profile and path types. // Scale scale_x = volume_params.getRatioX(); scale_y = volume_params.getRatioY(); BOOL linear_path = (path == LL_PCODE_PATH_LINE) || (path == LL_PCODE_PATH_FLEXIBLE); if ( linear_path && profile == LL_PCODE_PROFILE_CIRCLE ) { selected_item = MI_CYLINDER; } else if ( linear_path && profile == LL_PCODE_PROFILE_SQUARE ) { selected_item = MI_BOX; } else if ( linear_path && profile == LL_PCODE_PROFILE_ISOTRI ) { selected_item = MI_PRISM; } else if ( linear_path && profile == LL_PCODE_PROFILE_EQUALTRI ) { selected_item = MI_PRISM; } else if ( linear_path && profile == LL_PCODE_PROFILE_RIGHTTRI ) { selected_item = MI_PRISM; } else if (path == LL_PCODE_PATH_FLEXIBLE) // shouldn't happen { selected_item = MI_CYLINDER; // reasonable default } else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_CIRCLE && scale_y > 0.75f) { selected_item = MI_SPHERE; } else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_CIRCLE && scale_y <= 0.75f) { selected_item = MI_TORUS; } else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_CIRCLE_HALF) { selected_item = MI_SPHERE; } else if ( path == LL_PCODE_PATH_CIRCLE2 && profile == LL_PCODE_PROFILE_CIRCLE ) { // Spirals aren't supported. Make it into a sphere. JC selected_item = MI_SPHERE; } else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_EQUALTRI ) { selected_item = MI_RING; } else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_SQUARE && scale_y <= 0.75f) { selected_item = MI_TUBE; } else { LL_INFOS() << "Unknown path " << (S32) path << " profile " << (S32) profile << " in getState" << LL_ENDL; selected_item = MI_BOX; } if (objectp->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT)) { selected_item = MI_SCULPT; //LLFirstUse::useSculptedPrim(); } mComboBaseType ->setCurrentByIndex( selected_item ); mSelectedType = selected_item; // Grab S path F32 begin_s = volume_params.getBeginS(); F32 end_s = volume_params.getEndS(); // Compute cut and advanced cut from S and T F32 begin_t = volume_params.getBeginT(); F32 end_t = volume_params.getEndT(); // Hollowness F32 hollow = 100.f * volume_params.getHollow(); mSpinHollow->set( hollow ); calcp->setVar(LLCalc::HOLLOW, hollow); // All hollow objects allow a shape to be selected. if (hollow > 0.f) { switch (hole) { case LL_PCODE_HOLE_CIRCLE: selected_hole = MI_HOLE_CIRCLE; break; case LL_PCODE_HOLE_SQUARE: selected_hole = MI_HOLE_SQUARE; break; case LL_PCODE_HOLE_TRIANGLE: selected_hole = MI_HOLE_TRIANGLE; break; case LL_PCODE_HOLE_SAME: default: selected_hole = MI_HOLE_SAME; break; } mComboHoleType->setCurrentByIndex( selected_hole ); hole_enabled = enabled; } else { mComboHoleType->setCurrentByIndex( MI_HOLE_SAME ); hole_enabled = FALSE; } // Cut interpretation varies based on base object type F32 cut_begin, cut_end, adv_cut_begin, adv_cut_end; if ( selected_item == MI_SPHERE || selected_item == MI_TORUS || selected_item == MI_TUBE || selected_item == MI_RING ) { cut_begin = begin_t; cut_end = end_t; adv_cut_begin = begin_s; adv_cut_end = end_s; } else { cut_begin = begin_s; cut_end = end_s; adv_cut_begin = begin_t; adv_cut_end = end_t; } mSpinCutBegin ->set( cut_begin ); mSpinCutEnd ->set( cut_end ); mCtrlPathBegin ->set( adv_cut_begin ); mCtrlPathEnd ->set( adv_cut_end ); calcp->setVar(LLCalc::CUT_BEGIN, cut_begin); calcp->setVar(LLCalc::CUT_END, cut_end); calcp->setVar(LLCalc::PATH_BEGIN, adv_cut_begin); calcp->setVar(LLCalc::PATH_END, adv_cut_end); // Twist F32 twist = volume_params.getTwist(); F32 twist_begin = volume_params.getTwistBegin(); // Check the path type for conversion. if (path == LL_PCODE_PATH_LINE || path == LL_PCODE_PATH_FLEXIBLE) { twist *= OBJECT_TWIST_LINEAR_MAX; twist_begin *= OBJECT_TWIST_LINEAR_MAX; } else { twist *= OBJECT_TWIST_MAX; twist_begin *= OBJECT_TWIST_MAX; } mSpinTwist ->set( twist ); mSpinTwistBegin ->set( twist_begin ); calcp->setVar(LLCalc::TWIST_END, twist); calcp->setVar(LLCalc::TWIST_BEGIN, twist_begin); // Shear F32 shear_x = volume_params.getShearX(); F32 shear_y = volume_params.getShearY(); mSpinShearX->set( shear_x ); mSpinShearY->set( shear_y ); calcp->setVar(LLCalc::X_SHEAR, shear_x); calcp->setVar(LLCalc::Y_SHEAR, shear_y); // Taper F32 taper_x = volume_params.getTaperX(); F32 taper_y = volume_params.getTaperY(); mSpinTaperX->set( taper_x ); mSpinTaperY->set( taper_y ); calcp->setVar(LLCalc::X_TAPER, taper_x); calcp->setVar(LLCalc::Y_TAPER, taper_y); // Radius offset. F32 radius_offset = volume_params.getRadiusOffset(); // Limit radius offset, based on taper and hole size y. F32 radius_mag = fabs(radius_offset); F32 hole_y_mag = fabs(scale_y); F32 taper_y_mag = fabs(taper_y); // Check to see if the taper effects us. if ( (radius_offset > 0.f && taper_y < 0.f) || (radius_offset < 0.f && taper_y > 0.f) ) { // The taper does not help increase the radius offset range. taper_y_mag = 0.f; } F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag); // Enforce the maximum magnitude. if (radius_mag > max_radius_mag) { // Check radius offset sign. if (radius_offset < 0.f) { radius_offset = -max_radius_mag; } else { radius_offset = max_radius_mag; } } mSpinRadiusOffset->set( radius_offset); calcp->setVar(LLCalc::RADIUS_OFFSET, radius_offset); // Revolutions F32 revolutions = volume_params.getRevolutions(); mSpinRevolutions->set( revolutions ); calcp->setVar(LLCalc::REVOLUTIONS, revolutions); // Skew F32 skew = volume_params.getSkew(); // Limit skew, based on revolutions hole size x. F32 skew_mag= fabs(skew); F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f); // Discontinuity; A revolution of 1 allows skews below 0.5. if ( fabs(revolutions - 1.0f) < 0.001) min_skew_mag = 0.0f; // Clip skew. if (skew_mag < min_skew_mag) { // Check skew sign. if (skew < 0.0f) { skew = -min_skew_mag; } else { skew = min_skew_mag; } } mSpinSkew->set( skew ); calcp->setVar(LLCalc::SKEW, skew); } // Compute control visibility, label names, and twist range. // Start with defaults. BOOL cut_visible = TRUE; BOOL hollow_visible = TRUE; BOOL top_size_x_visible = TRUE; BOOL top_size_y_visible = TRUE; BOOL top_shear_x_visible = TRUE; BOOL top_shear_y_visible = TRUE; BOOL twist_visible = TRUE; BOOL advanced_cut_visible = FALSE; BOOL taper_visible = FALSE; BOOL skew_visible = FALSE; BOOL radius_offset_visible = FALSE; BOOL revolutions_visible = FALSE; BOOL sculpt_texture_visible = FALSE; F32 twist_min = OBJECT_TWIST_LINEAR_MIN; F32 twist_max = OBJECT_TWIST_LINEAR_MAX; F32 twist_inc = OBJECT_TWIST_LINEAR_INC; BOOL advanced_is_dimple = FALSE; BOOL advanced_is_slice = FALSE; BOOL size_is_hole = FALSE; // Tune based on overall volume type switch (selected_item) { case MI_SPHERE: top_size_x_visible = FALSE; top_size_y_visible = FALSE; top_shear_x_visible = FALSE; top_shear_y_visible = FALSE; //twist_visible = FALSE; advanced_cut_visible = TRUE; advanced_is_dimple = TRUE; twist_min = OBJECT_TWIST_MIN; twist_max = OBJECT_TWIST_MAX; twist_inc = OBJECT_TWIST_INC; break; case MI_TORUS: case MI_TUBE: case MI_RING: //top_size_x_visible = FALSE; //top_size_y_visible = FALSE; size_is_hole = TRUE; skew_visible = TRUE; advanced_cut_visible = TRUE; taper_visible = TRUE; radius_offset_visible = TRUE; revolutions_visible = TRUE; twist_min = OBJECT_TWIST_MIN; twist_max = OBJECT_TWIST_MAX; twist_inc = OBJECT_TWIST_INC; break; case MI_SCULPT: cut_visible = FALSE; hollow_visible = FALSE; twist_visible = FALSE; top_size_x_visible = FALSE; top_size_y_visible = FALSE; top_shear_x_visible = FALSE; top_shear_y_visible = FALSE; skew_visible = FALSE; advanced_cut_visible = FALSE; taper_visible = FALSE; radius_offset_visible = FALSE; revolutions_visible = FALSE; sculpt_texture_visible = TRUE; break; case MI_BOX: advanced_cut_visible = TRUE; advanced_is_slice = TRUE; break; case MI_CYLINDER: advanced_cut_visible = TRUE; advanced_is_slice = TRUE; break; case MI_PRISM: advanced_cut_visible = TRUE; advanced_is_slice = TRUE; break; default: break; } // Check if we need to change top size/hole size params. switch (selected_item) { case MI_SPHERE: case MI_TORUS: case MI_TUBE: case MI_RING: mSpinScaleX->set( scale_x ); mSpinScaleY->set( scale_y ); calcp->setVar(LLCalc::X_HOLE, scale_x); calcp->setVar(LLCalc::Y_HOLE, scale_y); mSpinScaleX->setMinValue(OBJECT_MIN_HOLE_SIZE); mSpinScaleX->setMaxValue(OBJECT_MAX_HOLE_SIZE_X); mSpinScaleY->setMinValue(OBJECT_MIN_HOLE_SIZE); mSpinScaleY->setMaxValue(OBJECT_MAX_HOLE_SIZE_Y); break; default: if (editable) { mSpinScaleX->set( 1.f - scale_x ); mSpinScaleY->set( 1.f - scale_y ); mSpinScaleX->setMinValue(-1.f); mSpinScaleX->setMaxValue(1.f); mSpinScaleY->setMinValue(-1.f); mSpinScaleY->setMaxValue(1.f); // Torus' Hole Size is Box/Cyl/Prism's Taper calcp->setVar(LLCalc::X_TAPER, 1.f - scale_x); calcp->setVar(LLCalc::Y_TAPER, 1.f - scale_y); // Box/Cyl/Prism have no hole size calcp->setVar(LLCalc::X_HOLE, 0.f); calcp->setVar(LLCalc::Y_HOLE, 0.f); } break; } // Check if we need to limit the hollow based on the hole type. if ( selected_hole == MI_HOLE_SQUARE && ( selected_item == MI_CYLINDER || selected_item == MI_TORUS || selected_item == MI_PRISM || selected_item == MI_RING || selected_item == MI_SPHERE ) ) { mSpinHollow->setMinValue(0.f); mSpinHollow->setMaxValue(70.f); } else { mSpinHollow->setMinValue(0.f); mSpinHollow->setMaxValue(95.f); } // Update field enablement mComboBaseType ->setEnabled( enabled ); mLabelCut ->setEnabled( enabled ); mSpinCutBegin ->setEnabled( enabled ); mSpinCutEnd ->setEnabled( enabled ); mLabelHollow ->setEnabled( enabled ); mSpinHollow ->setEnabled( enabled ); mLabelHoleType ->setEnabled( hole_enabled ); mComboHoleType ->setEnabled( hole_enabled ); mLabelTwist ->setEnabled( enabled ); mSpinTwist ->setEnabled( enabled ); mSpinTwistBegin ->setEnabled( enabled ); mLabelSkew ->setEnabled( enabled ); mSpinSkew ->setEnabled( enabled ); getChildView("scale_hole")->setVisible( FALSE); getChildView("scale_taper")->setVisible( FALSE); if (top_size_x_visible || top_size_y_visible) { if (size_is_hole) { getChildView("scale_hole")->setVisible( TRUE); getChildView("scale_hole")->setEnabled(enabled); } else { getChildView("scale_taper")->setVisible( TRUE); getChildView("scale_taper")->setEnabled(enabled); } } mSpinScaleX ->setEnabled( enabled ); mSpinScaleY ->setEnabled( enabled ); mLabelShear ->setEnabled( enabled ); mSpinShearX ->setEnabled( enabled ); mSpinShearY ->setEnabled( enabled ); getChildView("advanced_cut")->setVisible( FALSE); getChildView("advanced_dimple")->setVisible( FALSE); getChildView("advanced_slice")->setVisible( FALSE); if (advanced_cut_visible) { if (advanced_is_dimple) { getChildView("advanced_dimple")->setVisible( TRUE); getChildView("advanced_dimple")->setEnabled(enabled); } else if (advanced_is_slice) { getChildView("advanced_slice")->setVisible( TRUE); getChildView("advanced_slice")->setEnabled(enabled); } else { getChildView("advanced_cut")->setVisible( TRUE); getChildView("advanced_cut")->setEnabled(enabled); } } mCtrlPathBegin ->setEnabled( enabled ); mCtrlPathEnd ->setEnabled( enabled ); mLabelTaper ->setEnabled( enabled ); mSpinTaperX ->setEnabled( enabled ); mSpinTaperY ->setEnabled( enabled ); mLabelRadiusOffset->setEnabled( enabled ); mSpinRadiusOffset ->setEnabled( enabled ); mLabelRevolutions->setEnabled( enabled ); mSpinRevolutions ->setEnabled( enabled ); // Update field visibility mLabelCut ->setVisible( cut_visible ); mSpinCutBegin ->setVisible( cut_visible ); mSpinCutEnd ->setVisible( cut_visible ); mLabelHollow ->setVisible( hollow_visible ); mSpinHollow ->setVisible( hollow_visible ); mLabelHoleType ->setVisible( hollow_visible ); mComboHoleType ->setVisible( hollow_visible ); mLabelTwist ->setVisible( twist_visible ); mSpinTwist ->setVisible( twist_visible ); mSpinTwistBegin ->setVisible( twist_visible ); mSpinTwist ->setMinValue( twist_min ); mSpinTwist ->setMaxValue( twist_max ); mSpinTwist ->setIncrement( twist_inc ); mSpinTwistBegin ->setMinValue( twist_min ); mSpinTwistBegin ->setMaxValue( twist_max ); mSpinTwistBegin ->setIncrement( twist_inc ); mSpinScaleX ->setVisible( top_size_x_visible ); mSpinScaleY ->setVisible( top_size_y_visible ); mLabelSkew ->setVisible( skew_visible ); mSpinSkew ->setVisible( skew_visible ); mLabelShear ->setVisible( top_shear_x_visible || top_shear_y_visible ); mSpinShearX ->setVisible( top_shear_x_visible ); mSpinShearY ->setVisible( top_shear_y_visible ); mCtrlPathBegin ->setVisible( advanced_cut_visible ); mCtrlPathEnd ->setVisible( advanced_cut_visible ); mLabelTaper ->setVisible( taper_visible ); mSpinTaperX ->setVisible( taper_visible ); mSpinTaperY ->setVisible( taper_visible ); mLabelRadiusOffset->setVisible( radius_offset_visible ); mSpinRadiusOffset ->setVisible( radius_offset_visible ); mLabelRevolutions->setVisible( revolutions_visible ); mSpinRevolutions ->setVisible( revolutions_visible ); mCtrlSculptTexture->setVisible(sculpt_texture_visible); mLabelSculptType->setVisible(sculpt_texture_visible); mCtrlSculptType->setVisible(sculpt_texture_visible); // sculpt texture if (selected_item == MI_SCULPT) { LLUUID id; LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT); if (sculpt_params) // if we have a legal sculpt param block for this object: { if (mObject != objectp) // we've just selected a new object, so save for undo { mSculptTextureRevert = sculpt_params->getSculptTexture(); mSculptTypeRevert = sculpt_params->getSculptType(); } U8 sculpt_type = sculpt_params->getSculptType(); U8 sculpt_stitching = sculpt_type & LL_SCULPT_TYPE_MASK; BOOL sculpt_invert = sculpt_type & LL_SCULPT_FLAG_INVERT; BOOL sculpt_mirror = sculpt_type & LL_SCULPT_FLAG_MIRROR; isMesh = (sculpt_stitching == LL_SCULPT_TYPE_MESH); LLTextureCtrl* mTextureCtrl = getChild("sculpt texture control"); if(mTextureCtrl) { mTextureCtrl->setTentative(FALSE); mTextureCtrl->setEnabled(editable && !isMesh); if (editable) mTextureCtrl->setImageAssetID(sculpt_params->getSculptTexture()); else mTextureCtrl->setImageAssetID(LLUUID::null); } mComboBaseType->setEnabled(!isMesh); if (mCtrlSculptType) { if (sculpt_stitching == LL_SCULPT_TYPE_NONE) { // since 'None' is no longer an option in the combo box // use 'Plane' as an equivalent sculpt type mCtrlSculptType->setSelectedByValue(LLSD(LL_SCULPT_TYPE_PLANE), true); } else { mCtrlSculptType->setSelectedByValue(LLSD(sculpt_stitching), true); } mCtrlSculptType->setEnabled(editable && !isMesh); } if (mCtrlSculptMirror) { mCtrlSculptMirror->set(sculpt_mirror); mCtrlSculptMirror->setEnabled(editable && !isMesh); } if (mCtrlSculptInvert) { mCtrlSculptInvert->set(sculpt_invert); mCtrlSculptInvert->setEnabled(editable); } if (mLabelSculptType) { mLabelSculptType->setEnabled(TRUE); } } } else { mSculptTextureRevert = LLUUID::null; } mCtrlSculptMirror->setVisible(sculpt_texture_visible && !isMesh); mCtrlSculptInvert->setVisible(sculpt_texture_visible && !isMesh); //---------------------------------------------------------------------------- mObject = objectp; mRootObject = root_objectp; } // static bool LLPanelObject::precommitValidate( const LLSD& data ) { // TODO: Richard will fill this in later. return TRUE; // FALSE means that validation failed and new value should not be commited. } void LLPanelObject::sendIsPhysical() { BOOL value = mCheckPhysics->get(); if( mIsPhysical != value ) { LLSelectMgr::getInstance()->selectionUpdatePhysics(value); mIsPhysical = value; LL_INFOS() << "update physics sent" << LL_ENDL; } else { LL_INFOS() << "update physics not changed" << LL_ENDL; } } void LLPanelObject::sendIsTemporary() { BOOL value = mCheckTemporary->get(); if( mIsTemporary != value ) { LLSelectMgr::getInstance()->selectionUpdateTemporary(value); mIsTemporary = value; LL_INFOS() << "update temporary sent" << LL_ENDL; } else { LL_INFOS() << "update temporary not changed" << LL_ENDL; } } void LLPanelObject::sendIsPhantom() { BOOL value = mCheckPhantom->get(); if( mIsPhantom != value ) { LLSelectMgr::getInstance()->selectionUpdatePhantom(value); mIsPhantom = value; LL_INFOS() << "update phantom sent" << LL_ENDL; } else { LL_INFOS() << "update phantom not changed" << LL_ENDL; } } // static void LLPanelObject::onCommitParametric( LLUICtrl* ctrl, void* userdata ) { LLPanelObject* self = (LLPanelObject*) userdata; if (self->mObject.isNull()) { return; } if (self->mObject->getPCode() != LL_PCODE_VOLUME) { // Don't allow modification of non-volume objects. return; } LLVolume *volume = self->mObject->getVolume(); if (!volume) { return; } LLVolumeParams volume_params; self->getVolumeParams(volume_params); // set sculpting S32 selected_type = self->mComboBaseType->getCurrentIndex(); if (selected_type == MI_SCULPT) { self->mObject->setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, TRUE, TRUE); LLSculptParams *sculpt_params = (LLSculptParams *)self->mObject->getParameterEntry(LLNetworkData::PARAMS_SCULPT); if (sculpt_params) volume_params.setSculptID(sculpt_params->getSculptTexture(), sculpt_params->getSculptType()); } else { LLSculptParams *sculpt_params = (LLSculptParams *)self->mObject->getParameterEntry(LLNetworkData::PARAMS_SCULPT); if (sculpt_params) self->mObject->setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, FALSE, TRUE); } // Update the volume, if necessary. self->mObject->updateVolume(volume_params); // This was added to make sure thate when changes are made, the UI // adjusts to present valid options. // *FIX: only some changes, ie, hollow or primitive type changes, // require a refresh. self->refresh(); } void LLPanelObject::getVolumeParams(LLVolumeParams& volume_params) { // Figure out what type of volume to make S32 was_selected_type = mSelectedType; S32 selected_type = mComboBaseType->getCurrentIndex(); U8 profile; U8 path; switch ( selected_type ) { case MI_CYLINDER: profile = LL_PCODE_PROFILE_CIRCLE; path = LL_PCODE_PATH_LINE; break; case MI_BOX: profile = LL_PCODE_PROFILE_SQUARE; path = LL_PCODE_PATH_LINE; break; case MI_PRISM: profile = LL_PCODE_PROFILE_EQUALTRI; path = LL_PCODE_PATH_LINE; break; case MI_SPHERE: profile = LL_PCODE_PROFILE_CIRCLE_HALF; path = LL_PCODE_PATH_CIRCLE; break; case MI_TORUS: profile = LL_PCODE_PROFILE_CIRCLE; path = LL_PCODE_PATH_CIRCLE; break; case MI_TUBE: profile = LL_PCODE_PROFILE_SQUARE; path = LL_PCODE_PATH_CIRCLE; break; case MI_RING: profile = LL_PCODE_PROFILE_EQUALTRI; path = LL_PCODE_PATH_CIRCLE; break; case MI_SCULPT: profile = LL_PCODE_PROFILE_CIRCLE; path = LL_PCODE_PATH_CIRCLE; break; default: LL_WARNS() << "Unknown base type " << selected_type << " in getVolumeParams()" << LL_ENDL; // assume a box selected_type = MI_BOX; profile = LL_PCODE_PROFILE_SQUARE; path = LL_PCODE_PATH_LINE; break; } if (path == LL_PCODE_PATH_LINE) { LLVOVolume *volobjp = (LLVOVolume *)(LLViewerObject*)(mObject); if (volobjp->isFlexible()) { path = LL_PCODE_PATH_FLEXIBLE; } } S32 selected_hole = mComboHoleType->getCurrentIndex(); U8 hole; switch (selected_hole) { case MI_HOLE_CIRCLE: hole = LL_PCODE_HOLE_CIRCLE; break; case MI_HOLE_SQUARE: hole = LL_PCODE_HOLE_SQUARE; break; case MI_HOLE_TRIANGLE: hole = LL_PCODE_HOLE_TRIANGLE; break; case MI_HOLE_SAME: default: hole = LL_PCODE_HOLE_SAME; break; } volume_params.setType(profile | hole, path); mSelectedType = selected_type; // Compute cut start/end F32 cut_begin = mSpinCutBegin->get(); F32 cut_end = mSpinCutEnd->get(); // Make sure at least OBJECT_CUT_INC of the object survives if (cut_begin > cut_end - OBJECT_MIN_CUT_INC) { cut_begin = cut_end - OBJECT_MIN_CUT_INC; mSpinCutBegin->set(cut_begin); } F32 adv_cut_begin = mCtrlPathBegin->get(); F32 adv_cut_end = mCtrlPathEnd->get(); // Make sure at least OBJECT_CUT_INC of the object survives if (adv_cut_begin > adv_cut_end - OBJECT_MIN_CUT_INC) { adv_cut_begin = adv_cut_end - OBJECT_MIN_CUT_INC; mCtrlPathBegin->set(adv_cut_begin); } F32 begin_s, end_s; F32 begin_t, end_t; if (selected_type == MI_SPHERE || selected_type == MI_TORUS || selected_type == MI_TUBE || selected_type == MI_RING) { begin_s = adv_cut_begin; end_s = adv_cut_end; begin_t = cut_begin; end_t = cut_end; } else { begin_s = cut_begin; end_s = cut_end; begin_t = adv_cut_begin; end_t = adv_cut_end; } volume_params.setBeginAndEndS(begin_s, end_s); volume_params.setBeginAndEndT(begin_t, end_t); // Hollowness F32 hollow = mSpinHollow->get() / 100.f; if ( selected_hole == MI_HOLE_SQUARE && ( selected_type == MI_CYLINDER || selected_type == MI_TORUS || selected_type == MI_PRISM || selected_type == MI_RING || selected_type == MI_SPHERE ) ) { if (hollow > 0.7f) hollow = 0.7f; } volume_params.setHollow( hollow ); // Twist Begin,End F32 twist_begin = mSpinTwistBegin->get(); F32 twist = mSpinTwist->get(); // Check the path type for twist conversion. if (path == LL_PCODE_PATH_LINE || path == LL_PCODE_PATH_FLEXIBLE) { twist_begin /= OBJECT_TWIST_LINEAR_MAX; twist /= OBJECT_TWIST_LINEAR_MAX; } else { twist_begin /= OBJECT_TWIST_MAX; twist /= OBJECT_TWIST_MAX; } volume_params.setTwistBegin(twist_begin); volume_params.setTwist(twist); // Scale X,Y F32 scale_x = mSpinScaleX->get(); F32 scale_y = mSpinScaleY->get(); if ( was_selected_type == MI_BOX || was_selected_type == MI_CYLINDER || was_selected_type == MI_PRISM) { scale_x = 1.f - scale_x; scale_y = 1.f - scale_y; } // Skew F32 skew = mSpinSkew->get(); // Taper X,Y F32 taper_x = mSpinTaperX->get(); F32 taper_y = mSpinTaperY->get(); // Radius offset F32 radius_offset = mSpinRadiusOffset->get(); // Revolutions F32 revolutions = mSpinRevolutions->get(); if ( selected_type == MI_SPHERE ) { // Snap values to valid sphere parameters. scale_x = 1.0f; scale_y = 1.0f; skew = 0.0f; taper_x = 0.0f; taper_y = 0.0f; radius_offset = 0.0f; revolutions = 1.0f; } else if ( selected_type == MI_TORUS || selected_type == MI_TUBE || selected_type == MI_RING ) { scale_x = llclamp( scale_x, OBJECT_MIN_HOLE_SIZE, OBJECT_MAX_HOLE_SIZE_X); scale_y = llclamp( scale_y, OBJECT_MIN_HOLE_SIZE, OBJECT_MAX_HOLE_SIZE_Y); // Limit radius offset, based on taper and hole size y. F32 radius_mag = fabs(radius_offset); F32 hole_y_mag = fabs(scale_y); F32 taper_y_mag = fabs(taper_y); // Check to see if the taper effects us. if ( (radius_offset > 0.f && taper_y < 0.f) || (radius_offset < 0.f && taper_y > 0.f) ) { // The taper does not help increase the radius offset range. taper_y_mag = 0.f; } F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag); // Enforce the maximum magnitude. if (radius_mag > max_radius_mag) { // Check radius offset sign. if (radius_offset < 0.f) { radius_offset = -max_radius_mag; } else { radius_offset = max_radius_mag; } } // Check the skew value against the revolutions. F32 skew_mag= fabs(skew); F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f); // Discontinuity; A revolution of 1 allows skews below 0.5. if ( fabs(revolutions - 1.0f) < 0.001) min_skew_mag = 0.0f; // Clip skew. if (skew_mag < min_skew_mag) { // Check skew sign. if (skew < 0.0f) { skew = -min_skew_mag; } else { skew = min_skew_mag; } } } volume_params.setRatio( scale_x, scale_y ); volume_params.setSkew(skew); volume_params.setTaper( taper_x, taper_y ); volume_params.setRadiusOffset(radius_offset); volume_params.setRevolutions(revolutions); // Shear X,Y F32 shear_x = mSpinShearX->get(); F32 shear_y = mSpinShearY->get(); volume_params.setShear( shear_x, shear_y ); if (selected_type == MI_SCULPT) { volume_params.setSculptID(LLUUID::null, 0); volume_params.setBeginAndEndT (0, 1); volume_params.setBeginAndEndS (0, 1); volume_params.setHollow (0); volume_params.setTwistBegin (0); volume_params.setTwistEnd (0); volume_params.setRatio (1, 0.5); volume_params.setShear (0, 0); volume_params.setTaper (0, 0); volume_params.setRevolutions (1); volume_params.setRadiusOffset (0); volume_params.setSkew (0); } } // BUG: Make work with multiple objects void LLPanelObject::sendRotation(BOOL btn_down) { if (mObject.isNull()) return; LLVector3 new_rot(mCtrlRotX->get(), mCtrlRotY->get(), mCtrlRotZ->get()); new_rot.mV[VX] = ll_round(new_rot.mV[VX], OBJECT_ROTATION_PRECISION); new_rot.mV[VY] = ll_round(new_rot.mV[VY], OBJECT_ROTATION_PRECISION); new_rot.mV[VZ] = ll_round(new_rot.mV[VZ], OBJECT_ROTATION_PRECISION); // Note: must compare before conversion to radians LLVector3 delta = new_rot - mCurEulerDegrees; if (delta.magVec() >= 0.0005f) { mCurEulerDegrees = new_rot; new_rot *= DEG_TO_RAD; LLQuaternion rotation; rotation.setQuat(new_rot.mV[VX], new_rot.mV[VY], new_rot.mV[VZ]); if (mRootObject != mObject) { rotation = rotation * ~mRootObject->getRotationRegion(); } // To include avatars into movements and rotation // If false, all children are selected anyway - move avatar // If true, not all children are selected - save positions bool individual_selection = gSavedSettings.getBOOL("EditLinkedParts"); std::vector& child_positions = mObject->mUnselectedChildrenPositions ; std::vector child_rotations; if (mObject->isRootEdit() && individual_selection) { mObject->saveUnselectedChildrenRotation(child_rotations) ; mObject->saveUnselectedChildrenPosition(child_positions) ; } mObject->setRotation(rotation); LLManip::rebuild(mObject) ; // for individually selected roots, we need to counterrotate all the children if (mObject->isRootEdit() && individual_selection) { mObject->resetChildrenRotationAndPosition(child_rotations, child_positions) ; } if(!btn_down) { child_positions.clear() ; LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_ROTATION | UPD_POSITION); } } } // BUG: Make work with multiple objects void LLPanelObject::sendScale(BOOL btn_down) { if (mObject.isNull()) return; LLVector3 newscale(mCtrlScaleX->get(), mCtrlScaleY->get(), mCtrlScaleZ->get()); LLVector3 delta = newscale - mObject->getScale(); if (delta.magVec() >= 0.0005f || (mSizeChanged && !btn_down)) { // scale changed by more than 1/2 millimeter mSizeChanged = btn_down; // check to see if we aren't scaling the textures // (in which case the tex coord's need to be recomputed) BOOL dont_stretch_textures = !LLManipScale::getStretchTextures(); if (dont_stretch_textures) { LLSelectMgr::getInstance()->saveSelectedObjectTransform(SELECT_ACTION_TYPE_SCALE); } mObject->setScale(newscale, TRUE); if(!btn_down) { LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_SCALE | UPD_POSITION); } LLSelectMgr::getInstance()->adjustTexturesByScale(TRUE, !dont_stretch_textures); // LL_INFOS() << "scale sent" << LL_ENDL; } else { // LL_INFOS() << "scale not changed" << LL_ENDL; } } void LLPanelObject::sendPosition(BOOL btn_down) { if (mObject.isNull()) return; LLVector3 newpos(mCtrlPosX->get(), mCtrlPosY->get(), mCtrlPosZ->get()); LLViewerRegion* regionp = mObject->getRegion(); // Clamp the Z height const F32 height = newpos.mV[VZ]; const F32 min_height = LLWorld::getInstance()->getMinAllowedZ(mObject, mObject->getPositionGlobal()); const F32 max_height = LLWorld::getInstance()->getRegionMaxHeight(); if (!mObject->isAttachment()) { if ( height < min_height) { newpos.mV[VZ] = min_height; mCtrlPosZ->set( min_height ); } else if ( height > max_height ) { newpos.mV[VZ] = max_height; mCtrlPosZ->set( max_height ); } // Grass is always drawn on the ground, so clamp its position to the ground if (mObject->getPCode() == LL_PCODE_LEGACY_GRASS) { mCtrlPosZ->set(LLWorld::getInstance()->resolveLandHeightAgent(newpos) + 1.f); } } // Make sure new position is in a valid region, so the object // won't get dumped by the simulator. LLVector3d new_pos_global = regionp->getPosGlobalFromRegion(newpos); if ( LLWorld::getInstance()->positionRegionValidGlobal(new_pos_global) ) { // send only if the position is changed, that is, the delta vector is not zero LLVector3d old_pos_global = mObject->getPositionGlobal(); LLVector3d delta = new_pos_global - old_pos_global; // moved more than 1/2 millimeter if (delta.magVec() >= 0.0005f) { if (mRootObject != mObject) { newpos = newpos - mRootObject->getPositionRegion(); newpos = newpos * ~mRootObject->getRotationRegion(); mObject->setPositionParent(newpos); } else { mObject->setPositionEdit(newpos); } LLManip::rebuild(mObject) ; // for individually selected roots, we need to counter-translate all unselected children if (mObject->isRootEdit()) { // only offset by parent's translation mObject->resetChildrenPosition(LLVector3(-delta), TRUE, TRUE) ; } if(!btn_down) { LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_POSITION); } LLSelectMgr::getInstance()->updateSelectionCenter(); } } else { // move failed, so we update the UI with the correct values LLVector3 vec = mRootObject->getPositionRegion(); mCtrlPosX->set(vec.mV[VX]); mCtrlPosY->set(vec.mV[VY]); mCtrlPosZ->set(vec.mV[VZ]); } } void LLPanelObject::sendSculpt() { if (mObject.isNull()) return; LLSculptParams sculpt_params; LLUUID sculpt_id = LLUUID::null; if (mCtrlSculptTexture) sculpt_id = mCtrlSculptTexture->getImageAssetID(); U8 sculpt_type = 0; if (mCtrlSculptType) sculpt_type |= mCtrlSculptType->getValue().asInteger(); bool enabled = sculpt_type != LL_SCULPT_TYPE_MESH; if (mCtrlSculptMirror) { mCtrlSculptMirror->setEnabled(enabled ? TRUE : FALSE); } if (mCtrlSculptInvert) { mCtrlSculptInvert->setEnabled(enabled ? TRUE : FALSE); } if ((mCtrlSculptMirror) && (mCtrlSculptMirror->get())) sculpt_type |= LL_SCULPT_FLAG_MIRROR; if ((mCtrlSculptInvert) && (mCtrlSculptInvert->get())) sculpt_type |= LL_SCULPT_FLAG_INVERT; sculpt_params.setSculptTexture(sculpt_id, sculpt_type); mObject->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, TRUE); } void LLPanelObject::refresh() { getState(); if (mObject.notNull() && mObject->isDead()) { mObject = NULL; } if (mRootObject.notNull() && mRootObject->isDead()) { mRootObject = NULL; } F32 max_scale = get_default_max_prim_scale(LLPickInfo::isFlora(mObject)); getChild("Scale X")->setMaxValue(max_scale); getChild("Scale Y")->setMaxValue(max_scale); getChild("Scale Z")->setMaxValue(max_scale); } void LLPanelObject::draw() { const LLColor4 white( 1.0f, 1.0f, 1.0f, 1); const LLColor4 red( 1.0f, 0.25f, 0.f, 1); const LLColor4 green( 0.f, 1.0f, 0.f, 1); const LLColor4 blue( 0.f, 0.5f, 1.0f, 1); // Tune the colors of the labels LLTool* tool = LLToolMgr::getInstance()->getCurrentTool(); if (tool == LLToolCompTranslate::getInstance()) { mCtrlPosX ->setLabelColor(red); mCtrlPosY ->setLabelColor(green); mCtrlPosZ ->setLabelColor(blue); mCtrlScaleX ->setLabelColor(white); mCtrlScaleY ->setLabelColor(white); mCtrlScaleZ ->setLabelColor(white); mCtrlRotX ->setLabelColor(white); mCtrlRotY ->setLabelColor(white); mCtrlRotZ ->setLabelColor(white); } else if ( tool == LLToolCompScale::getInstance() ) { mCtrlPosX ->setLabelColor(white); mCtrlPosY ->setLabelColor(white); mCtrlPosZ ->setLabelColor(white); mCtrlScaleX ->setLabelColor(red); mCtrlScaleY ->setLabelColor(green); mCtrlScaleZ ->setLabelColor(blue); mCtrlRotX ->setLabelColor(white); mCtrlRotY ->setLabelColor(white); mCtrlRotZ ->setLabelColor(white); } else if ( tool == LLToolCompRotate::getInstance() ) { mCtrlPosX ->setLabelColor(white); mCtrlPosY ->setLabelColor(white); mCtrlPosZ ->setLabelColor(white); mCtrlScaleX ->setLabelColor(white); mCtrlScaleY ->setLabelColor(white); mCtrlScaleZ ->setLabelColor(white); mCtrlRotX ->setLabelColor(red); mCtrlRotY ->setLabelColor(green); mCtrlRotZ ->setLabelColor(blue); } else { mCtrlPosX ->setLabelColor(white); mCtrlPosY ->setLabelColor(white); mCtrlPosZ ->setLabelColor(white); mCtrlScaleX ->setLabelColor(white); mCtrlScaleY ->setLabelColor(white); mCtrlScaleZ ->setLabelColor(white); mCtrlRotX ->setLabelColor(white); mCtrlRotY ->setLabelColor(white); mCtrlRotZ ->setLabelColor(white); } LLPanel::draw(); } // virtual void LLPanelObject::clearCtrls() { LLPanel::clearCtrls(); mCheckLock ->set(FALSE); mCheckLock ->setEnabled( FALSE ); mCheckPhysics ->set(FALSE); mCheckPhysics ->setEnabled( FALSE ); mCheckTemporary ->set(FALSE); mCheckTemporary ->setEnabled( FALSE ); mCheckPhantom ->set(FALSE); mCheckPhantom ->setEnabled( FALSE ); // Disable text labels mLabelPosition ->setEnabled( FALSE ); mLabelSize ->setEnabled( FALSE ); mLabelRotation ->setEnabled( FALSE ); mLabelCut ->setEnabled( FALSE ); mLabelHollow ->setEnabled( FALSE ); mLabelHoleType ->setEnabled( FALSE ); mLabelTwist ->setEnabled( FALSE ); mLabelSkew ->setEnabled( FALSE ); mLabelShear ->setEnabled( FALSE ); mLabelTaper ->setEnabled( FALSE ); mLabelRadiusOffset->setEnabled( FALSE ); mLabelRevolutions->setEnabled( FALSE ); getChildView("scale_hole")->setEnabled(FALSE); getChildView("scale_taper")->setEnabled(FALSE); getChildView("advanced_cut")->setEnabled(FALSE); getChildView("advanced_dimple")->setEnabled(FALSE); getChildView("advanced_slice")->setVisible( FALSE); } // // Static functions // // static void LLPanelObject::onCommitLock(LLUICtrl *ctrl, void *data) { // Checkbox will have toggled itself LLPanelObject *self = (LLPanelObject *)data; if(self->mRootObject.isNull()) return; BOOL new_state = self->mCheckLock->get(); LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_OWNER, !new_state, PERM_MOVE | PERM_MODIFY); } // static void LLPanelObject::onCommitPosition( LLUICtrl* ctrl, void* userdata ) { LLPanelObject* self = (LLPanelObject*) userdata; BOOL btn_down = ((LLSpinCtrl*)ctrl)->isMouseHeldDown() ; self->sendPosition(btn_down); } // static void LLPanelObject::onCommitScale( LLUICtrl* ctrl, void* userdata ) { LLPanelObject* self = (LLPanelObject*) userdata; BOOL btn_down = ((LLSpinCtrl*)ctrl)->isMouseHeldDown() ; self->sendScale(btn_down); } // static void LLPanelObject::onCommitRotation( LLUICtrl* ctrl, void* userdata ) { LLPanelObject* self = (LLPanelObject*) userdata; BOOL btn_down = ((LLSpinCtrl*)ctrl)->isMouseHeldDown() ; self->sendRotation(btn_down); } // static void LLPanelObject::onCommitPhysics( LLUICtrl* ctrl, void* userdata ) { LLPanelObject* self = (LLPanelObject*) userdata; self->sendIsPhysical(); } // static void LLPanelObject::onCommitTemporary( LLUICtrl* ctrl, void* userdata ) { LLPanelObject* self = (LLPanelObject*) userdata; self->sendIsTemporary(); } // static void LLPanelObject::onCommitPhantom( LLUICtrl* ctrl, void* userdata ) { LLPanelObject* self = (LLPanelObject*) userdata; self->sendIsPhantom(); } void LLPanelObject::onSelectSculpt(const LLSD& data) { LLTextureCtrl* mTextureCtrl = getChild("sculpt texture control"); if (mTextureCtrl) { mSculptTextureRevert = mTextureCtrl->getImageAssetID(); } sendSculpt(); } void LLPanelObject::onCommitSculpt( const LLSD& data ) { sendSculpt(); } BOOL LLPanelObject::onDropSculpt(LLInventoryItem* item) { LLTextureCtrl* mTextureCtrl = getChild("sculpt texture control"); if (mTextureCtrl) { LLUUID asset = item->getAssetUUID(); mTextureCtrl->setImageAssetID(asset); mSculptTextureRevert = asset; } return TRUE; } void LLPanelObject::onCancelSculpt(const LLSD& data) { LLTextureCtrl* mTextureCtrl = getChild("sculpt texture control"); if(!mTextureCtrl) return; if(mSculptTextureRevert == LLUUID::null) { mSculptTextureRevert = LLUUID(SCULPT_DEFAULT_TEXTURE); } mTextureCtrl->setImageAssetID(mSculptTextureRevert); sendSculpt(); } // static void LLPanelObject::onCommitSculptType(LLUICtrl *ctrl, void* userdata) { LLPanelObject* self = (LLPanelObject*) userdata; self->sendSculpt(); } void LLPanelObject::onCopyPos() { mClipboardPos = LLVector3(mCtrlPosX->get(), mCtrlPosY->get(), mCtrlPosZ->get()); std::string stringVec = llformat("<%g, %g, %g>", mClipboardPos.mV[VX], mClipboardPos.mV[VY], mClipboardPos.mV[VZ]); LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec)); LLStringUtil::format_map_t args; args["VALUE"] = stringVec; mBtnPastePos->setToolTip(getString("paste_position", args)); mBtnPastePos->setEnabled(TRUE); mHasPosClipboard = TRUE; } void LLPanelObject::onCopySize() { mClipboardSize = LLVector3(mCtrlScaleX->get(), mCtrlScaleY->get(), mCtrlScaleZ->get()); std::string stringVec = llformat("<%g, %g, %g>", mClipboardSize.mV[VX], mClipboardSize.mV[VY], mClipboardSize.mV[VZ]); LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec)); LLStringUtil::format_map_t args; args["VALUE"] = stringVec; mBtnPasteSize->setToolTip(getString("paste_size", args)); mBtnPasteSize->setEnabled(TRUE); mHasSizeClipboard = TRUE; } void LLPanelObject::onCopyRot() { mClipboardRot = LLVector3(mCtrlRotX->get(), mCtrlRotY->get(), mCtrlRotZ->get()); std::string stringVec = llformat("<%g, %g, %g>", mClipboardRot.mV[VX], mClipboardRot.mV[VY], mClipboardRot.mV[VZ]); LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(stringVec)); LLStringUtil::format_map_t args; args["VALUE"] = stringVec; mBtnPasteRot->setToolTip(getString("paste_rotation", args)); mBtnPasteRot->setEnabled(TRUE); mHasRotClipboard = TRUE; } void LLPanelObject::onPastePos() { if(!mHasPosClipboard) return; // Clamp pos on non-attachments, just keep the prims within the region if (!mObject->isAttachment()) { mClipboardPos.mV[VX] = llclamp( mClipboardPos.mV[VX], 0.f, 256.f); mClipboardPos.mV[VY] = llclamp( mClipboardPos.mV[VY], 0.f, 256.f); //height will get properly clammed by sendPosition } mCtrlPosX->set( mClipboardPos.mV[VX] ); mCtrlPosY->set( mClipboardPos.mV[VY] ); mCtrlPosZ->set( mClipboardPos.mV[VZ] ); sendPosition(FALSE); } void LLPanelObject::onPasteSize() { if(!mHasSizeClipboard) return; mClipboardSize.mV[VX] = llclamp(mClipboardSize.mV[VX], MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE); mClipboardSize.mV[VY] = llclamp(mClipboardSize.mV[VY], MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE); mClipboardSize.mV[VZ] = llclamp(mClipboardSize.mV[VZ], MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE); mCtrlScaleX->set( mClipboardSize.mV[VX] ); mCtrlScaleY->set( mClipboardSize.mV[VY] ); mCtrlScaleZ->set( mClipboardSize.mV[VZ] ); sendScale(FALSE); } void LLPanelObject::onPasteRot() { if(!mHasRotClipboard) return; mCtrlRotX->set( mClipboardRot.mV[VX] ); mCtrlRotY->set( mClipboardRot.mV[VY] ); mCtrlRotZ->set( mClipboardRot.mV[VZ] ); sendRotation(FALSE); } void LLPanelObject::onCopyParams() { LLViewerObject* objectp = mObject; if (!objectp) { return; } mParamsClipboard.clear(); mParamsClipboard["is_phantom"] = objectp->flagPhantom(); mParamsClipboard["is_physical"] = objectp->flagUsePhysics(); // Parametrics if (!objectp->isMesh()) { getVolumeParams(mClipboardVolumeParams); } LLVOVolume *volobjp = NULL; if (objectp && (objectp->getPCode() == LL_PCODE_VOLUME)) { volobjp = (LLVOVolume *)objectp; } // Flexi Prim if (volobjp && volobjp->isFlexible()) { LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE); if (attributes) { mParamsClipboard["flex"]["lod"] = attributes->getSimulateLOD(); mParamsClipboard["flex"]["gav"] = attributes->getGravity(); mParamsClipboard["flex"]["ten"] = attributes->getTension(); mParamsClipboard["flex"]["fri"] = attributes->getAirFriction(); mParamsClipboard["flex"]["sen"] = attributes->getWindSensitivity(); LLVector3 force = attributes->getUserForce(); mParamsClipboard["flex"]["forx"] = force.mV[0]; mParamsClipboard["flex"]["fory"] = force.mV[1]; mParamsClipboard["flex"]["forz"] = force.mV[2]; } } // Sculpted Prim if (objectp->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT)) { LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT); if (!objectp->isMesh()) { LLUUID texture_id = sculpt_params->getSculptTexture(); if (canCopyTexture(texture_id)) { LL_INFOS() << "copy texture " << LL_ENDL; mParamsClipboard["sculpt"]["id"] = texture_id; } else { mParamsClipboard["sculpt"]["id"] = LLUUID(SCULPT_DEFAULT_TEXTURE); } mParamsClipboard["sculpt"]["type"] = sculpt_params->getSculptType(); } } // Light Source if (volobjp && volobjp->getIsLight()) { mParamsClipboard["light"]["intensity"] = volobjp->getLightIntensity(); mParamsClipboard["light"]["radius"] = volobjp->getLightRadius(); mParamsClipboard["light"]["falloff"] = volobjp->getLightFalloff(); LLColor3 color = volobjp->getLightColor(); mParamsClipboard["light"]["r"] = color.mV[0]; mParamsClipboard["light"]["g"] = color.mV[1]; mParamsClipboard["light"]["b"] = color.mV[2]; // Spotlight if (volobjp->isLightSpotlight()) { LLUUID id = volobjp->getLightTextureID(); if (id.notNull() && canCopyTexture(id)) { mParamsClipboard["spot"]["id"] = id; LLVector3 spot_params = volobjp->getSpotLightParams(); mParamsClipboard["spot"]["fov"] = spot_params.mV[0]; mParamsClipboard["spot"]["focus"] = spot_params.mV[1]; mParamsClipboard["spot"]["ambiance"] = spot_params.mV[2]; } } } // Physics { mParamsClipboard["physics"]["shape"] = objectp->getPhysicsShapeType(); mParamsClipboard["physics"]["gravity"] = objectp->getPhysicsGravity(); mParamsClipboard["physics"]["friction"] = objectp->getPhysicsFriction(); mParamsClipboard["physics"]["density"] = objectp->getPhysicsDensity(); mParamsClipboard["physics"]["restitution"] = objectp->getPhysicsRestitution(); U8 material_code = 0; struct f : public LLSelectedTEGetFunctor { U8 get(LLViewerObject* object, S32 te) { return object->getMaterial(); } } func; bool material_same = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, material_code ); // This should always be true since material should be per object. if (material_same) { mParamsClipboard["physics"]["material"] = material_code; } } mHasParamsClipboard = TRUE; } void LLPanelObject::onPasteParams() { LLViewerObject* objectp = mObject; if (!objectp || !mHasParamsClipboard) { return; } LLVOVolume *volobjp = NULL; if (objectp && (objectp->getPCode() == LL_PCODE_VOLUME)) { volobjp = (LLVOVolume *)objectp; } // Light Source if (mPasteLight && volobjp) { if (mParamsClipboard.has("light")) { volobjp->setIsLight(TRUE); volobjp->setLightIntensity((F32)mParamsClipboard["light"]["intensity"].asReal()); volobjp->setLightRadius((F32)mParamsClipboard["light"]["radius"].asReal()); volobjp->setLightFalloff((F32)mParamsClipboard["light"]["falloff"].asReal()); F32 r = (F32)mParamsClipboard["light"]["r"].asReal(); F32 g = (F32)mParamsClipboard["light"]["g"].asReal(); F32 b = (F32)mParamsClipboard["light"]["b"].asReal(); volobjp->setLightColor(LLColor3(r,g,b)); } else { volobjp->setIsLight(FALSE); } if (mParamsClipboard.has("spot")) { volobjp->setLightTextureID(mParamsClipboard["spot"]["id"].asUUID()); LLVector3 spot_params; spot_params.mV[0] = (F32)mParamsClipboard["spot"]["fov"].asReal(); spot_params.mV[1] = (F32)mParamsClipboard["spot"]["focus"].asReal(); spot_params.mV[2] = (F32)mParamsClipboard["spot"]["ambiance"].asReal(); volobjp->setSpotLightParams(spot_params); } } // Physics if (mPastePhysics) { bool is_root = objectp->isRoot(); // Not sure if phantom should go under physics, but doesn't fit elsewhere BOOL is_phantom = mParamsClipboard["is_phantom"].asBoolean() && is_root; LLSelectMgr::getInstance()->selectionUpdatePhantom(is_phantom); BOOL is_physical = mParamsClipboard["is_physical"].asBoolean() && is_root; LLSelectMgr::getInstance()->selectionUpdatePhysics(is_physical); if (mParamsClipboard.has("physics")) { objectp->setPhysicsShapeType((U8)mParamsClipboard["physics"]["shape"].asInteger()); U8 cur_material = objectp->getMaterial(); U8 material = (U8)mParamsClipboard["physics"]["material"].asInteger() | (cur_material & ~LL_MCODE_MASK); objectp->setMaterial(material); objectp->sendMaterialUpdate(); objectp->setPhysicsGravity(mParamsClipboard["physics"]["gravity"].asReal()); objectp->setPhysicsFriction(mParamsClipboard["physics"]["friction"].asReal()); objectp->setPhysicsDensity(mParamsClipboard["physics"]["density"].asReal()); objectp->setPhysicsRestitution(mParamsClipboard["physics"]["restitution"].asReal()); objectp->updateFlags(TRUE); } } if (mPasteFlexible) { bool is_flexible = mParamsClipboard.has("flex"); if (is_flexible) { LLVOVolume *volobjp = (LLVOVolume *)objectp; BOOL update_shape = FALSE; if (!mPasteParametric) { // do before setParameterEntry or it will think that it is already flexi update_shape = volobjp->setIsFlexible(is_flexible); } if (objectp->getClickAction() == CLICK_ACTION_SIT) { objectp->setClickAction(CLICK_ACTION_NONE); } LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE); if (attributes) { LLFlexibleObjectData new_attributes; new_attributes = *attributes; new_attributes.setSimulateLOD(mParamsClipboard["flex"]["lod"].asInteger()); new_attributes.setGravity(mParamsClipboard["flex"]["gav"].asReal()); new_attributes.setTension(mParamsClipboard["flex"]["ten"].asReal()); new_attributes.setAirFriction(mParamsClipboard["flex"]["fri"].asReal()); new_attributes.setWindSensitivity(mParamsClipboard["flex"]["sen"].asReal()); F32 fx = (F32)mParamsClipboard["flex"]["forx"].asReal(); F32 fy = (F32)mParamsClipboard["flex"]["fory"].asReal(); F32 fz = (F32)mParamsClipboard["flex"]["forz"].asReal(); LLVector3 force(fx, fy, fz); new_attributes.setUserForce(force); objectp->setParameterEntry(LLNetworkData::PARAMS_FLEXIBLE, new_attributes, true); } if (!mPasteParametric && update_shape) { mObject->sendShapeUpdate(); LLSelectMgr::getInstance()->selectionUpdatePhantom(volobjp->flagPhantom()); } } else if (!mPasteParametric) { LLVOVolume *volobjp = (LLVOVolume *)objectp; if (volobjp->setIsFlexible(is_flexible)) { mObject->sendShapeUpdate(); LLSelectMgr::getInstance()->selectionUpdatePhantom(volobjp->flagPhantom()); } } } // Parametric does updateVolume(), make sure we won't affect flexible else if (mPasteParametric) { LLVOVolume *volobjp = (LLVOVolume *)objectp; if (volobjp->isFlexible()) { if (mClipboardVolumeParams.getPathParams().getCurveType() == LL_PCODE_PATH_LINE) { mClipboardVolumeParams.getPathParams().setCurveType(LL_PCODE_PATH_FLEXIBLE); } } else if (mClipboardVolumeParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE) { mClipboardVolumeParams.getPathParams().setCurveType(LL_PCODE_PATH_LINE); } } // Parametrics if(mPasteParametric) { // Sculpted Prim if (mParamsClipboard.has("sculpt")) { LLSculptParams sculpt_params; LLUUID sculpt_id = mParamsClipboard["sculpt"]["id"].asUUID(); U8 sculpt_type = (U8)mParamsClipboard["sculpt"]["type"].asInteger(); sculpt_params.setSculptTexture(sculpt_id, sculpt_type); objectp->setParameterEntry(LLNetworkData::PARAMS_SCULPT, sculpt_params, TRUE); } else { LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT); if (sculpt_params) { objectp->setParameterEntryInUse(LLNetworkData::PARAMS_SCULPT, FALSE, TRUE); } } objectp->updateVolume(mClipboardVolumeParams); } } bool LLPanelObject::pasteCheckMenuItem(const LLSD& userdata) { std::string command = userdata.asString(); if ("Parametric" == command) { return mPasteParametric; } if ("Flexible" == command) { return mPasteFlexible; } if ("Physics" == command) { return mPastePhysics; } if ("Light" == command) { return mPasteLight; } return false; } void LLPanelObject::pasteDoMenuItem(const LLSD& userdata) { std::string command = userdata.asString(); if ("Parametric" == command) { mPasteParametric = !mPasteParametric; } if ("Flexible" == command) { mPasteFlexible = !mPasteFlexible; } if ("Physics" == command) { mPastePhysics = !mPastePhysics; } if ("Light" == command) { mPasteLight = !mPasteLight; } } bool LLPanelObject::pasteEnabletMenuItem(const LLSD& userdata) { std::string command = userdata.asString(); // Keep at least one option enabled if (mPasteParametric + mPasteFlexible + mPastePhysics + mPasteLight == 1) { if ("Parametric" == command && mPasteParametric) { return false; } if ("Flexible" == command && mPasteFlexible) { return false; } if ("Physics" == command && mPastePhysics) { return false; } if ("Light" == command && mPasteLight) { return false; } } return true; } //static bool LLPanelObject::isLibraryTexture(LLUUID image_id) { if (gInventory.isObjectDescendentOf(image_id, gInventory.getLibraryRootFolderID()) || image_id == LLUUID(gSavedSettings.getString("DefaultObjectTexture")) || image_id == LLUUID(gSavedSettings.getString("UIImgWhiteUUID")) || image_id == LLUUID(gSavedSettings.getString("UIImgInvisibleUUID")) || image_id == LLUUID(SCULPT_DEFAULT_TEXTURE)) { return true; } return false; } //static LLUUID LLPanelObject::getCopyPermInventoryTextureId(LLUUID image_id) { LLViewerInventoryCategory::cat_array_t cats; LLViewerInventoryItem::item_array_t items; LLAssetIDMatches asset_id_matches(image_id); gInventory.collectDescendentsIf(LLUUID::null, cats, items, LLInventoryModel::INCLUDE_TRASH, asset_id_matches); if (items.size()) { for (S32 i = 0; i < items.size(); i++) { LLViewerInventoryItem* itemp = items[i]; if (itemp) { LLPermissions item_permissions = itemp->getPermissions(); if (item_permissions.allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) { return itemp->getUUID(); } } } } return LLUUID::null; } // Static bool LLPanelObject::canCopyTexture(LLUUID image_id) { // User is allowed to copy a texture if: // library asset or default texture, // or copy perm asset exists in user's inventory return isLibraryTexture(image_id) || getCopyPermInventoryTextureId(image_id).notNull(); }