/** * @file llvoavatar.cpp * @brief Implementation of LLVOAvatar class which is a derivation fo LLViewerObject * * $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" #include "llvoavatarself.h" #include "llvoavatar.h" #include "pipeline.h" #include "llagent.h" // Get state values from here #include "llattachmentsmgr.h" #include "llagentcamera.h" #include "llagentwearables.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" #include "llinventoryfunctions.h" #include "lllocaltextureobject.h" #include "llnotificationsutil.h" #include "llselectmgr.h" #include "lltoolgrab.h" // for needsRenderBeam #include "lltoolmgr.h" // for needsRenderBeam #include "lltoolmorph.h" #include "lltrans.h" #include "llviewercamera.h" #include "llviewercontrol.h" #include "llviewermenu.h" #include "llviewerobjectlist.h" #include "llviewerstats.h" #include "llviewerregion.h" #include "llviewertexlayer.h" #include "llviewerwearable.h" #include "llappearancemgr.h" #include "llmeshrepository.h" #include "llvovolume.h" #include "llsdutil.h" #include "llstartup.h" #include "llsdserialize.h" #include "llcorehttputil.h" #include "lluiusage.h" // Firestorm includes #include "fslslbridge.h" #include "lggbeammaps.h" #include "piemenu.h" #include "pieslice.h" #include "pieseparator.h" // [RLVa:KB] - Checked: RLVa-2.0.2 #include "rlvhandler.h" #include "rlvhelper.h" #include "rlvlocks.h" // [/RLVa:KB] // [Legacy Bake] #include "llviewernetwork.h" // [Legacy Bake] #include LLPointer gAgentAvatarp = NULL; bool isAgentAvatarValid() { return (gAgentAvatarp.notNull() && gAgentAvatarp->isValid()); } void selfStartPhase(const std::string& phase_name) { if (isAgentAvatarValid()) { gAgentAvatarp->startPhase(phase_name); } } void selfStopPhase(const std::string& phase_name, bool err_check) { if (isAgentAvatarValid()) { gAgentAvatarp->stopPhase(phase_name, err_check); } } void selfClearPhases() { if (isAgentAvatarValid()) { gAgentAvatarp->clearPhases(); } } using namespace LLAvatarAppearanceDefines; LLSD summarize_by_buckets(std::vector in_records, std::vector by_fields, std::string val_field); /********************************************************************************* ** ** ** Begin private LLVOAvatarSelf Support classes ** **/ struct LocalTextureData { LocalTextureData() : mIsBakedReady(false), mDiscard(MAX_DISCARD_LEVEL+1), mImage(NULL), mWearableID(IMG_DEFAULT_AVATAR), mTexEntry(NULL) {} LLPointer mImage; bool mIsBakedReady; S32 mDiscard; LLUUID mWearableID; // UUID of the wearable that this texture belongs to, not of the image itself LLTextureEntry *mTexEntry; }; //----------------------------------------------------------------------------- // Callback data //----------------------------------------------------------------------------- /** ** ** End LLVOAvatarSelf Support classes ** ** *********************************************************************************/ //----------------------------------------------------------------------------- // Static Data //----------------------------------------------------------------------------- S32Bytes LLVOAvatarSelf::sScratchTexBytes(0); std::map< LLGLenum, LLGLuint*> LLVOAvatarSelf::sScratchTexNames; /********************************************************************************* ** ** ** Begin LLVOAvatarSelf Constructor routines ** **/ LLVOAvatarSelf::LLVOAvatarSelf(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) : LLVOAvatar(id, pcode, regionp), // [RLVa:KB] - Checked: 2012-07-28 (RLVa-1.4.7) mAttachmentSignal(NULL), // [/RLVa:KB] mScreenp(NULL), mLastRegionHandle(0), mRegionCrossingCount(0), mIsCrossingRegion(false), // FIRE-12004: Attachments getting lost on TP // Value outside legal range, so will always be a mismatch the // first time through. mLastHoverOffsetSent(LLVector3(0.0f, 0.0f, -999.0f)), mInitialMetric(true), mMetricSequence(0) { // [Legacy Bake] #ifdef OPENSIM if (!LLGridManager::getInstance()->isInSecondLife()) { gAgentWearables.setAvatarObject(this); } #endif // [Legacy Bake] mMotionController.mIsSelf = true; LL_DEBUGS() << "Marking avatar as self " << id << LL_ENDL; } // Called periodically for diagnostics, return true when done. bool output_self_av_texture_diagnostics() { if (!isAgentAvatarValid()) return true; // done checking gAgentAvatarp->outputRezDiagnostics(); return false; } bool update_avatar_rez_metrics() { if (!isAgentAvatarValid()) return true; gAgentAvatarp->updateAvatarRezMetrics(false); return false; } // [Legacy Bake] bool check_for_unsupported_baked_appearance() { if (!isAgentAvatarValid()) return true; gAgentAvatarp->checkForUnsupportedServerBakeAppearance(); return false; } // [Legacy Bake] void LLVOAvatarSelf::initInstance() { bool status = true; // creates hud joint(mScreen) among other things status &= loadAvatarSelf(); // adds attachment points to mScreen among other things LLVOAvatar::initInstance(); LL_INFOS() << "Self avatar object created. Starting timer." << LL_ENDL; mDebugSelfLoadTimer.reset(); // clear all times to -1 for debugging for (U32 i =0; i < LLAvatarAppearanceDefines::TEX_NUM_INDICES; ++i) { for (U32 j = 0; j <= MAX_DISCARD_LEVEL; ++j) { mDebugTextureLoadTimes[i][j] = -1.0f; } } for (U32 i =0; i < LLAvatarAppearanceDefines::BAKED_NUM_INDICES; ++i) { mDebugBakedTextureTimes[i][0] = -1.0f; mDebugBakedTextureTimes[i][1] = -1.0f; } // [RLVa:KB] - Checked: 2010-12-12 (RLVa-1.2.2c) | Added: RLVa-1.2.2c RlvAttachPtLookup::initLookupTable(); // [/RLVa:KB] status &= buildMenus(); if (!status) { LL_ERRS() << "Unable to load user's avatar" << LL_ENDL; return; } setHoverIfRegionEnabled(); //doPeriodically(output_self_av_texture_diagnostics, 30.0); doPeriodically(update_avatar_rez_metrics, 5.0); // [Legacy Bake] doPeriodically(check_for_unsupported_baked_appearance, 120.0); doPeriodically(boost::bind(&LLVOAvatarSelf::checkStuckAppearance, this), 30.0); mInitFlags |= 1<<2; } void LLVOAvatarSelf::setHoverIfRegionEnabled() { if (getRegion() && getRegion()->simulatorFeaturesReceived()) { if (getRegion()->avatarHoverHeightEnabled()) { F32 hover_z = gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ"); setHoverOffset(LLVector3(0.0, 0.0, llclamp(hover_z,MIN_HOVER_Z,MAX_HOVER_Z))); LL_INFOS("Avatar") << avString() << " set hover height from debug setting " << hover_z << LL_ENDL; } // [Legacy bake] else if (!isUsingServerBakes()) { computeBodySize(); } // [Legacy bake] else { setHoverOffset(LLVector3(0.0, 0.0, 0.0)); LL_INFOS("Avatar") << avString() << " zeroing hover height, region does not support" << LL_ENDL; } } else { LL_INFOS("Avatar") << avString() << " region or simulator features not known, no change on hover" << LL_ENDL; if (getRegion()) { getRegion()->setSimulatorFeaturesReceivedCallback(boost::bind(&LLVOAvatarSelf::onSimulatorFeaturesReceived,this,_1)); } } } bool LLVOAvatarSelf::checkStuckAppearance() { const F32 CONDITIONAL_UNSTICK_INTERVAL = 300.0; const F32 UNCONDITIONAL_UNSTICK_INTERVAL = 600.0; if (gAgentWearables.isCOFChangeInProgress()) { LL_DEBUGS("Avatar") << "checking for stuck appearance" << LL_ENDL; F32 change_time = gAgentWearables.getCOFChangeTime(); LL_DEBUGS("Avatar") << "change in progress for " << change_time << " seconds" << LL_ENDL; S32 active_hp = LLAppearanceMgr::instance().countActiveHoldingPatterns(); LL_DEBUGS("Avatar") << "active holding patterns " << active_hp << " seconds" << LL_ENDL; S32 active_copies = LLAppearanceMgr::instance().getActiveCopyOperations(); LL_DEBUGS("Avatar") << "active copy operations " << active_copies << LL_ENDL; if ((change_time > CONDITIONAL_UNSTICK_INTERVAL && active_copies == 0) || (change_time > UNCONDITIONAL_UNSTICK_INTERVAL)) { gAgentWearables.notifyLoadingFinished(); } } // Return false to continue running check periodically. return LLApp::isExiting(); } // virtual void LLVOAvatarSelf::markDead() { mBeam = NULL; LLVOAvatar::markDead(); } /*virtual*/ bool LLVOAvatarSelf::loadAvatar() { bool success = LLVOAvatar::loadAvatar(); // set all parameters stored directly in the avatar to have // the isSelfParam to be true - this is used to prevent // them from being animated or trigger accidental rebakes // when we copy params from the wearable to the base avatar. for (LLViewerVisualParam* param = (LLViewerVisualParam*) getFirstVisualParam(); param; param = (LLViewerVisualParam*) getNextVisualParam()) { if (param->getWearableType() != LLWearableType::WT_INVALID) { param->setIsDummy(true); } } return success; } bool LLVOAvatarSelf::loadAvatarSelf() { bool success = true; // avatar_skeleton.xml if (!buildSkeletonSelf(sAvatarSkeletonInfo)) { LL_WARNS() << "avatar file: buildSkeleton() failed" << LL_ENDL; return false; } return success; } bool LLVOAvatarSelf::buildSkeletonSelf(const LLAvatarSkeletonInfo *info) { // add special-purpose "screen" joint mScreenp = new LLViewerJoint("mScreen", NULL); // for now, put screen at origin, as it is only used during special // HUD rendering mode F32 aspect = LLViewerCamera::getInstance()->getAspect(); LLVector3 scale(1.f, aspect, 1.f); mScreenp->setScale(scale); // SL-315 mScreenp->setWorldPosition(LLVector3::zero); // need to update screen agressively when sidebar opens/closes, for example mScreenp->mUpdateXform = true; return true; } bool LLVOAvatarSelf::buildMenus() { //------------------------------------------------------------------------- // build the attach and detach menus //------------------------------------------------------------------------- gAttachBodyPartPieMenus[0] = NULL; LLContextMenu::Params params; params.label(LLTrans::getString("BodyPartsRightArm")); params.name(params.label); params.visible(false); gAttachBodyPartPieMenus[1] = LLUICtrlFactory::create (params); params.label(LLTrans::getString("BodyPartsHead")); params.name(params.label); gAttachBodyPartPieMenus[2] = LLUICtrlFactory::create (params); params.label(LLTrans::getString("BodyPartsLeftArm")); params.name(params.label); gAttachBodyPartPieMenus[3] = LLUICtrlFactory::create (params); gAttachBodyPartPieMenus[4] = NULL; params.label(LLTrans::getString("BodyPartsLeftLeg")); params.name(params.label); gAttachBodyPartPieMenus[5] = LLUICtrlFactory::create (params); params.label(LLTrans::getString("BodyPartsTorso")); params.name(params.label); gAttachBodyPartPieMenus[6] = LLUICtrlFactory::create (params); params.label(LLTrans::getString("BodyPartsRightLeg")); params.name(params.label); gAttachBodyPartPieMenus[7] = LLUICtrlFactory::create (params); params.label(LLTrans::getString("BodyPartsEnhancedSkeleton")); params.name(params.label); gAttachBodyPartPieMenus[8] = LLUICtrlFactory::create(params); gDetachBodyPartPieMenus[0] = NULL; params.label(LLTrans::getString("BodyPartsRightArm")); params.name(params.label); gDetachBodyPartPieMenus[1] = LLUICtrlFactory::create (params); params.label(LLTrans::getString("BodyPartsHead")); params.name(params.label); gDetachBodyPartPieMenus[2] = LLUICtrlFactory::create (params); params.label(LLTrans::getString("BodyPartsLeftArm")); params.name(params.label); gDetachBodyPartPieMenus[3] = LLUICtrlFactory::create (params); gDetachBodyPartPieMenus[4] = NULL; params.label(LLTrans::getString("BodyPartsLeftLeg")); params.name(params.label); gDetachBodyPartPieMenus[5] = LLUICtrlFactory::create (params); params.label(LLTrans::getString("BodyPartsTorso")); params.name(params.label); gDetachBodyPartPieMenus[6] = LLUICtrlFactory::create (params); params.label(LLTrans::getString("BodyPartsRightLeg")); params.name(params.label); gDetachBodyPartPieMenus[7] = LLUICtrlFactory::create (params); params.label(LLTrans::getString("BodyPartsEnhancedSkeleton")); params.name(params.label); gDetachBodyPartPieMenus[8] = LLUICtrlFactory::create(params); // Pie menu //------------------------------------------------------------------------- // build the attach and detach pie menus //------------------------------------------------------------------------- PieMenu::Params pieParams; pieParams.visible(false); gPieAttachBodyPartMenus[0] = NULL; pieParams.label(LLTrans::getString("BodyPartsRightArm")); pieParams.name(pieParams.label); gPieAttachBodyPartMenus[1] = LLUICtrlFactory::create (pieParams); pieParams.label(LLTrans::getString("BodyPartsHead")); pieParams.name(pieParams.label); gPieAttachBodyPartMenus[2] = LLUICtrlFactory::create (pieParams); pieParams.label(LLTrans::getString("BodyPartsLeftArm")); pieParams.name(pieParams.label); gPieAttachBodyPartMenus[3] = LLUICtrlFactory::create (pieParams); gPieAttachBodyPartMenus[4] = NULL; pieParams.label(LLTrans::getString("BodyPartsLeftLeg")); pieParams.name(pieParams.label); gPieAttachBodyPartMenus[5] = LLUICtrlFactory::create (pieParams); pieParams.label(LLTrans::getString("BodyPartsTorso")); pieParams.name(pieParams.label); gPieAttachBodyPartMenus[6] = LLUICtrlFactory::create (pieParams); pieParams.label(LLTrans::getString("BodyPartsRightLeg")); pieParams.name(pieParams.label); gPieAttachBodyPartMenus[7] = LLUICtrlFactory::create (pieParams); gPieDetachBodyPartMenus[0] = NULL; pieParams.label(LLTrans::getString("BodyPartsRightArm")); pieParams.name(pieParams.label); gPieDetachBodyPartMenus[1] = LLUICtrlFactory::create (pieParams); pieParams.label(LLTrans::getString("BodyPartsHead")); pieParams.name(pieParams.label); gPieDetachBodyPartMenus[2] = LLUICtrlFactory::create (pieParams); pieParams.label(LLTrans::getString("BodyPartsLeftArm")); pieParams.name(pieParams.label); gPieDetachBodyPartMenus[3] = LLUICtrlFactory::create (pieParams); gPieDetachBodyPartMenus[4] = NULL; pieParams.label(LLTrans::getString("BodyPartsLeftLeg")); pieParams.name(pieParams.label); gPieDetachBodyPartMenus[5] = LLUICtrlFactory::create (pieParams); pieParams.label(LLTrans::getString("BodyPartsTorso")); pieParams.name(pieParams.label); gPieDetachBodyPartMenus[6] = LLUICtrlFactory::create (pieParams); pieParams.label(LLTrans::getString("BodyPartsRightLeg")); pieParams.name(pieParams.label); gPieDetachBodyPartMenus[7] = LLUICtrlFactory::create (pieParams); // Pie menu for (S32 i = 0; i < 9; i++) { if (gAttachBodyPartPieMenus[i]) { gAttachPieMenu->appendContextSubMenu( gAttachBodyPartPieMenus[i] ); } else { for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment* attachment = iter->second; if (attachment && attachment->getGroup() == i) { LLMenuItemCallGL::Params item_params; std::string sub_piemenu_name = attachment->getName(); if (LLTrans::getString(sub_piemenu_name) != "") { item_params.label = LLTrans::getString(sub_piemenu_name); } else { item_params.label = sub_piemenu_name; } item_params.name =(item_params.label ); item_params.on_click.function_name = "Object.AttachToAvatar"; item_params.on_click.parameter = iter->first; // [RLVa:KB] - No changes, but we do need the parameter to always be idxAttachPt for object_selected_and_point_valid() item_params.on_enable.function_name = "Object.EnableWear"; item_params.on_enable.parameter = iter->first; LLMenuItemCallGL* item = LLUICtrlFactory::create(item_params); gAttachPieMenu->addChild(item); break; } } } if (gDetachBodyPartPieMenus[i]) { gDetachPieMenu->appendContextSubMenu( gDetachBodyPartPieMenus[i] ); gDetachAttSelfMenu->appendContextSubMenu(gDetachBodyPartPieMenus[i]); gDetachAvatarMenu->appendContextSubMenu(gDetachBodyPartPieMenus[i]); // FIRE-7893: "Detach" sub-menu on inspect menu without function gInspectSelfDetachMenu->appendContextSubMenu( gDetachBodyPartPieMenus[i] ); } else { for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment* attachment = iter->second; if (attachment && attachment->getGroup() == i) { LLMenuItemCallGL::Params item_params; std::string sub_piemenu_name = attachment->getName(); if (LLTrans::getString(sub_piemenu_name) != "") { item_params.label = LLTrans::getString(sub_piemenu_name); } else { item_params.label = sub_piemenu_name; } item_params.name =(item_params.label ); item_params.on_click.function_name = "Attachment.DetachFromPoint"; item_params.on_click.parameter = iter->first; item_params.on_enable.function_name = "Attachment.PointFilled"; item_params.on_enable.parameter = iter->first; LLMenuItemCallGL* item = LLUICtrlFactory::create(item_params); gDetachPieMenu->addChild(item); gDetachAttSelfMenu->addChild(LLUICtrlFactory::create(item_params)); gDetachAvatarMenu->addChild(LLUICtrlFactory::create(item_params)); // FIRE-7893: "Detach" sub-menu on inspect menu without function gInspectSelfDetachMenu->addChild(item); break; } } } } // Pie menu for (S32 i = 0; i < PIE_MAX_SLICES; i++) { // Skip former slices of left and right hand that have been moved into the // corresponding Left/Right arm groups; // 0 is Bento enhanced skeleton now, manually added in menu_pie_avatar_self.xml // and menu_pie_object.xml if (i == 0) { continue; } else if (i == 4) { PieSeparator::Params pie_sep_params; pieParams.name("AttachSlot4Separator"); gPieAttachMenu->addChild(LLUICtrlFactory::create(pie_sep_params)); pieParams.name("DetachSlot4Separator"); gPieDetachMenu->addChild(LLUICtrlFactory::create(pie_sep_params)); continue; } if (gPieAttachBodyPartMenus[i]) { gPieAttachMenu->appendContextSubMenu(gPieAttachBodyPartMenus[i]); } else { for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment* attachment = iter->second; if (attachment && attachment->getGroup() == i) { PieSlice::Params slice_params; std::string sub_piemenu_name = attachment->getName(); if (LLTrans::getString(sub_piemenu_name) != "") { slice_params.label = LLTrans::getString(sub_piemenu_name); } else { slice_params.label = sub_piemenu_name; } slice_params.name = (slice_params.label); slice_params.on_click.function_name = "Object.AttachToAvatar"; slice_params.on_click.parameter = iter->first; slice_params.on_enable.function_name = "Object.EnableWear"; slice_params.on_enable.parameter = iter->first; PieSlice* slice = LLUICtrlFactory::create(slice_params); gPieAttachMenu->addChild(slice); break; } } } if (gPieDetachBodyPartMenus[i]) { gPieDetachMenu->appendContextSubMenu(gPieDetachBodyPartMenus[i]); } else { for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment* attachment = iter->second; if (attachment && attachment->getGroup() == i) { PieSlice::Params slice_params; std::string sub_piemenu_name = attachment->getName(); if (LLTrans::getString(sub_piemenu_name) != "") { slice_params.label = LLTrans::getString(sub_piemenu_name); } else { slice_params.label = sub_piemenu_name; } slice_params.name = (slice_params.label); slice_params.on_click.function_name = "Attachment.DetachFromPoint"; slice_params.on_click.parameter = iter->first; slice_params.on_enable.function_name = "Attachment.PointFilled"; slice_params.on_enable.parameter = iter->first; PieSlice* slice = LLUICtrlFactory::create(slice_params); gPieDetachMenu->addChild(slice); break; } } } } // Pie menu // add screen attachments for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment* attachment = iter->second; if (attachment && attachment->getGroup() == 9) { LLMenuItemCallGL::Params item_params; PieSlice::Params slice_params; // Pie menu std::string sub_piemenu_name = attachment->getName(); if (LLTrans::getString(sub_piemenu_name) != "") { item_params.label = LLTrans::getString(sub_piemenu_name); slice_params.label = LLTrans::getString(sub_piemenu_name); // Pie menu } else { item_params.label = sub_piemenu_name; slice_params.label = sub_piemenu_name; // Pie menu } item_params.name =(item_params.label ); item_params.on_click.function_name = "Object.AttachToAvatar"; item_params.on_click.parameter = iter->first; // [RLVa:KB] - No changes, but we do need the parameter to always be idxAttachPt for object_selected_and_point_valid() item_params.on_enable.function_name = "Object.EnableWear"; item_params.on_enable.parameter = iter->first; LLMenuItemCallGL* item = LLUICtrlFactory::create(item_params); gAttachScreenPieMenu->addChild(item); item_params.on_click.function_name = "Attachment.DetachFromPoint"; item_params.on_click.parameter = iter->first; item_params.on_enable.function_name = "Attachment.PointFilled"; item_params.on_enable.parameter = iter->first; item = LLUICtrlFactory::create(item_params); gDetachScreenPieMenu->addChild(item); gDetachHUDAttSelfMenu->addChild(LLUICtrlFactory::create(item_params)); gDetachHUDAvatarMenu->addChild(LLUICtrlFactory::create(item_params)); // FIRE-7893: "Detach" sub-menu on inspect menu without function gInspectSelfDetachScreenMenu->addChild(LLUICtrlFactory::create(item_params)); // Pie menu slice_params.name =(slice_params.label ); slice_params.on_click.function_name = "Object.AttachToAvatar"; slice_params.on_click.parameter = iter->first; slice_params.on_enable.function_name = "Object.EnableWear"; slice_params.on_enable.parameter = iter->first; PieSlice* slice = LLUICtrlFactory::create(slice_params); gPieAttachScreenMenu->addChild(slice); slice_params.on_click.function_name = "Attachment.DetachFromPoint"; slice_params.on_click.parameter = iter->first; slice_params.on_enable.function_name = "Attachment.PointFilled"; slice_params.on_enable.parameter = iter->first; slice = LLUICtrlFactory::create(slice_params); gPieDetachScreenMenu->addChild(slice); // Pie menu } } for (S32 pass = 0; pass < 2; pass++) { // *TODO: Skinning - gAttachSubMenu is an awful, awful hack if (!gAttachSubMenu) { break; } for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment* attachment = iter->second; if (attachment->getIsHUDAttachment() != (pass == 1)) { continue; } LLMenuItemCallGL::Params item_params; std::string sub_piemenu_name = attachment->getName(); if (LLTrans::getString(sub_piemenu_name) != "") { item_params.label = LLTrans::getString(sub_piemenu_name); } else { item_params.label = sub_piemenu_name; } item_params.name =(item_params.label ); item_params.on_click.function_name = "Object.AttachToAvatar"; item_params.on_click.parameter = iter->first; // [RLVa:KB] - No changes, but we do need the parameter to always be idxAttachPt for object_selected_and_point_valid() item_params.on_enable.function_name = "Object.EnableWear"; item_params.on_enable.parameter = iter->first; //* TODO: Skinning: //LLSD params; //params["index"] = iter->first; //params["label"] = attachment->getName(); //item->addEventHandler("on_enable", LLMenuItemCallGL::MenuCallback().function_name("Attachment.Label").parameter(params)); LLMenuItemCallGL* item = LLUICtrlFactory::create(item_params); gAttachSubMenu->addChild(item); item_params.on_click.function_name = "Attachment.DetachFromPoint"; item_params.on_click.parameter = iter->first; item_params.on_enable.function_name = "Attachment.PointFilled"; item_params.on_enable.parameter = iter->first; //* TODO: Skinning: item->addEventHandler("on_enable", LLMenuItemCallGL::MenuCallback().function_name("Attachment.Label").parameter(params)); item = LLUICtrlFactory::create(item_params); gDetachSubMenu->addChild(item); } if (pass == 0) { // put separator between non-hud and hud attachments gAttachSubMenu->addSeparator(); gDetachSubMenu->addSeparator(); } } for (S32 group = 0; group < 9; group++) { // skip over groups that don't have sub menus if (!gAttachBodyPartPieMenus[group] || !gDetachBodyPartPieMenus[group]) { continue; } std::multimap attachment_pie_menu_map; // gather up all attachment points assigned to this group, and throw into map sorted by pie slice number for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment* attachment = iter->second; if(attachment && attachment->getGroup() == group) { // use multimap to provide a partial order off of the pie slice key S32 pie_index = attachment->getPieSlice(); attachment_pie_menu_map.insert(std::make_pair(pie_index, iter->first)); } } // add in requested order to pie menu, inserting separators as necessary for (std::multimap::iterator attach_it = attachment_pie_menu_map.begin(); attach_it != attachment_pie_menu_map.end(); ++attach_it) { S32 attach_index = attach_it->second; // Skip bridge attachment spot if (attach_index == 127) { continue; } // LLViewerJointAttachment* attachment = get_if_there(mAttachmentPoints, attach_index, (LLViewerJointAttachment*)NULL); if (attachment) { LLMenuItemCallGL::Params item_params; item_params.name = attachment->getName(); item_params.label = LLTrans::getString(attachment->getName()); item_params.on_click.function_name = "Object.AttachToAvatar"; item_params.on_click.parameter = attach_index; // [RLVa:KB] - No changes, but we do need the parameter to always be idxAttachPt for object_selected_and_point_valid() item_params.on_enable.function_name = "Object.EnableWear"; item_params.on_enable.parameter = attach_index; LLMenuItemCallGL* item = LLUICtrlFactory::create(item_params); gAttachBodyPartPieMenus[group]->addChild(item); item_params.on_click.function_name = "Attachment.DetachFromPoint"; item_params.on_click.parameter = attach_index; item_params.on_enable.function_name = "Attachment.PointFilled"; item_params.on_enable.parameter = attach_index; item = LLUICtrlFactory::create(item_params); gDetachBodyPartPieMenus[group]->addChild(item); } } } // Pie menu for (S32 group = 0; group < PIE_MAX_SLICES; group++) { // skip over groups that don't have sub menus if (!gPieAttachBodyPartMenus[group] || !gPieDetachBodyPartMenus[group]) { continue; } std::multimap attachment_pie_menu_map; // gather up all attachment points assigned to this group, and throw into map sorted by pie slice number for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment* attachment = iter->second; if(attachment && attachment->getGroup() == group) { // use multimap to provide a partial order off of the pie slice key S32 pie_index = attachment->getPieSlice(); attachment_pie_menu_map.insert(std::make_pair(pie_index, iter->first)); } } // add in requested order to pie menu, inserting separators as necessary for (std::multimap::iterator attach_it = attachment_pie_menu_map.begin(); attach_it != attachment_pie_menu_map.end(); ++attach_it) { S32 attach_index = attach_it->second; // Skip bridge attachment spot if (attach_index == 127) { continue; } // LLViewerJointAttachment* attachment = get_if_there(mAttachmentPoints, attach_index, (LLViewerJointAttachment*)NULL); if (attachment) { PieSlice::Params slice_params; slice_params.name = attachment->getName(); slice_params.label = LLTrans::getString(attachment->getName()); slice_params.on_click.function_name = "Object.AttachToAvatar"; slice_params.on_click.parameter = attach_index; slice_params.on_enable.function_name = "Object.EnableWear"; slice_params.on_enable.parameter = attach_index; PieSlice* slice = LLUICtrlFactory::create(slice_params); gPieAttachBodyPartMenus[group]->addChild(slice); slice_params.on_click.function_name = "Attachment.DetachFromPoint"; slice_params.on_click.parameter = attach_index; slice_params.on_enable.function_name = "Attachment.PointFilled"; slice_params.on_enable.parameter = attach_index; slice = LLUICtrlFactory::create(slice_params); gPieDetachBodyPartMenus[group]->addChild(slice); } } } // Pie menu return true; } void LLVOAvatarSelf::cleanup() { markDead(); delete mScreenp; mScreenp = NULL; mRegionp = NULL; } LLVOAvatarSelf::~LLVOAvatarSelf() { cleanup(); // [RLVa:KB] - Checked: 2012-07-28 (RLVa-1.4.7) delete mAttachmentSignal; // [/RLVa:KB] } /** ** ** End LLVOAvatarSelf Constructor routines ** ** *********************************************************************************/ // virtual bool LLVOAvatarSelf::updateCharacter(LLAgent &agent) { // update screen joint size if (mScreenp) { F32 aspect = LLViewerCamera::getInstance()->getAspect(); LLVector3 scale(1.f, aspect, 1.f); mScreenp->setScale(scale); mScreenp->updateWorldMatrixChildren(); resetHUDAttachments(); } return LLVOAvatar::updateCharacter(agent); } // virtual bool LLVOAvatarSelf::isValid() const { // Skip unregistering attachments during shutdown //return ((getRegion() != NULL) && !isDead()); return ((getRegion() != NULL) && !isDead() && !LLApp::isExiting()); } // virtual void LLVOAvatarSelf::idleUpdate(LLAgent &agent, const F64 &time) { if (isAgentAvatarValid()) { LLVOAvatar::idleUpdate(agent, time); idleUpdateTractorBeam(); // Make sure to definitely stop typing if ((gAgent.getTypingTime() > LLAgent::TYPING_TIMEOUT_SECS) && (gAgent.getRenderState() & AGENT_STATE_TYPING)) { gAgent.stopTyping(); } // } } // virtual // Query by JointKey rather than just a string, the key can be a U32 index for faster lookup //LLJoint *LLVOAvatarSelf::getJoint( const std::string &name ) LLJoint *LLVOAvatarSelf::getJoint( const JointKey &name ) // { LLJoint *jointp = NULL; jointp = LLVOAvatar::getJoint(name); if (!jointp && mScreenp) { // Query by JointKey rather than just a string, the key can be a U32 index for faster lookup //jointp = mScreenp->findJoint(name); jointp = mScreenp->findJoint(name.mName); // if (jointp) { // Query by JointKey rather than just a string, the key can be a U32 index for faster lookup //mJointMap[name] = jointp; mJointMap[name.mKey] = jointp; // } } if (jointp && jointp != mScreenp && jointp != mRoot) { llassert(LLVOAvatar::getJoint((S32)jointp->getJointNum())==jointp); } return jointp; } // virtual // [Legacy Bake] //bool LLVOAvatarSelf::setVisualParamWeight(const LLVisualParam *which_param, F32 weight) bool LLVOAvatarSelf::setVisualParamWeight(const LLVisualParam *which_param, F32 weight, bool upload_bake) { if (!which_param) { return false; } LLViewerVisualParam *param = (LLViewerVisualParam*) LLCharacter::getVisualParam(which_param->getID()); // [Legacy Bake] //return setParamWeight(param,weight); return setParamWeight(param,weight,upload_bake); } // virtual // [Legacy Bake] //bool LLVOAvatarSelf::setVisualParamWeight(const char* param_name, F32 weight) bool LLVOAvatarSelf::setVisualParamWeight(const char* param_name, F32 weight, bool upload_bake) { if (!param_name) { return false; } LLViewerVisualParam *param = (LLViewerVisualParam*) LLCharacter::getVisualParam(param_name); // [Legacy Bake] //return setParamWeight(param,weight); return setParamWeight(param,weight,upload_bake); } // virtual // [Legacy Bake] //bool LLVOAvatarSelf::setVisualParamWeight(const LLViewerVisualParam *param, F32 weight) bool LLVOAvatarSelf::setVisualParamWeight(S32 index, F32 weight, bool upload_bake) { LLViewerVisualParam *param = (LLViewerVisualParam*) LLCharacter::getVisualParam(index); // [Legacy Bake] //return setParamWeight(param,weight); return setParamWeight(param,weight,upload_bake); } // [Legacy Bake] //bool LLVOAvatarSelf::setParamWeight(const LLViewerVisualParam *param, F32 weight) bool LLVOAvatarSelf::setParamWeight(const LLViewerVisualParam *param, F32 weight, bool upload_bake) { if (!param) { return false; } if (param->getCrossWearable()) { LLWearableType::EType type = (LLWearableType::EType)param->getWearableType(); U32 size = gAgentWearables.getWearableCount(type); for (U32 count = 0; count < size; ++count) { LLViewerWearable *wearable = gAgentWearables.getViewerWearable(type,count); if (wearable) { // [Legacy Bake] //wearable->setVisualParamWeight(param->getID(), weight); wearable->setVisualParamWeight(param->getID(), weight, upload_bake); } } } // [Legacy Bake] //return LLCharacter::setVisualParamWeight(param,weight); return LLCharacter::setVisualParamWeight(param,weight,upload_bake); } /*virtual*/ void LLVOAvatarSelf::updateVisualParams() { LLVOAvatar::updateVisualParams(); } void LLVOAvatarSelf::writeWearablesToAvatar() { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; for (U32 type = 0; type < LLWearableType::WT_COUNT; type++) { LLWearable *wearable = gAgentWearables.getTopWearable((LLWearableType::EType)type); if (wearable) { wearable->writeToAvatar(this); } } } /*virtual*/ void LLVOAvatarSelf::idleUpdateAppearanceAnimation() { // Animate all top-level wearable visual parameters // [Legacy Bake] //gAgentWearables.animateAllWearableParams(calcMorphAmount()); gAgentWearables.animateAllWearableParams(calcMorphAmount(), false); // Apply wearable visual params to avatar writeWearablesToAvatar(); //allow avatar to process updates LLVOAvatar::idleUpdateAppearanceAnimation(); } // virtual void LLVOAvatarSelf::requestStopMotion(LLMotion* motion) { // Only agent avatars should handle the stop motion notifications. // Notify agent that motion has stopped gAgent.requestStopMotion(motion); } // virtual bool LLVOAvatarSelf::hasMotionFromSource(const LLUUID& source_id) { AnimSourceIterator motion_it = mAnimationSources.find(source_id); return motion_it != mAnimationSources.end(); } // virtual void LLVOAvatarSelf::stopMotionFromSource(const LLUUID& source_id) { for (AnimSourceIterator motion_it = mAnimationSources.find(source_id); motion_it != mAnimationSources.end(); ) { gAgent.sendAnimationRequest(motion_it->second, ANIM_REQUEST_STOP); mAnimationSources.erase(motion_it); // Must find() after each erase() to deal with potential iterator invalidation // This also ensures that we don't go past the end of this source's animations // into those of another source. motion_it = mAnimationSources.find(source_id); } LLViewerObject* object = gObjectList.findObject(source_id); if (object) { object->setFlagsWithoutUpdate(FLAGS_ANIM_SOURCE, false); } } void LLVOAvatarSelf::setLocalTextureTE(U8 te, LLViewerTexture* image, U32 index) { if (te >= TEX_NUM_INDICES) { llassert(0); return; } if (getTEImage(te)->getID() == image->getID()) { return; } if (isIndexBakedTexture((ETextureIndex)te)) { llassert(0); return; } setTEImage(te, image); } //virtual void LLVOAvatarSelf::removeMissingBakedTextures() { bool removed = false; for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { const S32 te = mBakedTextureDatas[i].mTextureIndex; const LLViewerTexture* tex = getTEImage(te); // Replace with default if we can't find the asset, assuming the // default is actually valid (which it should be unless something // is seriously wrong). if (!tex || tex->isMissingAsset()) { LLViewerTexture *imagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT_AVATAR); if (imagep && imagep != tex) { setTEImage(te, imagep); removed = true; } } } if (removed) { for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { LLViewerTexLayerSet *layerset = getTexLayerSet(i); layerset->setUpdatesEnabled(true); // [Legacy Bake] //invalidateComposite(layerset); invalidateComposite(layerset, false); } updateMeshTextures(); // [Legacy Bake] if (getRegion() && !getRegion()->getCentralBakeVersion()) { requestLayerSetUploads(); } // [Legacy Bake] } } // Check whether the BOM capability is different to last time we changed region (even across login) void LLVOAvatarSelf::checkBOMRebakeRequired() { if(getRegion()) { auto bom_can_be_used_here = getRegion()->bakesOnMeshEnabled(); static const LLCachedControl using_bom(gSavedSettings, "CurrentlyUsingBakesOnMesh", true); if( using_bom != bom_can_be_used_here) { // force a rebake when the last grid we were on (including previous login) had different BOM support // This replicates forceAppearanceUpdate rather than pulling in the whole of llavatarself. if(!LLGridManager::instance().isInSecondLife()) { doAfterInterval([](){ if (isAgentAvatarValid()) { gAgentAvatarp->forceBakeAllTextures(true); }}, 5.0); } // update the setting even if we are in SL so that switch SL to OS and back gSavedSettings.setBOOL("CurrentlyUsingBakesOnMesh", bom_can_be_used_here); } } } // void LLVOAvatarSelf::onSimulatorFeaturesReceived(const LLUUID& region_id) { LL_INFOS("Avatar") << "simulator features received, setting hover based on region props" << LL_ENDL; setHoverIfRegionEnabled(); checkBOMRebakeRequired();// BOM we may have stale cache, rebake may be needed } //virtual void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp) { // Save the global position LLVector3d global_pos_from_old_region = getPositionGlobal(); // Change the region setRegion(regionp); if (regionp) { // Set correct region-relative position from global coordinates setPositionGlobal(global_pos_from_old_region); // Diagnostic info //LLVector3d pos_from_new_region = getPositionGlobal(); //LL_INFOS() << "pos_from_old_region is " << global_pos_from_old_region // << " while pos_from_new_region is " << pos_from_new_region // << LL_ENDL; // Update hover height, or schedule callback, based on whether // it's supported in this region. if (regionp->simulatorFeaturesReceived()) { setHoverIfRegionEnabled(); checkBOMRebakeRequired();// BOM we may have stale cache, rebake may be needed } else { regionp->setSimulatorFeaturesReceivedCallback(boost::bind(&LLVOAvatarSelf::onSimulatorFeaturesReceived,this,_1)); } } if (!regionp || (regionp->getHandle() != mLastRegionHandle)) { if (mLastRegionHandle != 0) { ++mRegionCrossingCount; F64Seconds delta(mRegionCrossingTimer.getElapsedTimeF32()); record(LLStatViewer::REGION_CROSSING_TIME, delta); // Diagnostics LL_INFOS() << "Region crossing took " << (F32)(delta * 1000.0).value() << " ms " << LL_ENDL; } if (regionp) { mLastRegionHandle = regionp->getHandle(); } } mRegionCrossingTimer.reset(); LLViewerObject::updateRegion(regionp); setIsCrossingRegion(false); // FIRE-12004: Attachments getting lost on TP } //-------------------------------------------------------------------- // draw tractor (selection) beam when editing objects //-------------------------------------------------------------------- //virtual void LLVOAvatarSelf::idleUpdateTractorBeam() { LLColor4U rgb = gLggBeamMaps.getCurrentColor(LLColor4U(gAgent.getEffectColor())); // Private point at static LLCachedControl private_pointat(gSavedSettings, "PrivatePointAtTarget", false); // This is only done for yourself (maybe it should be in the agent?) // Private point at //if (!needsRenderBeam() || !isBuilt()) if (!needsRenderBeam() || !isBuilt() || private_pointat) // { mBeam = NULL; gLggBeamMaps.stopBeamChat(); } else if (!mBeam || mBeam->isDead()) { // VEFFECT: Tractor Beam mBeam = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM); mBeam->setColor(rgb); mBeam->setSourceObject(this); mBeamTimer.reset(); } if (!mBeam.isNull()) { LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); if (gAgentCamera.mPointAt.notNull()) { // get point from pointat effect mBeam->setPositionGlobal(gAgentCamera.mPointAt->getPointAtPosGlobal()); gLggBeamMaps.updateBeamChat(gAgentCamera.mPointAt->getPointAtPosGlobal()); mBeam->triggerLocal(); } else if (selection->getFirstRootObject() && selection->getSelectType() != SELECT_TYPE_HUD) { LLViewerObject* objectp = selection->getFirstRootObject(); mBeam->setTargetObject(objectp); } else { mBeam->setTargetObject(NULL); LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); if (tool->isEditing()) { if (tool->getEditingObject()) { mBeam->setTargetObject(tool->getEditingObject()); } else { mBeam->setPositionGlobal(tool->getEditingPointGlobal()); } } else { const LLPickInfo& pick = gViewerWindow->getLastPick(); mBeam->setPositionGlobal(pick.mPosGlobal); } } if (mBeamTimer.getElapsedTimeF32() > gLggBeamMaps.setUpAndGetDuration()) { mBeam->setColor(rgb); mBeam->setNeedsSendToSim(true); mBeamTimer.reset(); gLggBeamMaps.fireCurrentBeams(mBeam, rgb); } } } //----------------------------------------------------------------------------- // restoreMeshData() //----------------------------------------------------------------------------- // virtual void LLVOAvatarSelf::restoreMeshData() { //LL_INFOS() << "Restoring" << LL_ENDL; mMeshValid = true; updateJointLODs(); updateAttachmentVisibility(gAgentCamera.getCameraMode()); // force mesh update as LOD might not have changed to trigger this gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY); } //----------------------------------------------------------------------------- // updateAttachmentVisibility() //----------------------------------------------------------------------------- void LLVOAvatarSelf::updateAttachmentVisibility(U32 camera_mode) { for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment* attachment = iter->second; // Possible crash fix if (!attachment) { continue; } // if (attachment->getIsHUDAttachment()) { attachment->setAttachmentVisibility(true); } else { // [RLVa:KB] - Checked: RLVa-2.0.2 bool fRlvCanShowAttachment = true; if (rlv_handler_t::isEnabled()) { fRlvCanShowAttachment = (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWSELF)) && ( (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWSELFHEAD)) || (RLV_ATTACHGROUP_HEAD != rlvAttachGroupFromIndex(attachment->getGroup())) ); } // [/RLVa:KB] switch (camera_mode) { case CAMERA_MODE_MOUSELOOK: if ((LLVOAvatar::sVisibleInFirstPerson && attachment->getVisibleInFirstPerson()) || gPipeline.mHeroProbeManager.isMirrorPass()) { // [RLVa:KB] - Checked: RLVa-2.0.2 attachment->setAttachmentVisibility(fRlvCanShowAttachment); // [/RLVa:KB] // attachment->setAttachmentVisibility(true); } else { attachment->setAttachmentVisibility(false); } break; default: // [RLVa:KB] - Checked: RLVa-2.0.2 attachment->setAttachmentVisibility(fRlvCanShowAttachment); // [/RLVa:KB] // attachment->setAttachmentVisibility(true); break; } } } } //----------------------------------------------------------------------------- // updatedWearable( LLWearableType::EType type ) // forces an update to any baked textures relevant to type. // will force an upload of the resulting bake if the second parameter is true //----------------------------------------------------------------------------- // [Legacy Bake] //void LLVOAvatarSelf::wearableUpdated(LLWearableType::EType type) void LLVOAvatarSelf::wearableUpdated(LLWearableType::EType type, bool upload_result) { for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = baked_iter->second; const LLAvatarAppearanceDefines::EBakedTextureIndex index = baked_iter->first; if (baked_dict) { for (LLAvatarAppearanceDefines::wearables_vec_t::const_iterator type_iter = baked_dict->mWearables.begin(); type_iter != baked_dict->mWearables.end(); ++type_iter) { const LLWearableType::EType comp_type = *type_iter; if (comp_type == type) { LLViewerTexLayerSet *layerset = getLayerSet(index); if (layerset) { layerset->setUpdatesEnabled(true); // [Legacy Bake] //invalidateComposite(layerset); invalidateComposite(layerset, upload_result); } break; } } } } // [Legacy Bake] // Physics type has no associated baked textures, but change of params needs to be sent to // other avatars. if (type == LLWearableType::WT_PHYSICS) { gAgent.sendAgentSetAppearance(); } // [Legacy Bake] } //----------------------------------------------------------------------------- // isWearingAttachment() //----------------------------------------------------------------------------- bool LLVOAvatarSelf::isWearingAttachment(const LLUUID& inv_item_id) const { const LLUUID& base_inv_item_id = gInventory.getLinkedItemID(inv_item_id); for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { const LLViewerJointAttachment* attachment = iter->second; if (attachment->getAttachedObject(base_inv_item_id)) { return true; } } return false; } //----------------------------------------------------------------------------- // getWornAttachment() //----------------------------------------------------------------------------- LLViewerObject* LLVOAvatarSelf::getWornAttachment(const LLUUID& inv_item_id) { const LLUUID& base_inv_item_id = gInventory.getLinkedItemID(inv_item_id); for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment* attachment = iter->second; if (LLViewerObject *attached_object = attachment->getAttachedObject(base_inv_item_id)) { return attached_object; } } return NULL; } // [RLVa:KB] - Checked: 2012-07-28 (RLVa-1.4.7) boost::signals2::connection LLVOAvatarSelf::setAttachmentCallback(const attachment_signal_t::slot_type& cb) { if (!mAttachmentSignal) mAttachmentSignal = new attachment_signal_t(); return mAttachmentSignal->connect(cb); } // [/RLVa:KB] // [RLVa:KB] - Checked: 2010-03-14 (RLVa-1.2.0a) | Modified: RLVa-1.2.0a LLViewerJointAttachment* LLVOAvatarSelf::getWornAttachmentPoint(const LLUUID& idItem) const { const LLUUID& idItemBase = gInventory.getLinkedItemID(idItem); for (attachment_map_t::const_iterator itAttachPt = mAttachmentPoints.begin(); itAttachPt != mAttachmentPoints.end(); ++itAttachPt) { LLViewerJointAttachment* pAttachPt = itAttachPt->second; if (pAttachPt->getAttachedObject(idItemBase)) return pAttachPt; } return NULL; } // [/RLVa:KB] bool LLVOAvatarSelf::getAttachedPointName(const LLUUID& inv_item_id, std::string& name) const { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; if (!gInventory.getItem(inv_item_id)) { name = "ATTACHMENT_MISSING_ITEM"; return false; } const LLUUID& base_inv_item_id = gInventory.getLinkedItemID(inv_item_id); if (!gInventory.getItem(base_inv_item_id)) { name = "ATTACHMENT_MISSING_BASE_ITEM"; return false; } for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { const LLViewerJointAttachment* attachment = iter->second; if (attachment->getAttachedObject(base_inv_item_id)) { name = attachment->getName(); return true; } } name = "ATTACHMENT_NOT_ATTACHED"; return false; } //virtual const LLViewerJointAttachment *LLVOAvatarSelf::attachObject(LLViewerObject *viewer_object) { const LLViewerJointAttachment *attachment = LLVOAvatar::attachObject(viewer_object); if (!attachment) { return 0; } updateAttachmentVisibility(gAgentCamera.getCameraMode()); // Then make sure the inventory is in sync with the avatar. // Should just be the last object added if (attachment->isObjectAttached(viewer_object)) { const LLUUID& attachment_id = viewer_object->getAttachmentItemID(); LLAppearanceMgr::instance().registerAttachment(attachment_id); // ReplaceWornItemsOnly gInventory.wearAttachmentsOnAvatarCheckRemove(viewer_object, attachment); // // Client LSL Bridge if (attachment->getName() == FS_BRIDGE_ATTACHMENT_POINT_NAME && gSavedSettings.getBOOL("UseLSLBridge")) { LLViewerInventoryItem* inv_object = gInventory.getItem(viewer_object->getAttachmentItemID()); if (inv_object && inv_object->getName() == FSLSLBridge::instance().currentFullName()) { FSLSLBridge::instance().processAttach(viewer_object, attachment); } } // updateLODRiggedAttachments(); // [RLVa:KB] - Checked: 2010-08-22 (RLVa-1.2.1a) | Modified: RLVa-1.2.1a // NOTE: RLVa event handlers should be invoked *after* LLVOAvatar::attachObject() calls LLViewerJointAttachment::addObject() if (mAttachmentSignal) { (*mAttachmentSignal)(viewer_object, attachment, ACTION_ATTACH); } if (rlv_handler_t::isEnabled()) { RlvAttachmentLockWatchdog::instance().onAttach(viewer_object, attachment); gRlvHandler.onAttach(viewer_object, attachment); if ( (attachment->getIsHUDAttachment()) && (!gRlvAttachmentLocks.hasLockedHUD()) ) gRlvAttachmentLocks.updateLockedHUD(); } // [/RLVa:KB] } return attachment; } //virtual bool LLVOAvatarSelf::detachObject(LLViewerObject *viewer_object) { const LLUUID attachment_id = viewer_object->getAttachmentItemID(); // [RLVa:KB] - Checked: 2010-03-05 (RLVa-1.2.0a) | Added: RLVa-1.2.0a // NOTE: RLVa event handlers should be invoked *before* LLVOAvatar::detachObject() calls LLViewerJointAttachment::removeObject() //if (rlv_handler_t::isEnabled()) //{ // for (attachment_map_t::const_iterator itAttachPt = mAttachmentPoints.begin(); itAttachPt != mAttachmentPoints.end(); ++itAttachPt) // { // const LLViewerJointAttachment* pAttachPt = itAttachPt->second; // if (pAttachPt->isObjectAttached(viewer_object)) // { // Client LSL Bridge - moving the rlv check to get the pAttachPt for the bridge const LLViewerJointAttachment* pAttachPt = NULL; for (attachment_map_t::const_iterator itAttachPt = mAttachmentPoints.begin(); itAttachPt != mAttachmentPoints.end(); ++itAttachPt) { pAttachPt = itAttachPt->second; if (pAttachPt->isObjectAttached(viewer_object)) { if (rlv_handler_t::isEnabled()) { // RlvAttachmentLockWatchdog::instance().onDetach(viewer_object, pAttachPt); gRlvHandler.onDetach(viewer_object, pAttachPt); } if (mAttachmentSignal) { (*mAttachmentSignal)(viewer_object, pAttachPt, ACTION_DETACH); } // Client LSL Bridge if (pAttachPt->getName() == FS_BRIDGE_ATTACHMENT_POINT_NAME) { LLViewerInventoryItem* inv_object = gInventory.getItem(viewer_object->getAttachmentItemID()); if (inv_object && inv_object->getName() == FSLSLBridge::instance().currentFullName()) { FSLSLBridge::instance().processDetach(viewer_object, pAttachPt); } } // break; } } // [/RLVa:KB] if ( LLVOAvatar::detachObject(viewer_object) ) { // the simulator should automatically handle permission revocation stopMotionFromSource(attachment_id); LLFollowCamMgr::getInstance()->setCameraActive(viewer_object->getID(), false); LLViewerObject::const_child_list_t& child_list = viewer_object->getChildren(); for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); iter != child_list.end(); ++iter) { LLViewerObject* child_objectp = *iter; // the simulator should automatically handle // permissions revocation stopMotionFromSource(child_objectp->getID()); LLFollowCamMgr::getInstance()->setCameraActive(child_objectp->getID(), false); } // Make sure the inventory is in sync with the avatar. // Update COF contents, don't trigger appearance update. if (!isAgentAvatarValid()) { LL_INFOS() << "removeItemLinks skipped, avatar is under destruction" << LL_ENDL; } else { LLAppearanceMgr::instance().unregisterAttachment(attachment_id); } // [RLVa:KB] - Checked: 2010-08-22 (RLVa-1.2.1a) | Added: RLVa-1.2.1a if ( (rlv_handler_t::isEnabled()) && (viewer_object->isHUDAttachment()) && (gRlvAttachmentLocks.hasLockedHUD()) ) gRlvAttachmentLocks.updateLockedHUD(); // [/RLVa:KB] return true; } return false; } bool LLVOAvatarSelf::hasAttachmentsInTrash() { const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { LLViewerJointAttachment *attachment = iter->second; for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) { LLViewerObject *attached_object = attachment_iter->get(); if (attached_object && gInventory.isObjectDescendentOf(attached_object->getAttachmentItemID(), trash_id)) { return true; } } } return false; } // static bool LLVOAvatarSelf::detachAttachmentIntoInventory(const LLUUID &item_id) { LLInventoryItem* item = gInventory.getItem(item_id); // if (item) // [RLVa:KB] - Checked: 2010-09-04 (RLVa-1.2.1c) | Added: RLVa-1.2.1c if ( (item) && (((!rlv_handler_t::isEnabled()) || (gRlvAttachmentLocks.canDetach(item))) || FSLSLBridge::instance().canDetach(item->getUUID())) ) // [/RLVa:KB] { gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv); gMessageSystem->nextBlockFast(_PREHASH_ObjectData); gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); gMessageSystem->addUUIDFast(_PREHASH_ItemID, item_id); gMessageSystem->sendReliable(gAgent.getRegionHost()); // This object might have been selected, so let the selection manager know it's gone now LLViewerObject *found_obj = gObjectList.findObject(item_id); if (found_obj) { LLSelectMgr::getInstance()->remove(found_obj); } // Error checking in case this object was attached to an invalid point // In that case, just remove the item from COF preemptively since detach // will fail. if (isAgentAvatarValid()) { const LLViewerObject *attached_obj = gAgentAvatarp->getWornAttachment(item_id); if (!attached_obj) { LLAppearanceMgr::instance().removeCOFItemLinks(item_id); } } return true; } return false; } U32 LLVOAvatarSelf::getNumWearables(LLAvatarAppearanceDefines::ETextureIndex i) const { LLWearableType::EType type = sAvatarDictionary->getTEWearableType(i); return gAgentWearables.getWearableCount(type); } // virtual void LLVOAvatarSelf::localTextureLoaded(bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src_raw, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata) { const LLUUID& src_id = src_vi->getID(); LLAvatarTexData *data = (LLAvatarTexData *)userdata; ETextureIndex index = data->mIndex; if (!isIndexLocalTexture(index)) return; LLLocalTextureObject *local_tex_obj = getLocalTextureObject(index, 0); // fix for EXT-268. Preventing using of NULL pointer if(NULL == local_tex_obj) { LL_WARNS("TAG") << "There is no Local Texture Object with index: " << index << ", final: " << final << LL_ENDL; return; } if (success) { if (!local_tex_obj->getBakedReady() && local_tex_obj->getImage() != NULL && (local_tex_obj->getID() == src_id) && discard_level < local_tex_obj->getDiscard()) { local_tex_obj->setDiscard(discard_level); requestLayerSetUpdate(index); if (isEditingAppearance()) { LLVisualParamHint::requestHintUpdates(); } updateMeshTextures(); } } else if (final) { // Failed: asset is missing if (!local_tex_obj->getBakedReady() && local_tex_obj->getImage() != NULL && local_tex_obj->getImage()->getID() == src_id) { local_tex_obj->setDiscard(0); requestLayerSetUpdate(index); updateMeshTextures(); } } } // virtual bool LLVOAvatarSelf::getLocalTextureGL(ETextureIndex type, LLViewerTexture** tex_pp, U32 index) const { *tex_pp = NULL; if (!isIndexLocalTexture(type)) return false; if (getLocalTextureID(type, index) == IMG_DEFAULT_AVATAR) return true; const LLLocalTextureObject *local_tex_obj = getLocalTextureObject(type, index); if (!local_tex_obj) { return false; } *tex_pp = dynamic_cast (local_tex_obj->getImage()); return true; } LLViewerFetchedTexture* LLVOAvatarSelf::getLocalTextureGL(LLAvatarAppearanceDefines::ETextureIndex type, U32 index) const { if (!isIndexLocalTexture(type)) { return NULL; } const LLLocalTextureObject *local_tex_obj = getLocalTextureObject(type, index); if (!local_tex_obj) { return NULL; } if (local_tex_obj->getID() == IMG_DEFAULT_AVATAR) { return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT_AVATAR); } return dynamic_cast (local_tex_obj->getImage()); } const LLUUID& LLVOAvatarSelf::getLocalTextureID(ETextureIndex type, U32 index) const { if (!isIndexLocalTexture(type)) return IMG_DEFAULT_AVATAR; const LLLocalTextureObject *local_tex_obj = getLocalTextureObject(type, index); if (local_tex_obj && local_tex_obj->getImage() != NULL) { return local_tex_obj->getImage()->getID(); } return IMG_DEFAULT_AVATAR; } //----------------------------------------------------------------------------- // isLocalTextureDataAvailable() // Returns true if at least the lowest quality discard level exists for every texture // in the layerset. //----------------------------------------------------------------------------- bool LLVOAvatarSelf::isLocalTextureDataAvailable(const LLViewerTexLayerSet* layerset) const { /* if (layerset == mBakedTextureDatas[BAKED_HEAD].mTexLayerSet) return getLocalDiscardLevel(TEX_HEAD_BODYPAINT) >= 0; */ for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const EBakedTextureIndex baked_index = baked_iter->first; if (layerset == mBakedTextureDatas[baked_index].mTexLayerSet) { bool ret = true; const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = baked_iter->second; for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { bool tex_avail = (getLocalDiscardLevel(tex_index, wearable_index) >= 0); ret &= tex_avail; } } return ret; } } llassert(0); return false; } //----------------------------------------------------------------------------- // virtual // isLocalTextureDataFinal() // Returns true if the highest quality discard level exists for every texture // in the layerset. //----------------------------------------------------------------------------- bool LLVOAvatarSelf::isLocalTextureDataFinal(const LLViewerTexLayerSet* layerset) const { // Replace frequently called gSavedSettings //const U32 desired_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel"); static LLCachedControl sTextureDiscardLevel(gSavedSettings, "TextureDiscardLevel"); const U32 desired_tex_discard_level = sTextureDiscardLevel(); // // const U32 desired_tex_discard_level = 0; // hack to not bake textures on lower discard levels. for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { if (layerset == mBakedTextureDatas[i].mTexLayerSet) { const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = sAvatarDictionary->getBakedTexture((EBakedTextureIndex)i); for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { S32 local_discard_level = getLocalDiscardLevel(*local_tex_iter, wearable_index); if ((local_discard_level > (S32)(desired_tex_discard_level)) || (local_discard_level < 0 )) { return false; } } } return true; } } llassert(0); return false; } bool LLVOAvatarSelf::isAllLocalTextureDataFinal() const { // Replace frequently called gSavedSettings //const U32 desired_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel"); static LLCachedControl sTextureDiscardLevel(gSavedSettings, "TextureDiscardLevel"); const U32 desired_tex_discard_level = sTextureDiscardLevel(); // // const U32 desired_tex_discard_level = 0; // hack to not bake textures on lower discard levels for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = sAvatarDictionary->getBakedTexture((EBakedTextureIndex)i); for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { S32 local_discard_level = getLocalDiscardLevel(*local_tex_iter, wearable_index); if ((local_discard_level > (S32)(desired_tex_discard_level)) || (local_discard_level < 0 )) { return false; } } } } return true; } bool LLVOAvatarSelf::isTextureDefined(LLAvatarAppearanceDefines::ETextureIndex type, U32 index) const { LLUUID id; bool isDefined = true; if (isIndexLocalTexture(type)) { const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(type); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); if (index >= wearable_count) { // invalid index passed in. check all textures of a given type for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { id = getLocalTextureID(type, wearable_index); isDefined &= (id != IMG_DEFAULT_AVATAR && id != IMG_DEFAULT); } } else { id = getLocalTextureID(type, index); isDefined &= (id != IMG_DEFAULT_AVATAR && id != IMG_DEFAULT); } } else { id = getTEImage(type)->getID(); isDefined &= (id != IMG_DEFAULT_AVATAR && id != IMG_DEFAULT); } return isDefined; } //virtual bool LLVOAvatarSelf::isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex type, U32 index) const { if (isIndexBakedTexture(type)) { return LLVOAvatar::isTextureVisible(type, (U32)0); } LLUUID tex_id = getLocalTextureID(type,index); return (tex_id != IMG_INVISIBLE) || (LLDrawPoolAlpha::sShowDebugAlpha); } //virtual bool LLVOAvatarSelf::isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex type, LLViewerWearable *wearable) const { if (isIndexBakedTexture(type)) { return LLVOAvatar::isTextureVisible(type); } U32 index; if (gAgentWearables.getWearableIndex(wearable,index)) { return isTextureVisible(type,index); } else { LL_WARNS() << "Wearable not found" << LL_ENDL; return false; } } bool LLVOAvatarSelf::areTexturesCurrent() const { // [Legacy Bake] //return gAgentWearables.areWearablesLoaded(); return !hasPendingBakedUploads() && gAgentWearables.areWearablesLoaded(); } // [Legacy Bake] //void LLVOAvatarSelf::invalidateComposite( LLTexLayerSet* layerset) void LLVOAvatarSelf::invalidateComposite( LLTexLayerSet* layerset, bool upload_result) { LLViewerTexLayerSet *layer_set = dynamic_cast(layerset); if( !layer_set || !layer_set->getUpdatesEnabled() ) { return; } // LL_INFOS() << "LLVOAvatar::invalidComposite() " << layerset->getBodyRegionName() << LL_ENDL; layer_set->requestUpdate(); layer_set->invalidateMorphMasks(); // [Legacy Bake] if( upload_result && (getRegion() && !getRegion()->getCentralBakeVersion())) { llassert(isSelf()); ETextureIndex baked_te = getBakedTE( layer_set ); setTEImage( baked_te, LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT_AVATAR) ); layer_set->requestUpload(); updateMeshTextures(); } // [Legacy Bake] } void LLVOAvatarSelf::invalidateAll() { for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { LLViewerTexLayerSet *layerset = getTexLayerSet(i); // [Legacy Bake] //invalidateComposite(layerset); invalidateComposite(layerset, true); } //mDebugSelfLoadTimer.reset(); } //----------------------------------------------------------------------------- // setCompositeUpdatesEnabled() //----------------------------------------------------------------------------- void LLVOAvatarSelf::setCompositeUpdatesEnabled( bool b ) { for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { setCompositeUpdatesEnabled(i, b); } } void LLVOAvatarSelf::setCompositeUpdatesEnabled(U32 index, bool b) { LLViewerTexLayerSet *layerset = getTexLayerSet(index); if (layerset ) { layerset->setUpdatesEnabled( b ); } } bool LLVOAvatarSelf::isCompositeUpdateEnabled(U32 index) { LLViewerTexLayerSet *layerset = getTexLayerSet(index); if (layerset) { return layerset->getUpdatesEnabled(); } return false; } void LLVOAvatarSelf::setupComposites() { for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { ETextureIndex tex_index = mBakedTextureDatas[i].mTextureIndex; bool layer_baked = isTextureDefined(tex_index, gAgentWearables.getWearableCount(tex_index)); LLViewerTexLayerSet *layerset = getTexLayerSet(i); if (layerset) { layerset->setUpdatesEnabled(!layer_baked); } } } void LLVOAvatarSelf::updateComposites() { for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { LLViewerTexLayerSet *layerset = getTexLayerSet(i); if (layerset && ((i != BAKED_SKIRT) || isWearingWearableType(LLWearableType::WT_SKIRT))) { layerset->updateComposite(); } } } // virtual S32 LLVOAvatarSelf::getLocalDiscardLevel(ETextureIndex type, U32 wearable_index) const { if (!isIndexLocalTexture(type)) return false; const LLLocalTextureObject *local_tex_obj = getLocalTextureObject(type, wearable_index); if (local_tex_obj) { const LLViewerFetchedTexture* image = dynamic_cast( local_tex_obj->getImage() ); if (type >= 0 && local_tex_obj->getID() != IMG_DEFAULT_AVATAR && !image->isMissingAsset()) { return image->getDiscardLevel(); } else { // We don't care about this (no image associated with the layer) treat as fully loaded. return 0; } } return 0; } // virtual // Counts the memory footprint of local textures. void LLVOAvatarSelf::getLocalTextureByteCount(S32* gl_bytes) const { *gl_bytes = 0; for (S32 type = 0; type < TEX_NUM_INDICES; type++) { if (!isIndexLocalTexture((ETextureIndex)type)) continue; U32 max_tex = getNumWearables((ETextureIndex) type); for (U32 num = 0; num < max_tex; num++) { const LLLocalTextureObject *local_tex_obj = getLocalTextureObject((ETextureIndex) type, num); if (local_tex_obj) { const LLViewerFetchedTexture* image_gl = dynamic_cast( local_tex_obj->getImage() ); if (image_gl) { S32 bytes = (S32)image_gl->getWidth() * image_gl->getHeight() * image_gl->getComponents(); if (image_gl->hasGLTexture()) { *gl_bytes += bytes; } } } } } } // virtual void LLVOAvatarSelf::setLocalTexture(ETextureIndex type, LLViewerTexture* src_tex, bool baked_version_ready, U32 index) { if (!isIndexLocalTexture(type)) return; LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(src_tex, true) ; if(!tex) { return ; } S32 desired_discard = isSelf() ? 0 : 2; LLLocalTextureObject *local_tex_obj = getLocalTextureObject(type,index); if (!local_tex_obj) { if (type >= TEX_NUM_INDICES) { LL_ERRS() << "Tried to set local texture with invalid type: (" << (U32) type << ", " << index << ")" << LL_ENDL; return; } LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(type); if (!gAgentWearables.getViewerWearable(wearable_type,index)) { // no wearable is loaded, cannot set the texture. return; } gAgentWearables.addLocalTextureObject(wearable_type,type,index); local_tex_obj = getLocalTextureObject(type,index); if (!local_tex_obj) { LL_ERRS() << "Unable to create LocalTextureObject for wearable type & index: (" << (U32) wearable_type << ", " << index << ")" << LL_ENDL; return; } LLViewerTexLayerSet *layer_set = getLayerSet(type); if (layer_set) { layer_set->cloneTemplates(local_tex_obj, type, gAgentWearables.getViewerWearable(wearable_type,index)); } } if (!baked_version_ready) { if (tex != local_tex_obj->getImage() || local_tex_obj->getBakedReady()) { local_tex_obj->setDiscard(MAX_DISCARD_LEVEL+1); } if (tex->getID() != IMG_DEFAULT_AVATAR) { if (local_tex_obj->getDiscard() > desired_discard) { S32 tex_discard = tex->getDiscardLevel(); if (tex_discard >= 0 && tex_discard <= desired_discard) { local_tex_obj->setDiscard(tex_discard); if (isSelf()) { requestLayerSetUpdate(type); if (isEditingAppearance()) { LLVisualParamHint::requestHintUpdates(); } } } else { tex->setLoadedCallback(onLocalTextureLoaded, desired_discard, true, false, new LLAvatarTexData(getID(), type), NULL); } } tex->setMinDiscardLevel(desired_discard); } } local_tex_obj->setImage(tex); local_tex_obj->setID(tex->getID()); setBakedReady(type,baked_version_ready,index); } //virtual void LLVOAvatarSelf::setBakedReady(LLAvatarAppearanceDefines::ETextureIndex type, bool baked_version_exists, U32 index) { if (!isIndexLocalTexture(type)) return; LLLocalTextureObject *local_tex_obj = getLocalTextureObject(type,index); if (local_tex_obj) { local_tex_obj->setBakedReady( baked_version_exists ); } } // virtual void LLVOAvatarSelf::dumpLocalTextures() const { LL_INFOS() << "Local Textures:" << LL_ENDL; /* ETextureIndex baked_equiv[] = { TEX_UPPER_BAKED, if (isTextureDefined(baked_equiv[i])) */ for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = sAvatarDictionary->getTextures().begin(); iter != sAvatarDictionary->getTextures().end(); ++iter) { const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second; if (!texture_dict->mIsLocalTexture || !texture_dict->mIsUsedByBakedTexture) continue; const EBakedTextureIndex baked_index = texture_dict->mBakedTextureIndex; const ETextureIndex baked_equiv = sAvatarDictionary->getBakedTexture(baked_index)->mTextureIndex; const std::string &name = texture_dict->mName; const LLLocalTextureObject *local_tex_obj = getLocalTextureObject(iter->first, 0); // index is baked texture - index is not relevant. putting in 0 as placeholder if (isTextureDefined(baked_equiv, 0)) { #if LL_RELEASE_FOR_DOWNLOAD // End users don't get to trivially see avatar texture IDs, makes textures // easier to steal. JC LL_INFOS() << "LocTex " << name << ": Baked " << LL_ENDL; #else LL_INFOS() << "LocTex " << name << ": Baked " << getTEImage(baked_equiv)->getID() << LL_ENDL; #endif } else if (local_tex_obj && local_tex_obj->getImage() != NULL) { if (local_tex_obj->getImage()->getID() == IMG_DEFAULT_AVATAR) { LL_INFOS() << "LocTex " << name << ": None" << LL_ENDL; } else { LLViewerFetchedTexture* image = dynamic_cast( local_tex_obj->getImage() ); LL_INFOS() << "LocTex " << name << ": " << "Discard " << image->getDiscardLevel() << ", " << "(" << image->getWidth() << ", " << image->getHeight() << ") " #if !LL_RELEASE_FOR_DOWNLOAD // End users don't get to trivially see avatar texture IDs, // makes textures easier to steal << image->getID() << " " #endif << "Priority: " << image->getMaxVirtualSize() << LL_ENDL; } } else { LL_INFOS() << "LocTex " << name << ": No LLViewerTexture" << LL_ENDL; } } } //----------------------------------------------------------------------------- // static // onLocalTextureLoaded() //----------------------------------------------------------------------------- void LLVOAvatarSelf::onLocalTextureLoaded(bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src_raw, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata) { LLAvatarTexData *data = (LLAvatarTexData *)userdata; LLVOAvatarSelf *self = (LLVOAvatarSelf *)gObjectList.findObject(data->mAvatarID); if (self) { // We should only be handling local textures for ourself self->localTextureLoaded(success, src_vi, src_raw, aux_src, discard_level, final, userdata); } // ensure data is cleaned up if (final || !success) { delete data; } } /*virtual*/ void LLVOAvatarSelf::setImage(const U8 te, LLViewerTexture *imagep, const U32 index) { if (isIndexLocalTexture((ETextureIndex)te)) { setLocalTexture((ETextureIndex)te, imagep, false ,index); } else { setTEImage(te,imagep); } } /*virtual*/ LLViewerTexture* LLVOAvatarSelf::getImage(const U8 te, const U32 index) const { if (isIndexLocalTexture((ETextureIndex)te)) { return getLocalTextureGL((ETextureIndex)te,index); } else { return getTEImage(te); } } // static void LLVOAvatarSelf::dumpTotalLocalTextureByteCount() { S32 gl_bytes = 0; gAgentAvatarp->getLocalTextureByteCount(&gl_bytes); LL_INFOS() << "Total Avatar LocTex GL:" << (gl_bytes/1024) << "KB" << LL_ENDL; } bool LLVOAvatarSelf::getIsCloud() const { // Let people know why they're clouded without spamming them into oblivion. bool do_warn = false; static LLTimer time_since_notice; F32 update_freq = 30.0; if (time_since_notice.getElapsedTimeF32() > update_freq) { time_since_notice.reset(); do_warn = true; } // do we have our body parts? S32 shape_count = gAgentWearables.getWearableCount(LLWearableType::WT_SHAPE); S32 hair_count = gAgentWearables.getWearableCount(LLWearableType::WT_HAIR); S32 eye_count = gAgentWearables.getWearableCount(LLWearableType::WT_EYES); S32 skin_count = gAgentWearables.getWearableCount(LLWearableType::WT_SKIN); if (!shape_count || !hair_count || !eye_count || !skin_count) { if (do_warn) { LL_INFOS() << "Self is clouded due to missing one or more required body parts: " << (shape_count ? "" : "SHAPE ") << (hair_count ? "" : "HAIR ") << (eye_count ? "" : "EYES ") << (skin_count ? "" : "SKIN ") << LL_ENDL; } return true; } if (!isTextureDefined(TEX_HAIR, 0)) { if (do_warn) { LL_INFOS() << "Self is clouded because of no hair texture" << LL_ENDL; } return true; } if (!mPreviousFullyLoaded) { if (!isLocalTextureDataAvailable(getLayerSet(BAKED_LOWER)) && (!isTextureDefined(TEX_LOWER_BAKED, 0))) { if (do_warn) { LL_INFOS() << "Self is clouded because lower textures not baked" << LL_ENDL; } return true; } if (!isLocalTextureDataAvailable(getLayerSet(BAKED_UPPER)) && (!isTextureDefined(TEX_UPPER_BAKED, 0))) { if (do_warn) { LL_INFOS() << "Self is clouded because upper textures not baked" << LL_ENDL; } return true; } for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { if (i == BAKED_SKIRT && !isWearingWearableType(LLWearableType::WT_SKIRT)) continue; const BakedTextureData& texture_data = mBakedTextureDatas[i]; if (!isTextureDefined(texture_data.mTextureIndex, 0)) continue; // Check for the case that texture is defined but not sufficiently loaded to display anything. const LLViewerTexture* baked_img = getImage( texture_data.mTextureIndex, 0 ); if (!baked_img || !baked_img->hasGLTexture()) { if (do_warn) { LL_INFOS() << "Self is clouded because texture at index " << i << " (texture index is " << texture_data.mTextureIndex << ") is not loaded" << LL_ENDL; } return true; } } LL_DEBUGS() << "Avatar de-clouded" << LL_ENDL; } return false; } /*static*/ void LLVOAvatarSelf::debugOnTimingLocalTexLoaded(bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata) { if (gAgentAvatarp.notNull()) { gAgentAvatarp->debugTimingLocalTexLoaded(success, src_vi, src, aux_src, discard_level, final, userdata); } } void LLVOAvatarSelf::debugTimingLocalTexLoaded(bool success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, bool final, void* userdata) { LLAvatarTexData *data = (LLAvatarTexData *)userdata; if (!data) { return; } ETextureIndex index = data->mIndex; if (index < 0 || index >= TEX_NUM_INDICES) { return; } if (discard_level >=0 && discard_level <= MAX_DISCARD_LEVEL) // ignore discard level -1, as it means we have no data. { mDebugTextureLoadTimes[(U32)index][(U32)discard_level] = mDebugSelfLoadTimer.getElapsedTimeF32(); } if (final) { delete data; } } void LLVOAvatarSelf::debugBakedTextureUpload(EBakedTextureIndex index, bool finished) { U32 done = 0; if (finished) { done = 1; } mDebugBakedTextureTimes[index][done] = mDebugSelfLoadTimer.getElapsedTimeF32(); } const std::string LLVOAvatarSelf::verboseDebugDumpLocalTextureDataInfo(const LLViewerTexLayerSet* layerset) const { std::ostringstream outbuf; LLWearableType *wr_inst = LLWearableType::getInstance(); for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const EBakedTextureIndex baked_index = baked_iter->first; if (layerset == mBakedTextureDatas[baked_index].mTexLayerSet) { outbuf << "baked_index: " << baked_index << "\n"; const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = baked_iter->second; for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; const std::string tex_name = sAvatarDictionary->getTexture(tex_index)->mName; outbuf << " tex_index " << (S32) tex_index << " name " << tex_name << "\n"; const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); if (wearable_count > 0) { for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { outbuf << " " << wr_inst->getTypeName(wearable_type) << " " << wearable_index << ":"; const LLLocalTextureObject *local_tex_obj = getLocalTextureObject(tex_index, wearable_index); if (local_tex_obj) { LLViewerFetchedTexture* image = dynamic_cast( local_tex_obj->getImage() ); if (tex_index >= 0 && local_tex_obj->getID() != IMG_DEFAULT_AVATAR && !image->isMissingAsset()) { outbuf << " id: " << image->getID() << " refs: " << image->getNumRefs() << " glocdisc: " << getLocalDiscardLevel(tex_index, wearable_index) << " discard: " << image->getDiscardLevel() << " desired: " << image->getDesiredDiscardLevel() << " vsize: " << image->getMaxVirtualSize() << " ts: " << image->getTextureState() << " bl: " << image->getBoostLevel() << " fl: " << image->isFullyLoaded() // this is not an accessor for mFullyLoaded - see comment there. << " cl: " << (image->isFullyLoaded() && image->getDiscardLevel()==0) // "completely loaded" << " mvs: " << image->getMaxVirtualSize() << " mvsc: " << image->getMaxVirtualSizeResetCounter() << " mem: " << image->getTextureMemory(); } } outbuf << "\n"; } } } break; } } return outbuf.str(); } void LLVOAvatarSelf::dumpAllTextures() const { std::string vd_text = "Local textures per baked index and wearable:\n"; for (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const LLAvatarAppearanceDefines::EBakedTextureIndex baked_index = baked_iter->first; const LLViewerTexLayerSet *layerset = debugGetLayerSet(baked_index); if (!layerset) continue; const LLViewerTexLayerSetBuffer *layerset_buffer = layerset->getViewerComposite(); if (!layerset_buffer) continue; vd_text += verboseDebugDumpLocalTextureDataInfo(layerset); } LL_DEBUGS("Avatar") << vd_text << LL_ENDL; } const std::string LLVOAvatarSelf::debugDumpLocalTextureDataInfo(const LLViewerTexLayerSet* layerset) const { std::string text=""; LLWearableType *wr_inst = LLWearableType::getInstance(); text = llformat("[Final:%d Avail:%d] ",isLocalTextureDataFinal(layerset), isLocalTextureDataAvailable(layerset)); /* if (layerset == mBakedTextureDatas[BAKED_HEAD].mTexLayerSet) return getLocalDiscardLevel(TEX_HEAD_BODYPAINT) >= 0; */ for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const EBakedTextureIndex baked_index = baked_iter->first; if (layerset == mBakedTextureDatas[baked_index].mTexLayerSet) { const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = baked_iter->second; text += llformat("%d-%s ( ",baked_index, baked_dict->mName.c_str()); for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); if (wearable_count > 0) { text += wr_inst->getTypeName(wearable_type) + ":"; for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { const U32 discard_level = getLocalDiscardLevel(tex_index, wearable_index); std::string discard_str = llformat("%d ",discard_level); text += llformat("%d ",discard_level); } } } text += ")"; break; } } return text; } const std::string LLVOAvatarSelf::debugDumpAllLocalTextureDataInfo() const { std::string text; const U32 override_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel"); for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = sAvatarDictionary->getBakedTexture((EBakedTextureIndex)i); bool is_texture_final = true; for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin(); local_tex_iter != baked_dict->mLocalTextures.end(); ++local_tex_iter) { const ETextureIndex tex_index = *local_tex_iter; const LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(tex_index); const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type); for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++) { is_texture_final &= (getLocalDiscardLevel(*local_tex_iter, wearable_index) <= (S32)(override_tex_discard_level)); } } text += llformat("%s:%d ",baked_dict->mName.c_str(),is_texture_final); } return text; } void LLVOAvatarSelf::appearanceChangeMetricsCoro(std::string url) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("appearanceChangeMetrics", httpPolicy)); LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); S32 currentSequence = mMetricSequence; if (S32_MAX == ++mMetricSequence) mMetricSequence = 0; LLSD msg; msg["message"] = "ViewerAppearanceChangeMetrics"; msg["session_id"] = gAgentSessionID; msg["agent_id"] = gAgentID; msg["sequence"] = currentSequence; msg["initial"] = mInitialMetric; msg["break"] = false; msg["duration"] = mTimeSinceLastRezMessage.getElapsedTimeF32(); // Status of our own rezzing. msg["rez_status"] = LLVOAvatar::rezStatusToString(getRezzedStatus()); msg["first_decloud_time"] = getFirstDecloudTime(); // Status of all nearby avs including ourself. msg["nearby"] = LLSD::emptyArray(); std::vector rez_counts; F32 avg_time; S32 total_cloud_avatars; LLVOAvatar::getNearbyRezzedStats(rez_counts, avg_time, total_cloud_avatars); for (S32 rez_stat = 0; rez_stat < rez_counts.size(); ++rez_stat) { std::string rez_status_name = LLVOAvatar::rezStatusToString(rez_stat); msg["nearby"][rez_status_name] = rez_counts[rez_stat]; } msg["nearby"]["avg_decloud_time"] = avg_time; msg["nearby"]["cloud_total"] = total_cloud_avatars; // std::vector bucket_fields("timer_name","is_self","grid_x","grid_y","is_using_server_bake"); std::vector by_fields; by_fields.push_back("timer_name"); by_fields.push_back("completed"); by_fields.push_back("grid_x"); by_fields.push_back("grid_y"); by_fields.push_back("is_using_server_bakes"); by_fields.push_back("is_self"); by_fields.push_back("central_bake_version"); LLSD summary = summarize_by_buckets(mPendingTimerRecords, by_fields, std::string("elapsed")); msg["timers"] = summary; mPendingTimerRecords.clear(); LL_DEBUGS("Avatar") << avString() << "message: " << ll_pretty_print_sd(msg) << LL_ENDL; gPendingMetricsUploads++; LLSD result = httpAdapter->postAndSuspend(httpRequest, url, msg); LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); gPendingMetricsUploads--; if (!status) { LL_WARNS("Avatar") << "Unable to upload statistics" << LL_ENDL; return; } else { LL_INFOS("Avatar") << "Statistics upload OK" << LL_ENDL; mInitialMetric = false; } } bool LLVOAvatarSelf::updateAvatarRezMetrics(bool force_send) { const F32 AV_METRICS_INTERVAL_QA = 30.0; F32 send_period = 300.0; static LLCachedControl qa_mode_metrics(gSavedSettings,"QAModeMetrics"); if (qa_mode_metrics) { send_period = AV_METRICS_INTERVAL_QA; } if (force_send || mTimeSinceLastRezMessage.getElapsedTimeF32() > send_period) { // Stats for completed phases have been getting logged as they // complete. This will give us stats for any timers that // haven't finished as of the metric's being sent. if (force_send) { LLVOAvatar::logPendingPhasesAllAvatars(); } sendViewerAppearanceChangeMetrics(); } return false; } void LLVOAvatarSelf::addMetricsTimerRecord(const LLSD& record) { mPendingTimerRecords.push_back(record); } bool operator<(const LLSD& a, const LLSD& b) { std::ostringstream aout, bout; aout << LLSDNotationStreamer(a); bout << LLSDNotationStreamer(b); std::string astring = aout.str(); std::string bstring = bout.str(); return astring < bstring; } // Given a vector of LLSD records, return an LLSD array of bucketed stats for val_field. LLSD summarize_by_buckets(std::vector in_records, std::vector by_fields, std::string val_field) { LLSD result = LLSD::emptyArray(); std::map accum; for (std::vector::iterator in_record_iter = in_records.begin(); in_record_iter != in_records.end(); ++in_record_iter) { LLSD& record = *in_record_iter; LLSD key; for (std::vector::iterator field_iter = by_fields.begin(); field_iter != by_fields.end(); ++field_iter) { const std::string& field = *field_iter; key[field] = record[field]; } LLViewerStats::StatsAccumulator& stats = accum[key]; F32 value = (F32)record[val_field].asReal(); stats.push(value); } for (std::map::iterator accum_it = accum.begin(); accum_it != accum.end(); ++accum_it) { LLSD out_record = accum_it->first; out_record["stats"] = accum_it->second.asLLSD(); result.append(out_record); } return result; } void LLVOAvatarSelf::sendViewerAppearanceChangeMetrics() { std::string caps_url; if (getRegion()) { // runway - change here to activate. caps_url = getRegion()->getCapability("ViewerMetrics"); } if (!caps_url.empty()) { LLCoros::instance().launch("LLVOAvatarSelf::appearanceChangeMetricsCoro", boost::bind(&LLVOAvatarSelf::appearanceChangeMetricsCoro, this, caps_url)); mTimeSinceLastRezMessage.reset(); } } const LLUUID& LLVOAvatarSelf::grabBakedTexture(EBakedTextureIndex baked_index) const { if (canGrabBakedTexture(baked_index)) { ETextureIndex tex_index = sAvatarDictionary->bakedToLocalTextureIndex(baked_index); if (tex_index == TEX_NUM_INDICES) { return LLUUID::null; } return getTEImage( tex_index )->getID(); } return LLUUID::null; } bool LLVOAvatarSelf::canGrabBakedTexture(EBakedTextureIndex baked_index) const { ETextureIndex tex_index = sAvatarDictionary->bakedToLocalTextureIndex(baked_index); if (tex_index == TEX_NUM_INDICES) { return false; } // Check if the texture hasn't been baked yet. if (!isTextureDefined(tex_index, 0)) { LL_DEBUGS() << "getTEImage( " << (U32) tex_index << " )->getID() == IMG_DEFAULT_AVATAR" << LL_ENDL; return false; } if (gAgent.isGodlikeWithoutAdminMenuFakery()) return true; // Check permissions of textures that show up in the // baked texture. We don't want people copying people's // work via baked textures. const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = sAvatarDictionary->getBakedTexture(baked_index); for (texture_vec_t::const_iterator iter = baked_dict->mLocalTextures.begin(); iter != baked_dict->mLocalTextures.end(); ++iter) { const ETextureIndex t_index = (*iter); LLWearableType::EType wearable_type = sAvatarDictionary->getTEWearableType(t_index); U32 count = gAgentWearables.getWearableCount(wearable_type); LL_DEBUGS() << "Checking index " << (U32) t_index << " count: " << count << LL_ENDL; for (U32 wearable_index = 0; wearable_index < count; ++wearable_index) { LLViewerWearable *wearable = gAgentWearables.getViewerWearable(wearable_type, wearable_index); if (wearable) { const LLLocalTextureObject *texture = wearable->getLocalTextureObject((S32)t_index); const LLUUID& texture_id = texture->getID(); if (texture_id != IMG_DEFAULT_AVATAR) { // Search inventory for this texture. LLViewerInventoryCategory::cat_array_t cats; LLViewerInventoryItem::item_array_t items; LLAssetIDMatches asset_id_matches(texture_id); gInventory.collectDescendentsIf(LLUUID::null, cats, items, LLInventoryModel::INCLUDE_TRASH, asset_id_matches); bool can_grab = false; LL_DEBUGS() << "item count for asset " << texture_id << ": " << items.size() << LL_ENDL; if (items.size()) { // search for full permissions version for (S32 i = 0; i < items.size(); i++) { LLViewerInventoryItem* itemp = items[i]; if (itemp->getIsFullPerm()) { can_grab = true; break; } } } if (!can_grab) return false; } } } } return true; } void LLVOAvatarSelf::addLocalTextureStats( ETextureIndex type, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, bool render_avatar, bool covered_by_baked) { if (!isIndexLocalTexture(type)) return; // Sunshine - ignoring covered_by_baked will force local textures // to always load. Fix for SH-4001 and many related issues. Do // not restore this without some more targetted fix for the local // textures failing to load issue. //if (!covered_by_baked) { if (imagep->getID() != IMG_DEFAULT_AVATAR) { imagep->setNoDelete(); if (imagep->getDiscardLevel() != 0) { F32 desired_pixels; desired_pixels = llmin(mPixelArea, (F32)getTexImageArea()); imagep->setBoostLevel(getAvatarBoostLevel()); imagep->resetTextureStats(); imagep->setMaxVirtualSizeResetInterval(MAX_TEXTURE_VIRTUAL_SIZE_RESET_INTERVAL); imagep->addTextureStats( desired_pixels / texel_area_ratio ); imagep->forceUpdateBindStats() ; if (imagep->getDiscardLevel() < 0) { mHasGrey = true; // for statistics gathering } } } else { // texture asset is missing mHasGrey = true; // for statistics gathering } } } LLLocalTextureObject* LLVOAvatarSelf::getLocalTextureObject(LLAvatarAppearanceDefines::ETextureIndex i, U32 wearable_index) const { LLWearableType::EType type = sAvatarDictionary->getTEWearableType(i); LLViewerWearable* wearable = gAgentWearables.getViewerWearable(type, wearable_index); if (wearable) { return wearable->getLocalTextureObject(i); } return NULL; } //----------------------------------------------------------------------------- // getBakedTE() // Used by the LayerSet. (Layer sets don't in general know what textures depend on them.) //----------------------------------------------------------------------------- ETextureIndex LLVOAvatarSelf::getBakedTE( const LLViewerTexLayerSet* layerset ) const { for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { if (layerset == mBakedTextureDatas[i].mTexLayerSet ) { return mBakedTextureDatas[i].mTextureIndex; } } llassert(0); return TEX_HEAD_BAKED; } // FIXME: This is not called consistently. Something may be broken. void LLVOAvatarSelf::outputRezDiagnostics() const { if(!gSavedSettings.getBOOL("DebugAvatarLocalTexLoadedTime")) { return ; } const F32 final_time = mDebugSelfLoadTimer.getElapsedTimeF32(); LL_DEBUGS("Avatar") << "REZTIME: Myself rez stats:" << LL_ENDL; LL_DEBUGS("Avatar") << "\t Time from avatar creation to load wearables: " << (S32)mDebugTimeWearablesLoaded << LL_ENDL; LL_DEBUGS("Avatar") << "\t Time from avatar creation to de-cloud: " << (S32)mDebugTimeAvatarVisible << LL_ENDL; LL_DEBUGS("Avatar") << "\t Time from avatar creation to de-cloud for others: " << (S32)final_time << LL_ENDL; LL_DEBUGS("Avatar") << "\t Load time for each texture: " << LL_ENDL; for (U32 i = 0; i < LLAvatarAppearanceDefines::TEX_NUM_INDICES; ++i) { std::stringstream out; out << "\t\t (" << i << ") "; U32 j=0; for (j=0; j <= MAX_DISCARD_LEVEL; j++) { out << "\t"; S32 load_time = (S32)mDebugTextureLoadTimes[i][j]; if (load_time == -1) { out << "*"; if (j == 0) break; } else { out << load_time; } } // Don't print out non-existent textures. if (j != 0) { LL_DEBUGS("Avatar") << out.str() << LL_ENDL; } } LL_DEBUGS("Avatar") << "\t Time points for each upload (start / finish)" << LL_ENDL; // Missed update for OpenSim BOM // for (U32 i = 0; i < LLAvatarAppearanceDefines::BAKED_NUM_INDICES; ++i) for (S32 i = 0; i < getNumBakes(); ++i) // { LL_DEBUGS("Avatar") << "\t\t (" << i << ") \t" << (S32)mDebugBakedTextureTimes[i][0] << " / " << (S32)mDebugBakedTextureTimes[i][1] << LL_ENDL; } for (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = sAvatarDictionary->getBakedTextures().begin(); baked_iter != sAvatarDictionary->getBakedTextures().end(); ++baked_iter) { const LLAvatarAppearanceDefines::EBakedTextureIndex baked_index = baked_iter->first; const LLViewerTexLayerSet *layerset = debugGetLayerSet(baked_index); if (!layerset) continue; const LLViewerTexLayerSetBuffer *layerset_buffer = layerset->getViewerComposite(); if (!layerset_buffer) continue; LL_DEBUGS("Avatar") << layerset_buffer->dumpTextureInfo() << LL_ENDL; } dumpAllTextures(); } void LLVOAvatarSelf::outputRezTiming(const std::string& msg) const { LL_DEBUGS("Avatar") << avString() << llformat("%s. Time from avatar creation: %.2f", msg.c_str(), mDebugSelfLoadTimer.getElapsedTimeF32()) << LL_ENDL; } void LLVOAvatarSelf::reportAvatarRezTime() const { // TODO: report mDebugSelfLoadTimer.getElapsedTimeF32() somehow. } //-- SUNSHINE CLEANUP - not clear we need any of this, may be sufficient to request server appearance in llviewermenu.cpp:handle_rebake_textures() void LLVOAvatarSelf::forceBakeAllTextures(bool slam_for_debug) { if(!isAgentAvatarValid()){ return; } // Avoid force bak e on invalid avatar pointer (based on similar change by Rye) LL_INFOS() << "TAT: forced full rebake. " << LL_ENDL; for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { ETextureIndex baked_index = mBakedTextureDatas[i].mTextureIndex; LLViewerTexLayerSet* layer_set = getLayerSet(baked_index); if (layer_set) { if (slam_for_debug) { layer_set->setUpdatesEnabled(true); // [Legacy Bake] layer_set->cancelUpload(); } // [Legacy Bake] //invalidateComposite(layer_set); invalidateComposite(layer_set, true); add(LLStatViewer::TEX_REBAKES, 1); } else { LL_WARNS() << "TAT: NO LAYER SET FOR " << (S32)baked_index << LL_ENDL; } } // Don't know if this is needed updateMeshTextures(); } //----------------------------------------------------------------------------- // requestLayerSetUpdate() //----------------------------------------------------------------------------- void LLVOAvatarSelf::requestLayerSetUpdate(ETextureIndex index ) { /* switch(index) case LOCTEX_UPPER_BODYPAINT: case LOCTEX_UPPER_SHIRT: if( mUpperBodyLayerSet ) mUpperBodyLayerSet->requestUpdate(); */ const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = sAvatarDictionary->getTexture(index); if (!texture_dict) return; if (!texture_dict->mIsLocalTexture || !texture_dict->mIsUsedByBakedTexture) return; const EBakedTextureIndex baked_index = texture_dict->mBakedTextureIndex; if (mBakedTextureDatas[baked_index].mTexLayerSet) { mBakedTextureDatas[baked_index].mTexLayerSet->requestUpdate(); } } LLViewerTexLayerSet* LLVOAvatarSelf::getLayerSet(ETextureIndex index) const { /* switch(index) case TEX_HEAD_BAKED: case TEX_HEAD_BODYPAINT: return mHeadLayerSet; */ const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = sAvatarDictionary->getTexture(index); if (texture_dict && texture_dict->mIsUsedByBakedTexture) { const EBakedTextureIndex baked_index = texture_dict->mBakedTextureIndex; return getLayerSet(baked_index); } return NULL; } LLViewerTexLayerSet* LLVOAvatarSelf::getLayerSet(EBakedTextureIndex baked_index) const { /* switch(index) case TEX_HEAD_BAKED: case TEX_HEAD_BODYPAINT: return mHeadLayerSet; */ // BOM fallback support for OpenSim legacy // if (baked_index >= 0 && baked_index < BAKED_NUM_INDICES) if (baked_index >= 0 && baked_index < getNumBakes()) // { return getTexLayerSet(baked_index); } return NULL; } // static void LLVOAvatarSelf::onCustomizeStart(bool disable_camera_switch) { if (isAgentAvatarValid()) { LLUIUsage::instance().logCommand("Avatar.CustomizeStart"); if (!gAgentAvatarp->mEndCustomizeCallback.get()) { gAgentAvatarp->mEndCustomizeCallback = new LLUpdateAppearanceOnDestroy; } gAgentAvatarp->mIsEditingAppearance = true; gAgentAvatarp->mUseLocalAppearance = true; if (gSavedSettings.getBOOL("AppearanceCameraMovement") && !disable_camera_switch) { gAgentCamera.changeCameraToCustomizeAvatar(); } gAgentAvatarp->invalidateAll(); // mark all bakes as dirty, request updates gAgentAvatarp->updateMeshTextures(); // make sure correct textures are applied to the avatar mesh. gAgentAvatarp->updateTextures(); // call updateTextureStats } } // static void LLVOAvatarSelf::onCustomizeEnd(bool disable_camera_switch) { if (isAgentAvatarValid()) { gAgentAvatarp->mIsEditingAppearance = false; // [Legacy Bake] if (gAgentAvatarp->getRegion() && !gAgentAvatarp->getRegion()->getCentralBakeVersion()) { // FIXME DRANO - move to sendAgentSetAppearance, make conditional on upload complete. gAgentAvatarp->mUseLocalAppearance = false; } // [Legacy Bake] gAgentAvatarp->invalidateAll(); // Cinder's fix for STORM-2096 / FIRE-5740 //if (gSavedSettings.getBOOL("AppearanceCameraMovement") && !disable_camera_switch) if (gAgentCamera.cameraCustomizeAvatar() && !disable_camera_switch) // { gAgentCamera.changeCameraToDefault(); gAgentCamera.resetView(); } // Dereferencing the previous callback will cause // updateAppearanceFromCOF to be called, whenever all refs // have resolved. gAgentAvatarp->mEndCustomizeCallback = NULL; } } // virtual bool LLVOAvatarSelf::shouldRenderRigged() const { return gAgent.needsRenderAvatar(); } // HACK: this will null out the avatar's local texture IDs before the TE message is sent // to ensure local texture IDs are not sent to other clients in the area. // this is a short-term solution. The long term solution will be to not set the texture // IDs in the avatar object, and keep them only in the wearable. // This will involve further refactoring that is too risky for the initial release of 2.0. bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const { LLUUID texture_id[TEX_NUM_INDICES]; // pack away current TEs to make sure we don't send them out for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = sAvatarDictionary->getTextures().begin(); iter != sAvatarDictionary->getTextures().end(); ++iter) { const ETextureIndex index = iter->first; const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second; // hide the surplus bakes and universals from non-BOM // if (!texture_dict->mIsBakedTexture) if( (index == TEX_SKIRT || index == TEX_SKIRT_TATTOO) && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT) ) { // TODO(BEQ): combine this with clause below once proven it works. LL_DEBUGS("Avatar") << "Ignoring skirt related texture at index=" << index << LL_ENDL; LLTextureEntry &entry = getTEref((U8) index); texture_id[index] = entry.getID(); entry.setID(IMG_DEFAULT_AVATAR); } if (!texture_dict->mIsBakedTexture || index >= getRegion()->getRegionMaxTEs()) // { LLTextureEntry &entry = getTEref((U8) index); texture_id[index] = entry.getID(); entry.setID(IMG_DEFAULT_AVATAR); } } // hack -- Zwagoth LL_INFOS() << "Setting clientTag" << LL_ENDL; LLTextureEntry* entry = getTE(0); //You edit this to change the tag in your client. Yes. const char* tag_client = "Firestorm"; LLUUID client_name; strncpy((char*)&client_name.mData[0], tag_client, UUID_BYTES); static LLCachedControl tag_color(gSavedPerAccountSettings, "FirestormTagColor", LLColor4(1,0,1,1)); entry->setColor(tag_color); //This glow is used to tell if the tag color is set or not. entry->setGlow(0.1f); entry->setID(client_name); // Need to reset these if you turn off the tag system without relogging, they persist otherwise. // //LL_INFOS() << "Clearing clientTag" << LL_ENDL; //LLTextureEntry* entry = getTE(0); //if(entry->getGlow()>0.f) //{ // entry->setGlow(0.0f); // entry->setColor(LLColor4::white); //} // bool success = packTEMessage(mesgsys); // unpack TEs to make sure we don't re-trigger a bake for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = sAvatarDictionary->getTextures().begin(); iter != sAvatarDictionary->getTextures().end(); ++iter) { const ETextureIndex index = iter->first; const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second; // hide the surplus bakes and universals from non-BOM // if (!texture_dict->mIsBakedTexture) if (!texture_dict->mIsBakedTexture || index >= getRegion()->getRegionMaxTEs()) // { LLTextureEntry &entry = getTEref((U8) index); entry.setID(texture_id[index]); } } return success; } //------------------------------------------------------------------------ // sendHoverHeight() //------------------------------------------------------------------------ void LLVOAvatarSelf::sendHoverHeight() const { std::string url = gAgent.getRegionCapability("AgentPreferences"); if (!url.empty()) { LLSD update = LLSD::emptyMap(); const LLVector3& hover_offset = getHoverOffset(); update["hover_height"] = hover_offset[2]; LL_DEBUGS("Avatar") << avString() << "sending hover height value " << hover_offset[2] << LL_ENDL; // *TODO: - this class doesn't really do anything, could just use a base // class responder if nothing else gets added. // (comment from removed Responder) LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, update, "Hover height sent to sim", "Hover height not sent to sim"); mLastHoverOffsetSent = hover_offset; } } void LLVOAvatarSelf::setHoverOffset(const LLVector3& hover_offset, bool send_update) { if (getHoverOffset() != hover_offset) { LL_INFOS("Avatar") << avString() << " setting hover due to change " << hover_offset[2] << LL_ENDL; LLVOAvatar::setHoverOffset(hover_offset, send_update); } if (send_update && (hover_offset != mLastHoverOffsetSent)) { LL_INFOS("Avatar") << avString() << " sending hover due to change " << hover_offset[2] << LL_ENDL; sendHoverHeight(); } } //------------------------------------------------------------------------ // needsRenderBeam() //------------------------------------------------------------------------ bool LLVOAvatarSelf::needsRenderBeam() { LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); bool is_touching_or_grabbing = (tool == LLToolGrab::getInstance() && LLToolGrab::getInstance()->isEditing()); LLViewerObject* objp = LLToolGrab::getInstance()->getEditingObject(); if (objp // might need to be "!objp ||" instead of "objp &&". && (objp->isAttachment() || objp->isAvatar())) { // don't render grab tool's selection beam on hud objects, // attachments or avatars is_touching_or_grabbing = false; } return is_touching_or_grabbing || (getAttachmentState() & AGENT_STATE_EDITING && LLSelectMgr::getInstance()->shouldShowSelection()); } // static void LLVOAvatarSelf::deleteScratchTextures() { for(std::map< LLGLenum, LLGLuint*>::iterator it = sScratchTexNames.begin(), end_it = sScratchTexNames.end(); it != end_it; ++it) { LLImageGL::deleteTextures(1, (U32 *)it->second ); stop_glerror(); } if( sScratchTexBytes.value() ) { LL_DEBUGS() << "Clearing Scratch Textures " << (S32Kilobytes)sScratchTexBytes << LL_ENDL; delete_and_clear(sScratchTexNames); sScratchTexBytes = S32Bytes(0); } } // static void LLVOAvatarSelf::dumpScratchTextureByteCount() { LL_INFOS() << "Scratch Texture GL: " << (sScratchTexBytes/1024) << "KB" << LL_ENDL; } void LLVOAvatarSelf::dumpWearableInfo(LLAPRFile& outfile) { // Remove LLVolatileAPRPool/apr_file_t and use FILE* instead // apr_file_t* file = outfile.getFileHandle(); LLAPRFile::tFiletype* file = outfile.getFileHandle(); // if (!file) { return; } apr_file_printf( file, "\n\n" ); LLWearableData *wd = getWearableData(); LLWearableType *wr_inst = LLWearableType::getInstance(); for (S32 type = 0; type < LLWearableType::WT_COUNT; type++) { const std::string& type_name = wr_inst->getTypeName((LLWearableType::EType)type); for (U32 j=0; j< wd->getWearableCount((LLWearableType::EType)type); j++) { LLViewerWearable *wearable = gAgentWearables.getViewerWearable((LLWearableType::EType)type,j); apr_file_printf( file, "\n\t \n", type_name.c_str(), wearable->getName().c_str() ); LLWearable::visual_param_vec_t v_params; wearable->getVisualParams(v_params); for (LLWearable::visual_param_vec_t::iterator it = v_params.begin(); it != v_params.end(); ++it) { LLVisualParam *param = *it; dump_visual_param(file, param, param->getWeight()); } } } apr_file_printf( file, "\n\n" ); } // [RLVa:KB] - Checked: 2013-03-03 (RLVa-1.4.8) F32 LLVOAvatarSelf::getAvatarOffset() /*const*/ { #ifdef OPENSIM if (!LLGridManager::getInstance()->isInSecondLife()) { return (isUsingServerBakes()) ? LLAvatarAppearance::getAvatarOffset() : gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ"); } #endif return LLAvatarAppearance::getAvatarOffset(); } // [/RLVa:KB] // [Legacy Bake] //----------------------------------------------------------------------------- // Legacy baking //----------------------------------------------------------------------------- //virtual U32 LLVOAvatarSelf::processUpdateMessage(LLMessageSystem *mesgsys, void **user_data, U32 block_num, const EObjectUpdateType update_type, LLDataPacker *dp) { U32 retval = LLVOAvatar::processUpdateMessage(mesgsys,user_data,block_num,update_type,dp); return retval; } bool LLVOAvatarSelf::isBakedTextureFinal(const LLAvatarAppearanceDefines::EBakedTextureIndex index) const { const LLViewerTexLayerSet *layerset = getLayerSet(index); if (!layerset) return false; const LLViewerTexLayerSetBuffer *layerset_buffer = layerset->getViewerComposite(); if (!layerset_buffer) return false; return !layerset_buffer->uploadNeeded(); } //----------------------------------------------------------------------------- // requestLayerSetUploads() //----------------------------------------------------------------------------- void LLVOAvatarSelf::requestLayerSetUploads() { for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { requestLayerSetUpload((EBakedTextureIndex)i); } } void LLVOAvatarSelf::requestLayerSetUpload(LLAvatarAppearanceDefines::EBakedTextureIndex i) { ETextureIndex tex_index = mBakedTextureDatas[i].mTextureIndex; const bool layer_baked = isTextureDefined(tex_index, gAgentWearables.getWearableCount(tex_index)); LLViewerTexLayerSet *layerset = getLayerSet(i); if (!layer_baked && layerset) { layerset->requestUpload(); } } // virtual bool LLVOAvatarSelf::hasPendingBakedUploads() const { // BOMOS constrain uploads for non-BOM. // for (U32 i = 0; i < mBakedTextureDatas.size(); i++) for (S32 i = 0; i < getNumBakes(); i++) { LLViewerTexLayerSet* layerset = getTexLayerSet(i); if (layerset && layerset->getViewerComposite() && layerset->getViewerComposite()->uploadPending()) { return true; } } return false; } void CheckAgentAppearanceService_httpSuccess( LLSD const &aData ) { LL_DEBUGS("Avatar") << "OK" << LL_ENDL; } void forceAppearanceUpdate() { // Trying to rebake immediately after crossing region boundary // seems to be failure prone; adding a delay factor. Yes, this // fix is ad-hoc and not guaranteed to work in all cases. // Never trust the avatarp lifetime. // doAfterInterval(boost::bind(&LLVOAvatarSelf::forceBakeAllTextures, gAgentAvatarp.get(), true), 5.0); doAfterInterval([](){ if (isAgentAvatarValid()) { gAgentAvatarp->forceBakeAllTextures(true); }}, 5.0); // } void CheckAgentAppearanceService_httpFailure( LLSD const &aData ) { if (isAgentAvatarValid()) { LL_DEBUGS("Avatar") << "failed, will rebake " << aData << LL_ENDL; forceAppearanceUpdate(); } } void LLVOAvatarSelf::checkForUnsupportedServerBakeAppearance() { // Need to check only if we have a server baked appearance and are // in a non-baking region. if (!gAgentAvatarp->isUsingServerBakes()) return; if (!gAgent.getRegion() || gAgent.getRegion()->getCentralBakeVersion()!=0) return; // if baked image service is unknown, need to refresh. if (LLAppearanceMgr::instance().getAppearanceServiceURL().empty()) { forceAppearanceUpdate(); } // query baked image service to check status. std::string image_url = gAgentAvatarp->getImageURL(TEX_HEAD_BAKED, getTE(TEX_HEAD_BAKED)->getID()); LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet( image_url, CheckAgentAppearanceService_httpSuccess, CheckAgentAppearanceService_httpFailure ); } void LLVOAvatarSelf::setNewBakedTexture(LLAvatarAppearanceDefines::EBakedTextureIndex i, const LLUUID &uuid) { ETextureIndex index = LLAvatarAppearance::getDictionary()->bakedToLocalTextureIndex(i); setNewBakedTexture(index, uuid); } //----------------------------------------------------------------------------- // setNewBakedTexture() // A new baked texture has been successfully uploaded and we can start using it now. //----------------------------------------------------------------------------- void LLVOAvatarSelf::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid ) { // Baked textures live on other sims. LLHost target_host = getObjectHost(); setTEImage( te, LLViewerTextureManager::getFetchedTextureFromHost( uuid, FTT_HOST_BAKE, target_host ) ); updateMeshTextures(); dirtyMesh(); LLVOAvatar::cullAvatarsByPixelArea(); /* switch(te) case TEX_HEAD_BAKED: LL_INFOS() << "New baked texture: HEAD" << LL_ENDL; */ const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearance::getDictionary()->getTexture(te); if (texture_dict->mIsBakedTexture) { debugBakedTextureUpload(texture_dict->mBakedTextureIndex, true); // false for start of upload, true for finish. LL_INFOS() << "New baked texture: " << texture_dict->mName << " UUID: " << uuid <cancelUpload(); */ for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { LLViewerTexLayerSet *layerset = getTexLayerSet(i); if ( mBakedTextureDatas[i].mTextureIndex == te && layerset) { layerset->cancelUpload(); } } } // static void LLVOAvatarSelf::processRebakeAvatarTextures(LLMessageSystem* msg, void**) { LLUUID texture_id; msg->getUUID("TextureData", "TextureID", texture_id); if (!isAgentAvatarValid()) return; // If this is a texture corresponding to one of our baked entries, // just rebake that layer set. bool found{ false }; /* ETextureIndex baked_texture_indices[BAKED_NUM_INDICES] = TEX_HEAD_BAKED, TEX_UPPER_BAKED, */ for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearance::getDictionary()->getTextures().begin(); iter != LLAvatarAppearance::getDictionary()->getTextures().end(); ++iter) { const ETextureIndex index = iter->first; const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second; if (texture_dict->mIsBakedTexture) { if (texture_id == gAgentAvatarp->getTEImage(index)->getID()) { LLViewerTexLayerSet* layer_set = gAgentAvatarp->getLayerSet(index); if (layer_set) { LL_INFOS() << "TAT: rebake - matched entry " << (S32)index << LL_ENDL; gAgentAvatarp->invalidateComposite(layer_set, true); found = true; add(LLStatViewer::TEX_REBAKES, 1); } } } } // If texture not found, rebake all entries. if (!found) { gAgentAvatarp->forceBakeAllTextures(); } else { // Not sure if this is necessary, but forceBakeAllTextures() does it. gAgentAvatarp->updateMeshTextures(); } } // [Legacy Bake]