/** * @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 "llerror.h" #include "llfontgl.h" #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 "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 }; const F32 MAX_ATTACHMENT_DIST = 3.5f; // meters //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 mMenuClipboardPos = getChild("clipboard_pos_btn"); 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 mMenuClipboardSize = getChild("clipboard_size_btn"); 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 mMenuClipboardRot = getChild("clipboard_rot_btn"); 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); //-------------------------------------------------------- // Base Type mComboBaseType = getChild("comboBaseType"); childSetCommitCallback("comboBaseType",onCommitParametric,this); mMenuClipboardParams = getChild("clipboard_obj_params_btn"); // 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); 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), mHasClipboardPos(false), mHasClipboardSize(false), mHasClipboardRot(false), mSizeChanged(FALSE) { mCommitCallbackRegistrar.add("PanelObject.menuDoToSelected", boost::bind(&LLPanelObject::menuDoToSelected, this, _2)); mEnableCallbackRegistrar.add("PanelObject.menuEnable", boost::bind(&LLPanelObject::menuEnableItem, 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); } mMenuClipboardPos->setEnabled(enable_move); mLabelPosition->setEnabled( enable_move ); mCtrlPosX->setEnabled(enable_move); mCtrlPosY->setEnabled(enable_move); mCtrlPosZ->setEnabled(enable_move); 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); } mMenuClipboardSize->setEnabled(enable_scale); mLabelSize->setEnabled( enable_scale ); mCtrlScaleX->setEnabled( enable_scale ); mCtrlScaleY->setEnabled( enable_scale ); mCtrlScaleZ->setEnabled( enable_scale ); 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); } mMenuClipboardRot->setEnabled(enable_rotate); mLabelRotation->setEnabled( enable_rotate ); mCtrlRotX->setEnabled( enable_rotate ); mCtrlRotY->setEnabled( enable_rotate ); mCtrlRotZ->setEnabled( enable_rotate ); 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("FloaterTools") << "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 && single_volume) { 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 ); mMenuClipboardParams->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); mMenuClipboardParams->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("FloaterTools") << "update physics sent" << LL_ENDL; } else { LL_INFOS("FloaterTools") << "update physics not changed" << LL_ENDL; } } void LLPanelObject::sendIsTemporary() { BOOL value = mCheckTemporary->get(); if( mIsTemporary != value ) { LLSelectMgr::getInstance()->selectionUpdateTemporary(value); mIsTemporary = value; LL_INFOS("FloaterTools") << "update temporary sent" << LL_ENDL; } else { LL_INFOS("FloaterTools") << "update temporary not changed" << LL_ENDL; } } void LLPanelObject::sendIsPhantom() { BOOL value = mCheckPhantom->get(); if( mIsPhantom != value ) { LLSelectMgr::getInstance()->selectionUpdatePhantom(value); mIsPhantom = value; LL_INFOS("FloaterTools") << "update phantom sent" << LL_ENDL; } else { LL_INFOS("FloaterTools") << "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("FloaterTools") << "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(); if (!regionp) return; if (!mObject->isAttachment()) { // 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 ( 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); } } else { if (newpos.length() > MAX_ATTACHMENT_DIST) { newpos.clampLength(MAX_ATTACHMENT_DIST); mCtrlPosX->set(newpos.mV[VX]); mCtrlPosY->set(newpos.mV[VY]); mCtrlPosZ->set(newpos.mV[VZ]); } } // 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); bool is_valid_pos = true; if (mObject->isAttachment()) { LLVector3 delta_pos = mObject->getPositionEdit() - newpos; LLVector3d attachment_pos = regionp->getPosGlobalFromRegion(mObject->getPositionRegion() + delta_pos); is_valid_pos = LLWorld::getInstance()->positionRegionValidGlobal(attachment_pos); } else { is_valid_pos = LLWorld::getInstance()->positionRegionValidGlobal(new_pos_global); } if (is_valid_pos) { // 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::menuDoToSelected(const LLSD& userdata) { std::string command = userdata.asString(); // paste if (command == "psr_paste") { onPastePos(); onPasteSize(); onPasteRot(); } else if (command == "pos_paste") { onPastePos(); } else if (command == "size_paste") { onPasteSize(); } else if (command == "rot_paste") { onPasteRot(); } else if (command == "params_paste") { onPasteParams(); } // copy else if (command == "psr_copy") { onCopyPos(); onCopySize(); onCopyRot(); } else if (command == "pos_copy") { onCopyPos(); } else if (command == "size_copy") { onCopySize(); } else if (command == "rot_copy") { onCopyRot(); } else if (command == "params_copy") { onCopyParams(); } } bool LLPanelObject::menuEnableItem(const LLSD& userdata) { std::string command = userdata.asString(); // paste options if (command == "psr_paste") { S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode(LL_PCODE_VOLUME)) && (selected_count == 1); if (!single_volume) { return false; } bool enable_move; bool enable_modify; LLSelectMgr::getInstance()->selectGetEditMoveLinksetPermissions(enable_move, enable_modify); return enable_move && enable_modify && mHasClipboardPos && mHasClipboardSize && mHasClipboardRot; } else if (command == "pos_paste") { // assumes that menu won't be active if there is no move permission return mHasClipboardPos; } else if (command == "size_paste") { return mHasClipboardSize; } else if (command == "rot_paste") { return mHasClipboardRot; } else if (command == "params_paste") { return mClipboardParams.isMap() && (mClipboardParams.size() != 0); } // copy options else if (command == "psr_copy") { S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode(LL_PCODE_VOLUME)) && (selected_count == 1); if (!single_volume) { return false; } bool enable_move; bool enable_modify; LLSelectMgr::getInstance()->selectGetEditMoveLinksetPermissions(enable_move, enable_modify); // since we forbid seeing values we also should forbid copying them return enable_move && enable_modify; } return false; } 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)); mHasClipboardPos = 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)); mHasClipboardSize = 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)); mHasClipboardRot = true; } void LLPanelObject::onPastePos() { if (!mHasClipboardPos) return; if (mObject.isNull()) return; LLViewerRegion* regionp = mObject->getRegion(); if (!regionp) return; // Clamp pos on non-attachments, just keep the prims within the region if (!mObject->isAttachment()) { F32 max_width = regionp->getWidth(); // meters mClipboardPos.mV[VX] = llclamp(mClipboardPos.mV[VX], 0.f, max_width); mClipboardPos.mV[VY] = llclamp(mClipboardPos.mV[VY], 0.f, max_width); //height will get properly clamped by sendPosition } else { mClipboardPos.clampLength(MAX_ATTACHMENT_DIST); } mCtrlPosX->set( mClipboardPos.mV[VX] ); mCtrlPosY->set( mClipboardPos.mV[VY] ); mCtrlPosZ->set( mClipboardPos.mV[VZ] ); sendPosition(FALSE); } void LLPanelObject::onPasteSize() { if (!mHasClipboardSize) 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 (!mHasClipboardRot) 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 || objectp->isMesh()) { return; } mClipboardParams.clear(); // Parametrics LLVolumeParams params; getVolumeParams(params); mClipboardParams["volume_params"] = params.asLLSD(); // Sculpted Prim if (objectp->getParameterEntryInUse(LLNetworkData::PARAMS_SCULPT)) { LLSculptParams *sculpt_params = (LLSculptParams *)objectp->getParameterEntry(LLNetworkData::PARAMS_SCULPT); LLUUID texture_id = sculpt_params->getSculptTexture(); if (get_can_copy_texture(texture_id)) { LL_DEBUGS("FloaterTools") << "Recording texture" << LL_ENDL; mClipboardParams["sculpt"]["id"] = texture_id; } else { mClipboardParams["sculpt"]["id"] = LLUUID(SCULPT_DEFAULT_TEXTURE); } mClipboardParams["sculpt"]["type"] = sculpt_params->getSculptType(); } } void LLPanelObject::onPasteParams() { LLViewerObject* objectp = mObject; if (!objectp) { return; } // Sculpted Prim if (mClipboardParams.has("sculpt")) { LLSculptParams sculpt_params; LLUUID sculpt_id = mClipboardParams["sculpt"]["id"].asUUID(); U8 sculpt_type = (U8)mClipboardParams["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); } } // volume params // make sure updateVolume() won't affect flexible if (mClipboardParams.has("volume_params")) { LLVolumeParams params; params.fromLLSD(mClipboardParams["volume_params"]); LLVOVolume *volobjp = (LLVOVolume *)objectp; if (volobjp->isFlexible()) { if (params.getPathParams().getCurveType() == LL_PCODE_PATH_LINE) { params.getPathParams().setCurveType(LL_PCODE_PATH_FLEXIBLE); } } else if (params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE) { params.getPathParams().setCurveType(LL_PCODE_PATH_LINE); } objectp->updateVolume(params); } }