diff options
Diffstat (limited to 'indra/newview/llvoavatar.cpp')
-rw-r--r--[-rwxr-xr-x] | indra/newview/llvoavatar.cpp | 579 |
1 files changed, 362 insertions, 217 deletions
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 6f7b23ba01..1e9de3ba94 100755..100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1,4 +1,4 @@ -/** +/** * @File llvoavatar.cpp * @brief Implementation of LLVOAvatar class which is a derivation of LLViewerObject * @@ -43,6 +43,7 @@ #include "llanimationstates.h" #include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" +#include "llavatarrendernotifier.h" #include "llexperiencecache.h" #include "llphysicsmotion.h" #include "llviewercontrol.h" @@ -115,8 +116,6 @@ extern U32 JOINT_COUNT_REQUIRED_FOR_FULLRIG; const F32 MAX_HOVER_Z = 2.0; const F32 MIN_HOVER_Z = -2.0; -// #define OUTPUT_BREAST_DATA - using namespace LLAvatarAppearanceDefines; //----------------------------------------------------------------------------- @@ -186,6 +185,8 @@ const F32 NAMETAG_UPDATE_THRESHOLD = 0.3f; const F32 NAMETAG_VERTICAL_SCREEN_OFFSET = 25.f; const F32 NAMETAG_VERT_OFFSET_WEIGHT = 0.17f; +const U32 LLVOAvatar::VISUAL_COMPLEXITY_UNKNOWN = 0; + enum ERenderName { RENDER_NAME_NEVER, @@ -622,7 +623,7 @@ private: //----------------------------------------------------------------------------- LLAvatarAppearanceDictionary *LLVOAvatar::sAvatarDictionary = NULL; S32 LLVOAvatar::sFreezeCounter = 0; -U32 LLVOAvatar::sMaxVisible = 12; +U32 LLVOAvatar::sMaxNonImpostors = 12; // overridden based on graphics setting F32 LLVOAvatar::sRenderDistance = 256.f; S32 LLVOAvatar::sNumVisibleAvatars = 0; S32 LLVOAvatar::sNumLODChangesThisFrame = 0; @@ -649,7 +650,7 @@ BOOL LLVOAvatar::sShowFootPlane = FALSE; BOOL LLVOAvatar::sVisibleInFirstPerson = FALSE; F32 LLVOAvatar::sLODFactor = 1.f; F32 LLVOAvatar::sPhysicsLODFactor = 1.f; -BOOL LLVOAvatar::sUseImpostors = FALSE; +bool LLVOAvatar::sUseImpostors = false; // overwridden by RenderAvatarMaxNonImpostors BOOL LLVOAvatar::sJointDebug = FALSE; F32 LLVOAvatar::sUnbakedTime = 0.f; F32 LLVOAvatar::sUnbakedUpdateTime = 0.f; @@ -670,9 +671,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, LLAvatarAppearance(&gAgentWearables), LLViewerObject(id, pcode, regionp), mSpecialRenderMode(0), - mAttachmentGeometryBytes(-1), - mAttachmentSurfaceArea(-1.f), - mReportedVisualComplexity(-1), + mAttachmentSurfaceArea(0.f), + mReportedVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN), mTurning(FALSE), mLastSkeletonSerialNum( 0 ), mIsSitting(FALSE), @@ -702,12 +702,14 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mNeedsSkin(FALSE), mLastSkinTime(0.f), mUpdatePeriod(1), + mVisualComplexityStale(true), + mVisuallyMuteSetting(AV_RENDER_NORMALLY), + mMutedAVColor(LLColor4::white /* used for "uninitialize" */), mFirstFullyVisible(TRUE), mFullyLoaded(FALSE), mPreviousFullyLoaded(FALSE), mFullyLoadedInitialized(FALSE), - mVisualComplexity(0), - mVisualComplexityStale(TRUE), + mVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN), mLoadedCallbacksPaused(FALSE), mRenderUnloadedAvatar(LLCachedControl<bool>(gSavedSettings, "RenderUnloadedAvatar", false)), mLastRezzedStatus(-1), @@ -718,6 +720,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mCachedMuteListUpdateTime(0), mCachedInMuteList(false) { + LL_DEBUGS("AvatarRender") << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << LL_ENDL; + //VTResume(); // VTune setHoverOffset(LLVector3(0.0, 0.0, 0.0)); @@ -726,7 +730,6 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mVoiceVisualizer = ( LLVoiceVisualizer *)LLHUDManager::getInstance()->createViewerEffect( LLHUDObject::LL_HUD_EFFECT_VOICE_VISUALIZER, needsSendToSim ); LL_DEBUGS("Avatar","Message") << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << LL_ENDL; - mPelvisp = NULL; mDirtyMesh = 2; // Dirty geometry, need to regenerate. @@ -775,17 +778,10 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mDebugExistenceTimer.reset(); mLastAppearanceMessageTimer.reset(); - if(LLSceneMonitor::getInstance()->isEnabled()) + if(LLSceneMonitor::getInstance()->isEnabled()) { - LLSceneMonitor::getInstance()->freezeAvatar((LLCharacter*)this); + LLSceneMonitor::getInstance()->freezeAvatar((LLCharacter*)this); } - - mCachedVisualMute = !isSelf(); - mCachedVisualMuteUpdateTime = LLFrameTimer::getTotalSeconds() + 5.0; - mVisuallyMuteSetting = VISUAL_MUTE_NOT_SET; - - F32 color_value = (F32) (getID().mData[0]); - mMutedAVColor = calcMutedAVColor(color_value, 0, 256); } std::string LLVOAvatar::avString() const @@ -993,8 +989,8 @@ std::string LLVOAvatar::rezStatusToString(S32 rez_status) { if (rez_status==0) return "cloud"; if (rez_status==1) return "gray"; - if (rez_status==2) return "textured"; - if (rez_status==3) return "textured_and_downloaded"; + if (rez_status==2) return "downloading"; + if (rez_status==3) return "full"; return "unknown"; } @@ -1106,6 +1102,7 @@ void LLVOAvatar::resetImpostors() { LLVOAvatar* avatar = (LLVOAvatar*) *iter; avatar->mImpostor.release(); + avatar->mNeedsImpostorUpdate = TRUE; } } @@ -1992,7 +1989,7 @@ LLViewerFetchedTexture *LLVOAvatar::getBakedTextureImage(const U8 te, const LLUU uuid == IMG_INVISIBLE) { // Should already exist, don't need to find it on sim or baked-texture host. - result = gTextureList.findImage(uuid); + result = gTextureList.findImage(uuid, TEX_LIST_DISCARD); } if (!result) { @@ -2156,7 +2153,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time) } idleUpdateNameTag( mLastRootPos ); - idleUpdateRenderCost(); + idleUpdateRenderComplexity(); } void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) @@ -2496,19 +2493,22 @@ void LLVOAvatar::idleUpdateLoadingEffect() // update visibility when avatar is partially loaded if (updateIsFullyLoaded()) // changed? { - if (isFullyLoaded() && mFirstFullyVisible && isSelf()) - { - LL_INFOS("Avatar") << avString() << "self isFullyLoaded, mFirstFullyVisible" << LL_ENDL; - mFirstFullyVisible = FALSE; - LLAppearanceMgr::instance().onFirstFullyVisible(); - } - if (isFullyLoaded() && mFirstFullyVisible && !isSelf()) - { - LL_INFOS("Avatar") << avString() << "other isFullyLoaded, mFirstFullyVisible" << LL_ENDL; - mFirstFullyVisible = FALSE; - } if (isFullyLoaded()) { + if (mFirstFullyVisible) + { + mFirstFullyVisible = FALSE; + if (isSelf()) + { + LL_INFOS("Avatar") << avString() << "self isFullyLoaded, mFirstFullyVisible" << LL_ENDL; + LLAppearanceMgr::instance().onFirstFullyVisible(); + } + else + { + LL_INFOS("Avatar") << avString() << "other isFullyLoaded, mFirstFullyVisible" << LL_ENDL; + } + } + deleteParticleSource(); updateLOD(); } @@ -2733,7 +2733,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) } else { - is_muted = LLMuteList::getInstance()->isMuted(getID()); + is_muted = isInMuteList(); } bool is_friend = LLAvatarTracker::instance().isBuddy(getID()); bool is_cloud = getIsCloud(); @@ -3091,86 +3091,32 @@ bool LLVOAvatar::isVisuallyMuted() { bool muted = false; + // Priority order (highest priority first) + // * own avatar is never visually muted + // * if on the "always draw normally" list, draw them normally + // * if on the "always visually mute" list, mute them + // * check against the render cost and attachment limits if (!isSelf()) { - static LLCachedControl<U32> render_auto_mute_functions(gSavedSettings, "RenderAutoMuteFunctions", 0); - if (render_auto_mute_functions) // Hacky debug switch for developing feature + if (mVisuallyMuteSetting == AV_ALWAYS_RENDER) { - // Priority order (highest priority first) - // * own avatar is never visually muted - // * if on the "always draw normally" list, draw them normally - // * if on the "always visually mute" list, mute them - // * draw them normally if they meet the following criteria: - // - within the closest N avatars OR on friends list OR in an IM chat - // - AND aren't over the thresholds - // * otherwise visually mute all other avatars - - static LLCachedControl<U32> max_attachment_bytes(gSavedSettings, "RenderAutoMuteByteLimit", 0); - static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit", 0.0); - static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAutoMuteRenderWeightLimit", 0); - - if (mVisuallyMuteSetting == ALWAYS_VISUAL_MUTE) - { // Always want to see this AV as an impostor - muted = true; - } - else if (mVisuallyMuteSetting == NEVER_VISUAL_MUTE) - { // Never show as impostor - muted = false; - } - else - { - F64 now = LLFrameTimer::getTotalSeconds(); - - if (now < mCachedVisualMuteUpdateTime) - { // Use cached mute value - muted = mCachedVisualMute; - } - else - { // Determine if visually muted or not - - U32 max_cost = (U32) (max_render_cost*(LLVOAvatar::sLODFactor+0.5)); - - muted = (mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0) || - (mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f) || - (mVisualComplexity > max_cost && max_render_cost > 0); - - // Could be part of the grand || collection above, but yanked out to make the logic visible - if (!muted) - { - if (sMaxVisible > 0) - { // They are above the visibilty rank - mute them - muted = (mVisibilityRank > sMaxVisible); - } - - // Always draw friends or those in IMs. Needs UI? - if ((render_auto_mute_functions & 0x02) && - (muted || sMaxVisible == 0)) // Don't mute friends or IMs - { - muted = !(LLAvatarTracker::instance().isBuddy(getID())); - if (muted) - { // Not a friend, so they are muted ... are they in an IM? - LLUUID session_id = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL,getID()); - muted = !gIMMgr->hasSession(session_id); - } - } - } - - // Save visual mute state and set interval for updating - const F64 SECONDS_BETWEEN_RENDER_AUTO_MUTE_UPDATES = 1.5; - mCachedVisualMuteUpdateTime = now + SECONDS_BETWEEN_RENDER_AUTO_MUTE_UPDATES; - mCachedVisualMute = muted; - } - } + muted = false; + } + else if (mVisuallyMuteSetting == AV_DO_NOT_RENDER) + { // Always want to see this AV as an impostor + muted = true; + } + else if (isInMuteList()) + { + muted = true; + } + else + { + muted = isTooComplex(); } } - return muted || isInMuteList(); -} - -void LLVOAvatar::forceUpdateVisualMuteSettings() -{ - // Set the cache time so it's updated ASAP - mCachedVisualMuteUpdateTime = LLFrameTimer::getTotalSeconds() - 1.0; + return muted; } bool LLVOAvatar::isInMuteList() @@ -3335,18 +3281,18 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) { // visually muted avatars update at 16 hz mUpdatePeriod = 16; } - else if (mVisibilityRank <= LLVOAvatar::sMaxVisible || - mDrawable->mDistanceWRTCamera < 1.f + mag) - { //first 25% of max visible avatars are not impostored - //also, don't impostor avatars whose bounding box may be penetrating the - //impostor camera near clip plane + else if ( ! shouldImpostor() + || mDrawable->mDistanceWRTCamera < 1.f + mag) + { // first 25% of max visible avatars are not impostored + // also, don't impostor avatars whose bounding box may be penetrating the + // impostor camera near clip plane mUpdatePeriod = 1; } - else if (mVisibilityRank > LLVOAvatar::sMaxVisible * 4) + else if ( shouldImpostor(4) ) { //background avatars are REALLY slow updating impostors mUpdatePeriod = 16; } - else if (mVisibilityRank > LLVOAvatar::sMaxVisible * 3) + else if ( shouldImpostor(3) ) { //back 25% of max visible avatars are slow updating impostors mUpdatePeriod = 8; } @@ -3930,6 +3876,10 @@ void LLVOAvatar::updateVisibility() } } + if ( visible != mVisible ) + { + LL_DEBUGS("AvatarRender") << "visible was " << mVisible << " now " << visible << LL_ENDL; + } mVisible = visible; } @@ -4119,11 +4069,9 @@ U32 LLVOAvatar::renderSkinned() BOOL first_pass = TRUE; if (!LLDrawPoolAvatar::sSkipOpaque) { - bool visually_muted = isVisuallyMuted(); - if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender) { - if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy || visually_muted) + if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy) { LLViewerJoint* head_mesh = getViewerJoint(MESH_ID_HEAD); if (head_mesh) @@ -4133,7 +4081,7 @@ U32 LLVOAvatar::renderSkinned() first_pass = FALSE; } } - if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy || visually_muted) + if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy) { LLViewerJoint* upper_mesh = getViewerJoint(MESH_ID_UPPER_BODY); if (upper_mesh) @@ -4143,7 +4091,7 @@ U32 LLVOAvatar::renderSkinned() first_pass = FALSE; } - if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy || visually_muted) + if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy) { LLViewerJoint* lower_mesh = getViewerJoint(MESH_ID_LOWER_BODY); if (lower_mesh) @@ -4202,8 +4150,8 @@ U32 LLVOAvatar::renderTransparent(BOOL first_pass) } // Can't test for baked hair being defined, since that won't always be the case (not all viewers send baked hair) // TODO: 1.25 will be able to switch this logic back to calling isTextureVisible(); - if ( ( getImage(TEX_HAIR_BAKED, 0) && - getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE ) || LLDrawPoolAlpha::sShowDebugAlpha) + if ( (getImage(TEX_HAIR_BAKED, 0) && getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE) + || LLDrawPoolAlpha::sShowDebugAlpha) { LLViewerJoint* hair_mesh = getViewerJoint(MESH_ID_HAIR); if (hair_mesh) @@ -4313,7 +4261,7 @@ bool LLVOAvatar::allTexturesCompletelyDownloaded(std::set<LLUUID>& ids) const { for (std::set<LLUUID>::const_iterator it = ids.begin(); it != ids.end(); ++it) { - LLViewerFetchedTexture *imagep = gTextureList.findImage(*it); + LLViewerFetchedTexture *imagep = gTextureList.findImage(*it, TEX_LIST_DISCARD); if (imagep && imagep->getDiscardLevel()!=0) { return false; @@ -4385,7 +4333,7 @@ S32Bytes LLVOAvatar::totalTextureMemForUUIDS(std::set<LLUUID>& ids) S32Bytes result(0); for (std::set<LLUUID>::const_iterator it = ids.begin(); it != ids.end(); ++it) { - LLViewerFetchedTexture *imagep = gTextureList.findImage(*it); + LLViewerFetchedTexture *imagep = gTextureList.findImage(*it, TEX_LIST_DISCARD); if (imagep) { result += imagep->getTextureMemory(); @@ -4473,7 +4421,7 @@ void LLVOAvatar::releaseOldTextures() { if (new_texture_ids.find(*it) == new_texture_ids.end()) { - LLViewerFetchedTexture *imagep = gTextureList.findImage(*it); + LLViewerFetchedTexture *imagep = gTextureList.findImage(*it, TEX_LIST_DISCARD); if (imagep) { current_texture_mem += imagep->getTextureMemory(); @@ -5840,7 +5788,7 @@ const LLViewerJointAttachment *LLVOAvatar::attachObject(LLViewerObject *viewer_o return 0; } - mVisualComplexityStale = TRUE; + updateVisualComplexity(); if (viewer_object->isSelected()) { @@ -5996,7 +5944,7 @@ BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object) if (attachment->isObjectAttached(viewer_object)) { - mVisualComplexityStale = TRUE; + updateVisualComplexity(); cleanupAttachedMesh( viewer_object ); attachment->removeObject(viewer_object); @@ -6298,32 +6246,20 @@ BOOL LLVOAvatar::isVisible() const } // Determine if we have enough avatar data to render -BOOL LLVOAvatar::getIsCloud() const +bool LLVOAvatar::getIsCloud() const { - // Do we have a shape? - if ((const_cast<LLVOAvatar*>(this))->visualParamWeightsAreDefault()) - { - return TRUE; - } - - if (!isTextureDefined(TEX_LOWER_BAKED) || - !isTextureDefined(TEX_UPPER_BAKED) || - !isTextureDefined(TEX_HEAD_BAKED)) - { - return TRUE; - } - - if (isTooComplex()) - { - return TRUE; - } - return FALSE; + return ( ((const_cast<LLVOAvatar*>(this))->visualParamWeightsAreDefault())// Do we have a shape? + || ( !isTextureDefined(TEX_LOWER_BAKED) + || !isTextureDefined(TEX_UPPER_BAKED) + || !isTextureDefined(TEX_HEAD_BAKED) + ) + ); } void LLVOAvatar::updateRezzedStatusTimers() { // State machine for rezzed status. Statuses are -1 on startup, 0 - // = cloud, 1 = gray, 2 = textured, 3 = textured_and_downloaded. + // = cloud, 1 = gray, 2 = downloading, 3 = full. // Purpose is to collect time data for each it takes avatar to reach // various loading landmarks: gray, textured (partial), textured fully. @@ -6363,6 +6299,8 @@ void LLVOAvatar::updateRezzedStatusTimers() selfStopPhase("update_appearance_from_cof"); selfStopPhase("wear_inventory_category", false); selfStopPhase("process_initial_wearables_update", false); + + updateVisualComplexity(); } } mLastRezzedStatus = rez_status; @@ -6495,7 +6433,7 @@ void LLVOAvatar::logMetricsTimerRecord(const std::string& phase_name, F32 elapse // returns true if the value has changed. BOOL LLVOAvatar::updateIsFullyLoaded() { - const BOOL loading = getIsCloud(); + const bool loading = getIsCloud(); updateRezzedStatusTimers(); updateRuthTimer(loading); return processFullyLoadedChange(loading); @@ -6556,6 +6494,12 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading) mPreviousFullyLoaded = mFullyLoaded; mFullyLoadedInitialized = TRUE; mFullyLoadedFrameCounter++; + + if (changed && isSelf()) + { + // to know about outfit switching + LLAvatarRenderNotifier::getInstance()->updateNotificationState(); + } return changed; } @@ -6567,15 +6511,28 @@ BOOL LLVOAvatar::isFullyLoaded() const bool LLVOAvatar::isTooComplex() const { - if (gSavedSettings.getS32("RenderAvatarComplexityLimit") > 0 && mVisualComplexity >= gSavedSettings.getS32("RenderAvatarComplexityLimit")) + bool too_complex; + if (isSelf() || mVisuallyMuteSetting == AV_ALWAYS_RENDER) + { + too_complex = false; + } + else { - return true; + // Determine if visually muted or not + static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAvatarMaxComplexity", 0U); + static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit", 1000.0f); + // If the user has chosen unlimited max complexity, we also disregard max attachment area + // so that unlimited will completely disable the overly complex impostor rendering + // yes, this leaves them vulnerable to griefing objects... their choice + too_complex = ( max_render_cost > 0 + && ( mVisualComplexity > max_render_cost + || (max_attachment_area > 0.0f && mAttachmentSurfaceArea > max_attachment_area) + )); } - return false; + return too_complex; } - //----------------------------------------------------------------------------- // findMotion() //----------------------------------------------------------------------------- @@ -7356,6 +7313,7 @@ bool resolve_appearance_version(const LLAppearanceMessageContents& contents, S32 //----------------------------------------------------------------------------- void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) { + static S32 largestSelfCOFSeen(LLViewerInventoryCategory::VERSION_UNKNOWN); LL_DEBUGS("Avatar") << "starts" << LL_ENDL; bool enable_verbose_dumps = gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"); @@ -7398,6 +7356,15 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) LL_DEBUGS("Avatar") << "this_update_cof_version " << this_update_cof_version << " last_update_request_cof_version " << last_update_request_cof_version << " my_cof_version " << LLAppearanceMgr::instance().getCOFVersion() << LL_ENDL; + + if (largestSelfCOFSeen > this_update_cof_version) + { + LL_WARNS("Avatar") << "Already processed appearance for COF version " << + largestSelfCOFSeen << ", discarding appearance with COF " << this_update_cof_version << LL_ENDL; + return; + } + largestSelfCOFSeen = this_update_cof_version; + } else { @@ -7432,6 +7399,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) } // No backsies zone - if we get here, the message should be valid and usable, will be processed. + LL_INFOS("Avatar") << "Processing appearance message version " << this_update_cof_version << LL_ENDL; // Note: // RequestAgentUpdateAppearanceResponder::onRequestRequested() @@ -7439,7 +7407,10 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) // appearance messages. mLastUpdateReceivedCOFVersion = this_update_cof_version; - applyParsedTEMessage(contents.mTEContents); + if (applyParsedTEMessage(contents.mTEContents) > 0 && isChanged(TEXTURE)) + { + updateVisualComplexity(); + } // prevent the overwriting of valid baked textures with invalid baked textures for (U8 baked_index = 0; baked_index < mBakedTextureDatas.size(); baked_index++) @@ -7557,7 +7528,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) // Got an update for some other avatar // Ignore updates for self, because we have a more authoritative value in the preferences. setHoverOffset(contents.mHoverOffset); - LL_INFOS("Avatar") << avString() << "setting hover from message" << contents.mHoverOffset[2] << LL_ENDL; + LL_INFOS("Avatar") << avString() << "setting hover to " << contents.mHoverOffset[2] << LL_ENDL; } if (!contents.mHoverOffsetWasSet && !isSelf()) @@ -8111,7 +8082,7 @@ LLHost LLVOAvatar::getObjectHost() const } else { - return LLHost::invalid; + return LLHost(); } } @@ -8134,7 +8105,7 @@ void LLVOAvatar::updateFreezeCounter(S32 counter) BOOL LLVOAvatar::updateLOD() { - if (isImpostor()) + if (isImpostor() && 0 != mDrawable->getNumFaces() && mDrawable->getFace(0)->hasGeometry()) { return TRUE; } @@ -8171,28 +8142,36 @@ U32 LLVOAvatar::getPartitionType() const } //static -void LLVOAvatar::updateImpostors() +void LLVOAvatar::updateImpostors() { - LLCharacter::sAllowInstancesChange = FALSE ; + LLCharacter::sAllowInstancesChange = FALSE; for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin(); - iter != LLCharacter::sInstances.end(); ++iter) + iter != LLCharacter::sInstances.end(); ++iter) { LLVOAvatar* avatar = (LLVOAvatar*) *iter; - if (!avatar->isDead() && avatar->needsImpostorUpdate() && avatar->isVisible() && avatar->isImpostor()) + if (!avatar->isDead() && avatar->isVisible() + && ( + (avatar->isImpostor() || LLVOAvatar::AV_DO_NOT_RENDER == avatar->getVisualMuteSettings()) && avatar->needsImpostorUpdate()) + ) { + avatar->calcMutedAVColor(); gPipeline.generateImpostor(avatar); } } - LLCharacter::sAllowInstancesChange = TRUE ; + LLCharacter::sAllowInstancesChange = TRUE; } BOOL LLVOAvatar::isImpostor() { - return (sUseImpostors && (isVisuallyMuted() || (mUpdatePeriod >= IMPOSTOR_PERIOD))) || isInMuteList() ? TRUE : FALSE; + return sUseImpostors && (isVisuallyMuted() || (mUpdatePeriod >= IMPOSTOR_PERIOD)) ? TRUE : FALSE; } +BOOL LLVOAvatar::shouldImpostor(const U32 rank_factor) const +{ + return (!isSelf() && sUseImpostors && mVisibilityRank > (sMaxNonImpostors * rank_factor)); +} BOOL LLVOAvatar::needsImpostorUpdate() const { @@ -8233,68 +8212,170 @@ void LLVOAvatar::getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& d angle.mV[2] = da; } +// static +const U32 LLVOAvatar::IMPOSTORS_OFF = 66; /* Must equal the maximum allowed the RenderAvatarMaxNonImpostors + * slider in panel_preferences_graphics1.xml */ -void LLVOAvatar::idleUpdateRenderCost() +// static +void LLVOAvatar::updateImpostorRendering(U32 newMaxNonImpostorsValue) { - static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAutoMuteRenderWeightLimit", 0); - static const U32 ARC_LIMIT = 20000; - - if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_ATTACHMENT_BYTES)) - { //set debug text to attachment geometry bytes here so render cost will override - setDebugText(llformat("%.1f KB, %.2f m^2", mAttachmentGeometryBytes/1024.f, mAttachmentSurfaceArea)); + U32 oldmax = sMaxNonImpostors; + bool oldflg = sUseImpostors; + + if (IMPOSTORS_OFF <= newMaxNonImpostorsValue) + { + sMaxNonImpostors = 0; } - - if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHAME) && max_render_cost == 0) + else { - return; + sMaxNonImpostors = newMaxNonImpostorsValue; } + // the sUseImpostors flag depends on whether or not sMaxNonImpostors is set to the no-limit value (0) + sUseImpostors = (0 != sMaxNonImpostors); + if ( oldflg != sUseImpostors ) + { + LL_DEBUGS("AvatarRender") + << "was " << (oldflg ? "use" : "don't use" ) << " impostors (max " << oldmax << "); " + << "now " << (sUseImpostors ? "use" : "don't use" ) << " impostors (max " << sMaxNonImpostors << "); " + << LL_ENDL; + } +} - calculateUpdateRenderCost(); // Update mVisualComplexity if needed - - if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHAME)) + +void LLVOAvatar::idleUpdateRenderComplexity() +{ + // Render Complexity + calculateUpdateRenderComplexity(); // Update mVisualComplexity if needed + + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_AVATAR_DRAW_INFO)) { - std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus()); - setDebugText(llformat("%s %d", viz_string.c_str(), mVisualComplexity)); - F32 green = 1.f-llclamp(((F32) mVisualComplexity-(F32)ARC_LIMIT)/(F32)ARC_LIMIT, 0.f, 1.f); - F32 red = llmin((F32) mVisualComplexity/(F32)ARC_LIMIT, 1.f); - mText->setColor(LLColor4(red,green,0,1)); + std::string info_line; + F32 red_level; + F32 green_level; + LLColor4 info_color; + LLFontGL::StyleFlags info_style; + + if ( !mText ) + { + initHudText(); + mText->setFadeDistance(20.0, 5.0); // limit clutter in large crowds + } + else + { + mText->clearString(); // clear debug text + } + + /* + * NOTE: the logic for whether or not each of the values below + * controls muting MUST match that in the isVisuallyMuted and isTooComplex methods. + */ + + static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAvatarMaxComplexity", 0); + info_line = llformat("%d Complexity", mVisualComplexity); + + if (max_render_cost != 0) // zero means don't care, so don't bother coloring based on this + { + green_level = 1.f-llclamp(((F32) mVisualComplexity-(F32)max_render_cost)/(F32)max_render_cost, 0.f, 1.f); + red_level = llmin((F32) mVisualComplexity/(F32)max_render_cost, 1.f); + info_color.set(red_level, green_level, 0.0, 1.0); + info_style = ( mVisualComplexity > max_render_cost + ? LLFontGL::BOLD : LLFontGL::NORMAL ); + } + else + { + info_color.set(LLColor4::grey); + info_style = LLFontGL::NORMAL; + } + mText->addLine(info_line, info_color, info_style); + + // Visual rank + info_line = llformat("%d rank", mVisibilityRank); + // Use grey for imposters, white for normal rendering or no impostors + info_color.set(isImpostor() ? LLColor4::grey : LLColor4::white); + info_style = LLFontGL::NORMAL; + mText->addLine(info_line, info_color, info_style); + + // Attachment Surface Area + static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit", 1000.0f); + info_line = llformat("%.0f m^2", mAttachmentSurfaceArea); + + if (max_render_cost != 0 && max_attachment_area != 0) // zero means don't care, so don't bother coloring based on this + { + green_level = 1.f-llclamp((mAttachmentSurfaceArea-max_attachment_area)/max_attachment_area, 0.f, 1.f); + red_level = llmin(mAttachmentSurfaceArea/max_attachment_area, 1.f); + info_color.set(red_level, green_level, 0.0, 1.0); + info_style = ( mAttachmentSurfaceArea > max_attachment_area + ? LLFontGL::BOLD : LLFontGL::NORMAL ); + + } + else + { + info_color.set(LLColor4::grey); + info_style = LLFontGL::NORMAL; + } + mText->addLine(info_line, info_color, info_style); + + updateText(); // corrects position } } +void LLVOAvatar::addAttachmentArea(F32 delta_area) +{ + mAttachmentSurfaceArea += delta_area; +} + +void LLVOAvatar::subtractAttachmentArea(F32 delta_area) +{ + mAttachmentSurfaceArea = delta_area > mAttachmentSurfaceArea ? 0.0 : mAttachmentSurfaceArea - delta_area; +} + +void LLVOAvatar::updateVisualComplexity() +{ + LL_DEBUGS("AvatarRender") << "avatar " << getID() << " appearance changed" << LL_ENDL; + // Set the cache time to in the past so it's updated ASAP + mVisualComplexityStale = true; +} // Calculations for mVisualComplexity value -void LLVOAvatar::calculateUpdateRenderCost() +void LLVOAvatar::calculateUpdateRenderComplexity() { - static const U32 ARC_BODY_PART_COST = 200; + /***************************************************************** + * This calculation should not be modified by third party viewers, + * since it is used to limit rendering and should be uniform for + * everyone. If you have suggested improvements, submit them to + * the official viewer for consideration. + *****************************************************************/ + static const U32 COMPLEXITY_BODY_PART_COST = 200; // Diagnostic list of all textures on our avatar static std::set<LLUUID> all_textures; if (mVisualComplexityStale) { - mVisualComplexityStale = FALSE; - U32 cost = 0; + U32 cost = VISUAL_COMPLEXITY_UNKNOWN; LLVOVolume::texture_cost_t textures; for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++) { - const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)baked_index); + const LLAvatarAppearanceDictionary::BakedEntry *baked_dict + = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)baked_index); ETextureIndex tex_index = baked_dict->mTextureIndex; if ((tex_index != TEX_SKIRT_BAKED) || (isWearingWearableType(LLWearableType::WT_SKIRT))) { if (isTextureVisible(tex_index)) { - cost +=ARC_BODY_PART_COST; + cost +=COMPLEXITY_BODY_PART_COST; } } } + LL_DEBUGS("ARCdetail") << "Avatar body parts complexity: " << cost << LL_ENDL; - for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); - ++iter) + for (attachment_map_t::const_iterator attachment_point = mAttachmentPoints.begin(); + attachment_point != mAttachmentPoints.end(); + ++attachment_point) { - LLViewerJointAttachment* attachment = iter->second; + LLViewerJointAttachment* attachment = attachment_point->second; for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) @@ -8309,7 +8390,12 @@ void LLVOAvatar::calculateUpdateRenderCost() const LLVOVolume* volume = drawable->getVOVolume(); if (volume) { - cost += volume->getRenderCost(textures); + U32 attachment_total_cost = 0; + U32 attachment_volume_cost = 0; + U32 attachment_texture_cost = 0; + U32 attachment_children_cost = 0; + + attachment_volume_cost += volume->getRenderCost(textures); const_child_list_t children = volume->getChildren(); for (const_child_list_t::const_iterator child_iter = children.begin(); @@ -8320,15 +8406,27 @@ void LLVOAvatar::calculateUpdateRenderCost() LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj ); if (child) { - cost += child->getRenderCost(textures); + attachment_children_cost += child->getRenderCost(textures); } } - for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter) + for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin(); + volume_texture != textures.end(); + ++volume_texture) { // add the cost of each individual texture in the linkset - cost += iter->second; + attachment_texture_cost += volume_texture->second; } + + attachment_total_cost = attachment_volume_cost + attachment_texture_cost + attachment_children_cost; + LL_DEBUGS("ARCdetail") << "Attachment costs " << attached_object->getAttachmentItemID() + << " total: " << attachment_total_cost + << ", volume: " << attachment_volume_cost + << ", textures: " << attachment_texture_cost + << ", " << volume->numChildren() + << " children: " << attachment_children_cost + << LL_ENDL; + cost += attachment_total_cost; } } } @@ -8374,38 +8472,85 @@ void LLVOAvatar::calculateUpdateRenderCost() } } + if ( cost != mVisualComplexity ) + { + LL_DEBUGS("AvatarRender") << "Avatar "<< getID() + << " complexity updated was " << mVisualComplexity << " now " << cost + << " reported " << mReportedVisualComplexity + << LL_ENDL; + } + else + { + LL_DEBUGS("AvatarRender") << "Avatar "<< getID() + << " complexity updated no change " << mVisualComplexity + << " reported " << mReportedVisualComplexity + << LL_ENDL; + } mVisualComplexity = cost; + mVisualComplexityStale = false; + + static LLCachedControl<U32> show_my_complexity_changes(gSavedSettings, "ShowMyComplexityChanges", 20); + + if (isSelf() && show_my_complexity_changes) + { + LLAvatarRenderNotifier::getInstance()->updateNotificationAgent(mVisualComplexity); + } } } +void LLVOAvatar::setVisualMuteSettings(VisualMuteSettings set) +{ + mVisuallyMuteSetting = set; + mNeedsImpostorUpdate = TRUE; +} -// static -LLColor4 LLVOAvatar::calcMutedAVColor(F32 value, S32 range_low, S32 range_high) + +void LLVOAvatar::calcMutedAVColor() { - F32 clamped_value = llmin(value, (F32) range_high); - clamped_value = llmax(value, (F32) range_low); - F32 spectrum = (clamped_value / range_high); // spectrum is between 0 and 1.f + LLColor4 new_color(mMutedAVColor); + std::string change_msg; + LLUUID av_id(getID()); - // Array of colors. These are arranged so only one RGB color changes between each step, - // and it loops back to red so there is an even distribution. It is not a heat map - const S32 NUM_SPECTRUM_COLORS = 7; - static LLColor4 * spectrum_color[NUM_SPECTRUM_COLORS] = { &LLColor4::red, &LLColor4::magenta, &LLColor4::blue, &LLColor4::cyan, &LLColor4::green, &LLColor4::yellow, &LLColor4::red }; + if (getVisualMuteSettings() == AV_DO_NOT_RENDER) + { + // explicitly not-rendered avatars are light grey + new_color = LLColor4::grey3; + change_msg = " not rendered: color is grey3"; + } + else if (LLMuteList::getInstance()->isMuted(av_id)) // the user blocked them + { + // blocked avatars are dark grey + new_color = LLColor4::grey4; + change_msg = " blocked: color is grey4"; + } + else if ( mMutedAVColor == LLColor4::white || mMutedAVColor == LLColor4::grey3 || mMutedAVColor == LLColor4::grey4 ) + { + // select a color based on the first byte of the agents uuid so any muted agent is always the same color + F32 color_value = (F32) (av_id.mData[0]); + F32 spectrum = (color_value / 256.0); // spectrum is between 0 and 1.f + + // Array of colors. These are arranged so only one RGB color changes between each step, + // and it loops back to red so there is an even distribution. It is not a heat map + const S32 NUM_SPECTRUM_COLORS = 7; + static LLColor4 * spectrum_color[NUM_SPECTRUM_COLORS] = { &LLColor4::red, &LLColor4::magenta, &LLColor4::blue, &LLColor4::cyan, &LLColor4::green, &LLColor4::yellow, &LLColor4::red }; - spectrum = spectrum * (NUM_SPECTRUM_COLORS - 1); // Scale to range of number of colors - S32 spectrum_index_1 = floor(spectrum); // Desired color will be after this index - S32 spectrum_index_2 = spectrum_index_1 + 1; // and before this index (inclusive) - F32 fractBetween = spectrum - (F32)(spectrum_index_1); // distance between the two indexes (0-1) + spectrum = spectrum * (NUM_SPECTRUM_COLORS - 1); // Scale to range of number of colors + S32 spectrum_index_1 = floor(spectrum); // Desired color will be after this index + S32 spectrum_index_2 = spectrum_index_1 + 1; // and before this index (inclusive) + F32 fractBetween = spectrum - (F32)(spectrum_index_1); // distance between the two indexes (0-1) - LLColor4 new_color = lerp(*spectrum_color[spectrum_index_1], *spectrum_color[spectrum_index_2], fractBetween); - new_color.normalize(); - new_color *= 0.7f; // Tone it down a bit + new_color = lerp(*spectrum_color[spectrum_index_1], *spectrum_color[spectrum_index_2], fractBetween); + new_color.normalize(); + new_color *= 0.28f; // Tone it down - //LL_INFOS() << "From value " << std::setprecision(3) << value << " returning color " << new_color - // << " using indexes " << spectrum_index_1 << ", " << spectrum_index_2 - // << " and fractBetween " << fractBetween - // << LL_ENDL; + change_msg = " over limit color "; + } - return new_color; + if (mMutedAVColor != new_color) + { + LL_DEBUGS("AvatarRender") << "avatar "<< av_id << change_msg << std::setprecision(3) << new_color << LL_ENDL; + mMutedAVColor = new_color; + } } // static |