diff options
Diffstat (limited to 'indra/newview/llvoavatar.cpp')
-rw-r--r-- | indra/newview/llvoavatar.cpp | 1373 |
1 files changed, 851 insertions, 522 deletions
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index eae8f2cc56..bc5cca4a65 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -44,6 +44,7 @@ #include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" #include "llavatarrendernotifier.h" +#include "llcontrolavatar.h" #include "llexperiencecache.h" #include "llphysicsmotion.h" #include "llviewercontrol.h" @@ -109,6 +110,8 @@ #include "llcallstack.h" #include "llrendersphere.h" +#include <boost/lexical_cast.hpp> + extern F32 SPEED_ADJUST_MAX; extern F32 SPEED_ADJUST_MAX_SEC; extern F32 ANIM_SPEED_MAX; @@ -142,7 +145,7 @@ const LLUUID ANIM_AGENT_PHYSICS_MOTION = LLUUID("7360e029-3cb8-ebc4-863e-212df44 //----------------------------------------------------------------------------- // Constants //----------------------------------------------------------------------------- -const F32 DELTA_TIME_MIN = 0.01f; // we clamp measured deltaTime to this +const F32 DELTA_TIME_MIN = 0.01f; // we clamp measured delta_time to this const F32 DELTA_TIME_MAX = 0.2f; // range to insure stability of computations. const F32 PELVIS_LAG_FLYING = 0.22f;// pelvis follow half life while flying @@ -617,6 +620,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, LLViewerObject(id, pcode, regionp), mSpecialRenderMode(0), mAttachmentSurfaceArea(0.f), + mAttachmentVisibleTriangleCount(0), + mAttachmentEstTriangleCount(0.f), mReportedVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN), mTurning(FALSE), mLastSkeletonSerialNum( 0 ), @@ -663,7 +668,9 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mLastUpdateRequestCOFVersion(-1), mLastUpdateReceivedCOFVersion(-1), mCachedMuteListUpdateTime(0), - mCachedInMuteList(false) + mCachedInMuteList(false), + mIsControlAvatar(false), + mEnableDefaultMotions(true) { LL_DEBUGS("AvatarRender") << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << LL_ENDL; @@ -718,6 +725,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mCurrentGesticulationLevel = 0; + mRuthTimer.reset(); mRuthDebugTimer.reset(); mDebugExistenceTimer.reset(); @@ -1099,7 +1107,7 @@ void LLVOAvatar::cleanupClass() } // virtual -void LLVOAvatar::initInstance(void) +void LLVOAvatar::initInstance() { //------------------------------------------------------------------------- // register motions @@ -1222,8 +1230,6 @@ const LLVector3 LLVOAvatar::getRenderPosition() const { return getPosition() * mDrawable->getParent()->getRenderMatrix(); } - - } void LLVOAvatar::updateDrawable(BOOL force_damped) @@ -1240,6 +1246,10 @@ void LLVOAvatar::onShift(const LLVector4a& shift_vector) void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax) { + if (mDrawable.isNull()) + { + return; + } if (isImpostor() && !needsImpostorUpdate()) { LLVector3 delta = getRenderPosition() - @@ -1404,13 +1414,29 @@ void LLVOAvatar::renderCollisionVolumes() static F32 sphere_scale = 1.0f; static F32 center_dot_scale = 0.05f; - static LLVector3 CV_COLOR_OCCLUDED(0.0f, 0.0f, 1.0f); - static LLVector3 CV_COLOR_VISIBLE(0.5f, 0.5f, 1.0f); - static LLVector3 DOT_COLOR_OCCLUDED(1.0f, 1.0f, 1.0f); - static LLVector3 DOT_COLOR_VISIBLE(1.0f, 1.0f, 1.0f); + static LLVector3 BLUE(0.0f, 0.0f, 1.0f); + static LLVector3 PASTEL_BLUE(0.5f, 0.5f, 1.0f); + static LLVector3 RED(1.0f, 0.0f, 0.0f); + static LLVector3 PASTEL_RED(1.0f, 0.5f, 0.5f); + static LLVector3 WHITE(1.0f, 1.0f, 1.0f); + - render_sphere_and_line(begin_pos, end_pos, sphere_scale, CV_COLOR_OCCLUDED, CV_COLOR_VISIBLE); - render_sphere_and_line(begin_pos, end_pos, center_dot_scale, DOT_COLOR_OCCLUDED, DOT_COLOR_VISIBLE); + LLVector3 cv_color_occluded; + LLVector3 cv_color_visible; + LLVector3 dot_color_occluded(WHITE); + LLVector3 dot_color_visible(WHITE); + if (isControlAvatar()) + { + cv_color_occluded = RED; + cv_color_visible = PASTEL_RED; + } + else + { + cv_color_occluded = BLUE; + cv_color_visible = PASTEL_BLUE; + } + render_sphere_and_line(begin_pos, end_pos, sphere_scale, cv_color_occluded, cv_color_visible); + render_sphere_and_line(begin_pos, end_pos, center_dot_scale, dot_color_occluded, dot_color_visible); gGL.popMatrix(); } @@ -1422,9 +1448,6 @@ void LLVOAvatar::renderCollisionVolumes() mNameText->lineSegmentIntersect(unused, unused, unused, TRUE); } - - mDebugText.clear(); - addDebugText(ostr.str()); } void LLVOAvatar::renderBones() @@ -1595,6 +1618,11 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& return FALSE; } + if (isControlAvatar()) + { + return FALSE; + } + if (lineSegmentBoundingBox(start, end)) { for (S32 i = 0; i < mNumCollisionVolumes; ++i) @@ -1680,6 +1708,7 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& return FALSE; } +// virtual LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, @@ -1796,7 +1825,11 @@ void LLVOAvatar::buildCharacter() mAahMorph = getVisualParam( "Express_Open_Mouth" ); } - startDefaultMotions(); + // Currently disabled for control avatars (animated objects), enabled for all others. + if (mEnableDefaultMotions) + { + startDefaultMotions(); + } //------------------------------------------------------------------------- // restart any currently active motions @@ -1942,7 +1975,8 @@ void LLVOAvatar::resetSkeleton(bool reset_animations) //----------------------------------------------------------------------------- void LLVOAvatar::releaseMeshData() { - if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || mIsDummy) + if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || + (mIsDummy && !isControlAvatar())) { return; } @@ -1962,15 +1996,15 @@ void LLVOAvatar::releaseMeshData() LLFace* facep = mDrawable->getFace(0); if (facep) { - facep->setSize(0, 0); - for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++) - { - facep = mDrawable->getFace(i); + facep->setSize(0, 0); + for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++) + { + facep = mDrawable->getFace(i); if (facep) { - facep->setSize(0, 0); - } - } + facep->setSize(0, 0); + } + } } } @@ -1994,6 +2028,10 @@ void LLVOAvatar::releaseMeshData() void LLVOAvatar::restoreMeshData() { llassert(!isSelf()); + if (mDrawable.isNull()) + { + return; + } //LL_INFOS() << "Restoring" << LL_ENDL; mMeshValid = TRUE; @@ -2303,7 +2341,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time) { LL_RECORD_BLOCK_TIME(FTM_JOINT_UPDATE); - if (mIsSitting && getParent()) + if (isSitting() && getParent()) { LLViewerObject *root_object = (LLViewerObject*)getRoot(); LLDrawable* drawablep = root_object->mDrawable; @@ -2469,7 +2507,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) // (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing) //-------------------------------------------------------------------------------------------- - if ( mIsSitting ) + if ( isSitting() ) { LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] ); mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot->getWorldPosition() + headOffset ); @@ -2593,13 +2631,16 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) } } - mDrawable->movePartition(); - - //force a move if sitting on an active object - if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive()) - { - gPipeline.markMoved(mDrawable, TRUE); - } + if (mDrawable.notNull()) + { + mDrawable->movePartition(); + + //force a move if sitting on an active object + if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive()) + { + gPipeline.markMoved(mDrawable, TRUE); + } + } } void LLVOAvatar::idleUpdateAppearanceAnimation() @@ -2760,7 +2801,8 @@ void LLVOAvatar::idleUpdateLoadingEffect() LLPartData::LL_PART_EMISSIVE_MASK | // LLPartData::LL_PART_FOLLOW_SRC_MASK | LLPartData::LL_PART_TARGET_POS_MASK ); - if (!isTooComplex()) // do not generate particles for overly-complex avatars + // do not generate particles for dummy or overly-complex avatars + if (!mIsDummy && !isTooComplex()) { setParticleSource(particle_parameters, getID()); } @@ -3360,8 +3402,7 @@ bool LLVOAvatar::isInMuteList() void LLVOAvatar::updateDebugText() { - // clear debug text - mDebugText.clear(); + // Leave mDebugText uncleared here, in case a derived class has added some state first if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) { @@ -3406,7 +3447,7 @@ void LLVOAvatar::updateDebugText() if (hover_offset[2] != 0.0) { debug_line += llformat(" hov_z: %.3f", hover_offset[2]); - debug_line += llformat(" %s", (mIsSitting ? "S" : "T")); + debug_line += llformat(" %s", (isSitting() ? "S" : "T")); debug_line += llformat("%s", (isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED) ? "G" : "-")); } LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition(); @@ -3422,12 +3463,14 @@ void LLVOAvatar::updateDebugText() addDebugText(debug_line); } + if (gSavedSettings.getBOOL("DebugAvatarCompositeBaked")) { if (!mBakedTextureDebugText.empty()) addDebugText(mBakedTextureDebugText); } + // Develop -> Avatar -> Animation Info if (LLVOAvatar::sShowAnimationDebug) { for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin(); @@ -3437,8 +3480,27 @@ void LLVOAvatar::updateDebugText() if (motionp->getMinPixelArea() < getPixelArea()) { std::string output; - if (motionp->getName().empty()) + std::string motion_name = motionp->getName(); + if (motion_name.empty()) { + if (isControlAvatar()) + { + LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this); + // Try to get name from inventory of associated object + LLVOVolume *volp = control_av->mRootVolp; + if (volp) + { + volp->requestInventory(); + LLViewerInventoryItem* item = volp->getInventoryItemByAsset(motionp->getID()); + if (item) + { + motion_name = item->getName(); + } + } + } + } + if (motion_name.empty()) + { std::string name; if (gAgent.isGodlikeWithoutAdminMenuFakery() || isSelf()) { @@ -3492,7 +3554,6 @@ void LLVOAvatar::updateDebugText() { name = LLUUID::null.asString(); } - output = llformat("%s - %d", name.c_str(), (U32)motionp->getPriority()); @@ -3500,8 +3561,8 @@ void LLVOAvatar::updateDebugText() else { output = llformat("%s - %d", - motionp->getName().c_str(), - (U32)motionp->getPriority()); + motion_name.c_str(), + (U32)motionp->getPriority()); } addDebugText(output); } @@ -3517,51 +3578,125 @@ void LLVOAvatar::updateDebugText() { setDebugText(mDebugText); } - mDebugText.clear(); - + mDebugText.clear(); } //------------------------------------------------------------------------ -// updateCharacter() -// called on both your avatar and other avatars +// updateFootstepSounds +// Factored out from updateCharacter() +// Generate footstep sounds when feet hit the ground //------------------------------------------------------------------------ -BOOL LLVOAvatar::updateCharacter(LLAgent &agent) -{ - updateDebugText(); - - if (!mIsBuilt) - { - return FALSE; - } +void LLVOAvatar::updateFootstepSounds() +{ + if (mIsDummy) + { + return; + } + + //------------------------------------------------------------------------- + // Find the ground under each foot, these are used for a variety + // of things that follow + //------------------------------------------------------------------------- + LLVector3 ankle_left_pos_agent = mFootLeftp->getWorldPosition(); + LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition(); - BOOL visible = isVisible(); + LLVector3 ankle_left_ground_agent = ankle_left_pos_agent; + LLVector3 ankle_right_ground_agent = ankle_right_pos_agent; + LLVector3 normal; + resolveHeightAgent(ankle_left_pos_agent, ankle_left_ground_agent, normal); + resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal); - // For fading out the names above heads, only let the timer - // run if we're visible. - if (mDrawable.notNull() && !visible) + F32 leftElev = llmax(-0.2f, ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]); + F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]); + + if (!isSitting()) { - mTimeVisible.reset(); + //------------------------------------------------------------------------- + // Figure out which foot is on ground + //------------------------------------------------------------------------- + if (!mInAir) + { + if ((leftElev < 0.0f) || (rightElev < 0.0f)) + { + ankle_left_pos_agent = mFootLeftp->getWorldPosition(); + ankle_right_pos_agent = mFootRightp->getWorldPosition(); + leftElev = ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]; + rightElev = ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]; + } + } } + + const LLUUID AGENT_FOOTSTEP_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_LAND}; + const S32 NUM_AGENT_FOOTSTEP_ANIMS = LL_ARRAY_SIZE(AGENT_FOOTSTEP_ANIMS); - //-------------------------------------------------------------------- - // the rest should only be done occasionally for far away avatars - //-------------------------------------------------------------------- + if ( gAudiop && isAnyAnimationSignaled(AGENT_FOOTSTEP_ANIMS, NUM_AGENT_FOOTSTEP_ANIMS) ) + { + BOOL playSound = FALSE; + LLVector3 foot_pos_agent; + + BOOL onGroundLeft = (leftElev <= 0.05f); + BOOL onGroundRight = (rightElev <= 0.05f); + + // did left foot hit the ground? + if ( onGroundLeft && !mWasOnGroundLeft ) + { + foot_pos_agent = ankle_left_pos_agent; + playSound = TRUE; + } + // did right foot hit the ground? + if ( onGroundRight && !mWasOnGroundRight ) + { + foot_pos_agent = ankle_right_pos_agent; + playSound = TRUE; + } + + mWasOnGroundLeft = onGroundLeft; + mWasOnGroundRight = onGroundRight; + + if ( playSound ) + { + const F32 STEP_VOLUME = 0.1f; + const LLUUID& step_sound_id = getStepSound(); + + LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent); + + if (LLViewerParcelMgr::getInstance()->canHearSound(foot_pos_global) + && !LLMuteList::getInstance()->isMuted(getID(), LLMute::flagObjectSounds)) + { + gAudiop->triggerSound(step_sound_id, getID(), STEP_VOLUME, LLAudioEngine::AUDIO_TYPE_AMBIENT, foot_pos_global); + } + } + } +} + +//------------------------------------------------------------------------ +// computeUpdatePeriod() +// Factored out from updateCharacter() +// Set new value for mUpdatePeriod based on distance and various other factors. +//------------------------------------------------------------------------ +void LLVOAvatar::computeUpdatePeriod() +{ bool visually_muted = isVisuallyMuted(); - if (visible && (!isSelf() || visually_muted) && !mIsDummy && sUseImpostors && !mNeedsAnimUpdate && !sFreezeCounter) + if (mDrawable.notNull() + && isVisible() + && (!isSelf() || visually_muted) + && !mIsDummy + && sUseImpostors + && !mNeedsAnimUpdate + && !sFreezeCounter) { const LLVector4a* ext = mDrawable->getSpatialExtents(); LLVector4a size; size.setSub(ext[1],ext[0]); F32 mag = size.getLength3().getF32()*0.5f; - F32 impostor_area = 256.f*512.f*(8.125f - LLVOAvatar::sLODFactor*8.f); if (visually_muted) { // visually muted avatars update at 16 hz mUpdatePeriod = 16; } - else if ( ! shouldImpostor() + 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 @@ -3585,63 +3720,215 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) //nearby avatars, update the impostors more frequently. mUpdatePeriod = 4; } - - visible = (LLDrawable::getCurrentFrame()+mID.mData[0])%mUpdatePeriod == 0 ? TRUE : FALSE; } else { mUpdatePeriod = 1; } +} - // don't early out for your own avatar, as we rely on your animations playing reliably - // for example, the "turn around" animation when entering customize avatar needs to trigger - // even when your avatar is offscreen - if (!visible && !isSelf()) - { - updateMotions(LLCharacter::HIDDEN_UPDATE); - return FALSE; - } +//------------------------------------------------------------------------ +// updateOrientation() +// Factored out from updateCharacter() +// This is used by updateCharacter() to update the avatar's orientation: +// - updates mTurning state +// - updates rotation of the mRoot joint in the skeleton +// - for self, calls setControlFlags() to notify the simulator about any turns +//------------------------------------------------------------------------ +void LLVOAvatar::updateOrientation(LLAgent& agent, F32 speed, F32 delta_time) +{ + LLQuaternion iQ; + LLVector3 upDir( 0.0f, 0.0f, 1.0f ); + + // Compute a forward direction vector derived from the primitive rotation + // and the velocity vector. When walking or jumping, don't let body deviate + // more than 90 from the view, if necessary, flip the velocity vector. - // change animation time quanta based on avatar render load - if (!isSelf() && !mIsDummy) + LLVector3 primDir; + if (isSelf()) + { + primDir = agent.getAtAxis() - projected_vec(agent.getAtAxis(), agent.getReferenceUpVector()); + primDir.normalize(); + } + else + { + primDir = getRotation().getMatrix3().getFwdRow(); + } + LLVector3 velDir = getVelocity(); + velDir.normalize(); + if ( mSignaledAnimations.find(ANIM_AGENT_WALK) != mSignaledAnimations.end()) + { + F32 vpD = velDir * primDir; + if (vpD < -0.5f) + { + velDir *= -1.0f; + } + } + LLVector3 fwdDir = lerp(primDir, velDir, clamp_rescale(speed, 0.5f, 2.0f, 0.0f, 1.0f)); + if (isSelf() && gAgentCamera.cameraMouselook()) + { + // make sure fwdDir stays in same general direction as primdir + if (gAgent.getFlying()) + { + fwdDir = LLViewerCamera::getInstance()->getAtAxis(); + } + else + { + LLVector3 at_axis = LLViewerCamera::getInstance()->getAtAxis(); + LLVector3 up_vector = gAgent.getReferenceUpVector(); + at_axis -= up_vector * (at_axis * up_vector); + at_axis.normalize(); + + F32 dot = fwdDir * at_axis; + if (dot < 0.f) + { + fwdDir -= 2.f * at_axis * dot; + fwdDir.normalize(); + } + } + } + + LLQuaternion root_rotation = mRoot->getWorldMatrix().quaternion(); + F32 root_roll, root_pitch, root_yaw; + root_rotation.getEulerAngles(&root_roll, &root_pitch, &root_yaw); + + // When moving very slow, the pelvis is allowed to deviate from the + // forward direction to allow it to hold its position while the torso + // and head turn. Once in motion, it must conform however. + BOOL self_in_mouselook = isSelf() && gAgentCamera.cameraMouselook(); + + LLVector3 pelvisDir( mRoot->getWorldMatrix().getFwdRow4().mV ); + + static LLCachedControl<F32> s_pelvis_rot_threshold_slow(gSavedSettings, "AvatarRotateThresholdSlow", 60.0); + static LLCachedControl<F32> s_pelvis_rot_threshold_fast(gSavedSettings, "AvatarRotateThresholdFast", 2.0); + + F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, s_pelvis_rot_threshold_slow, s_pelvis_rot_threshold_fast); + + if (self_in_mouselook) + { + pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR; + } + pelvis_rot_threshold *= DEG_TO_RAD; + + F32 angle = angle_between( pelvisDir, fwdDir ); + + // The avatar's root is allowed to have a yaw that deviates widely + // from the forward direction, but if roll or pitch are off even + // a little bit we need to correct the rotation. + if(root_roll < 1.f * DEG_TO_RAD + && root_pitch < 5.f * DEG_TO_RAD) + { + // smaller correction vector means pelvis follows prim direction more closely + if (!mTurning && angle > pelvis_rot_threshold*0.75f) + { + mTurning = TRUE; + } + + // use tighter threshold when turning + if (mTurning) + { + pelvis_rot_threshold *= 0.4f; + } + + // am I done turning? + if (angle < pelvis_rot_threshold) + { + mTurning = FALSE; + } + + LLVector3 correction_vector = (pelvisDir - fwdDir) * clamp_rescale(angle, pelvis_rot_threshold*0.75f, pelvis_rot_threshold, 1.0f, 0.0f); + fwdDir += correction_vector; + } + else + { + mTurning = FALSE; + } + + // Now compute the full world space rotation for the whole body (wQv) + LLVector3 leftDir = upDir % fwdDir; + leftDir.normalize(); + fwdDir = leftDir % upDir; + LLQuaternion wQv( fwdDir, leftDir, upDir ); + + if (isSelf() && mTurning) + { + if ((fwdDir % pelvisDir) * upDir > 0.f) + { + gAgent.setControlFlags(AGENT_CONTROL_TURN_RIGHT); + } + else + { + gAgent.setControlFlags(AGENT_CONTROL_TURN_LEFT); + } + } + + // Set the root rotation, but do so incrementally so that it + // lags in time by some fixed amount. + //F32 u = LLSmoothInterpolation::getInterpolant(PELVIS_LAG); + F32 pelvis_lag_time = 0.f; + if (self_in_mouselook) + { + pelvis_lag_time = PELVIS_LAG_MOUSELOOK; + } + else if (mInAir) + { + pelvis_lag_time = PELVIS_LAG_FLYING; + // increase pelvis lag time when moving slowly + pelvis_lag_time *= clamp_rescale(mSpeedAccum, 0.f, 15.f, 3.f, 1.f); + } + else + { + pelvis_lag_time = PELVIS_LAG_WALKING; + } + + F32 u = llclamp((delta_time / pelvis_lag_time), 0.0f, 1.0f); + + mRoot->setWorldRotation( slerp(u, mRoot->getWorldRotation(), wQv) ); +} + +//------------------------------------------------------------------------ +// updateTimeStep() +// Factored out from updateCharacter(). +// +// Updates the time step used by the motion controller, based on area +// and avatar count criteria. This will also stop the +// ANIM_AGENT_WALK_ADJUST animation under some circumstances. +// ------------------------------------------------------------------------ +void LLVOAvatar::updateTimeStep() +{ + bool is_pure_dummy = mIsDummy && !isControlAvatar(); + if (!isSelf() && !is_pure_dummy) // ie, non-self avatars, and animated objects will be affected. { + // AXON note that sInstances counts animated objects and + // standard avatars in the same bucket. Is this desirable? F32 time_quantum = clamp_rescale((F32)sInstances.size(), 10.f, 35.f, 0.f, 0.25f); F32 pixel_area_scale = clamp_rescale(mPixelArea, 100, 5000, 1.f, 0.f); F32 time_step = time_quantum * pixel_area_scale; + // Extrema: + // If number of avs is 10 or less, time_step is unmodified (flagged with 0.0). + // If area of av is 5000 or greater, time_step is unmodified (flagged with 0.0). + // If number of avs is 35 or greater, and area of av is 100 or less, + // time_step takes the maximum possible value of 0.25. + // Other situations will give values within the (0, 0.25) range. if (time_step != 0.f) { // disable walk motion servo controller as it doesn't work with motion timesteps stopMotion(ANIM_AGENT_WALK_ADJUST); removeAnimationData("Walk Speed"); } + // See SL-763 - playback with altered time step does not + // appear to work correctly, odd behavior for distant avatars. + // As of 11-2017, LLMotionController::updateMotions() will + // ignore the value here. Need to re-enable if it's every + // fixed. mMotionController.setTimeStep(time_step); - // LL_INFOS() << "Setting timestep to " << time_quantum * pixel_area_scale << LL_ENDL; - } - - if (getParent() && !mIsSitting) - { - sitOnObject((LLViewerObject*)getParent()); } - else if (!getParent() && mIsSitting && !isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED)) - { - getOffObject(); - } - - //-------------------------------------------------------------------- - // create local variables in world coords for region position values - //-------------------------------------------------------------------- - F32 speed; - LLVector3 normal; +} - LLVector3 xyVel = getVelocity(); - xyVel.mV[VZ] = 0.0f; - speed = xyVel.length(); - // remembering the value here prevents a display glitch if the - // animation gets toggled during this update. - bool was_sit_ground_constrained = isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED); - - if (!(mIsSitting && getParent())) +void LLVOAvatar::updateRootPositionAndRotation(LLAgent& agent, F32 speed, bool was_sit_ground_constrained) +{ + if (!(isSitting() && getParent())) { // This case includes all configurations except sitting on an // object, so does include ground sit. @@ -3655,7 +3942,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) { mTimeLast = animation_time; - // put the pelvis at slaved position/mRotation + // Initially put the pelvis at slaved position/mRotation // SL-315 mRoot->setWorldPosition( getPositionAgent() ); // first frame mRoot->setWorldRotation( getRotation() ); @@ -3664,9 +3951,9 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) //-------------------------------------------------------------------- // dont' let dT get larger than 1/5th of a second //-------------------------------------------------------------------- - F32 deltaTime = animation_time - mTimeLast; + F32 delta_time = animation_time - mTimeLast; - deltaTime = llclamp( deltaTime, DELTA_TIME_MIN, DELTA_TIME_MAX ); + delta_time = llclamp( delta_time, DELTA_TIME_MIN, DELTA_TIME_MAX ); mTimeLast = animation_time; mSpeedAccum = (mSpeedAccum * 0.95f) + (speed * 0.05f); @@ -3685,7 +3972,8 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) root_pos = gAgent.getPosGlobalFromAgent(getRenderPosition()); root_pos.mdV[VZ] += getVisualParamWeight(AVATAR_HOVER); - + // AXON need to review mInAir calcs for animated objects, if the value even matters. + LLVector3 normal; resolveHeightGlobal(root_pos, ground_under_pelvis, normal); F32 foot_to_ground = (F32) (root_pos.mdV[VZ] - mPelvisToFoot - ground_under_pelvis.mdV[VZ]); BOOL in_air = ((!LLWorld::getInstance()->getRegionFromPosGlobal(ground_under_pelvis)) || @@ -3707,185 +3995,156 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) // correct for the fact that the pelvis is not necessarily the center // of the agent's physical representation root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot; - if (!mIsSitting && !was_sit_ground_constrained) + if (!isSitting() && !was_sit_ground_constrained) { root_pos += LLVector3d(getHoverOffset()); } - - LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos); - - - if (newPosition != mRoot->getXform()->getWorldPosition()) - { - mRoot->touch(); - // SL-315 - mRoot->setWorldPosition( newPosition ); // regular update - } + LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this); + if (cav) + { + cav->matchVolumeTransform(); + } + else + { + LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos); + if (newPosition != mRoot->getXform()->getWorldPosition()) + { + mRoot->touch(); + // SL-315 + mRoot->setWorldPosition( newPosition ); // regular update + } + } //-------------------------------------------------------------------- // Propagate viewer object rotation to root of avatar //-------------------------------------------------------------------- - if (!isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS)) + if (!isControlAvatar() && !isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS)) { - LLQuaternion iQ; - LLVector3 upDir( 0.0f, 0.0f, 1.0f ); - - // Compute a forward direction vector derived from the primitive rotation - // and the velocity vector. When walking or jumping, don't let body deviate - // more than 90 from the view, if necessary, flip the velocity vector. - - LLVector3 primDir; - if (isSelf()) - { - primDir = agent.getAtAxis() - projected_vec(agent.getAtAxis(), agent.getReferenceUpVector()); - primDir.normalize(); - } - else - { - primDir = getRotation().getMatrix3().getFwdRow(); - } - LLVector3 velDir = getVelocity(); - velDir.normalize(); - if ( mSignaledAnimations.find(ANIM_AGENT_WALK) != mSignaledAnimations.end()) - { - F32 vpD = velDir * primDir; - if (vpD < -0.5f) - { - velDir *= -1.0f; - } - } - LLVector3 fwdDir = lerp(primDir, velDir, clamp_rescale(speed, 0.5f, 2.0f, 0.0f, 1.0f)); - if (isSelf() && gAgentCamera.cameraMouselook()) - { - // make sure fwdDir stays in same general direction as primdir - if (gAgent.getFlying()) - { - fwdDir = LLViewerCamera::getInstance()->getAtAxis(); - } - else - { - LLVector3 at_axis = LLViewerCamera::getInstance()->getAtAxis(); - LLVector3 up_vector = gAgent.getReferenceUpVector(); - at_axis -= up_vector * (at_axis * up_vector); - at_axis.normalize(); - - F32 dot = fwdDir * at_axis; - if (dot < 0.f) - { - fwdDir -= 2.f * at_axis * dot; - fwdDir.normalize(); - } - } - } - - LLQuaternion root_rotation = mRoot->getWorldMatrix().quaternion(); - F32 root_roll, root_pitch, root_yaw; - root_rotation.getEulerAngles(&root_roll, &root_pitch, &root_yaw); - - // When moving very slow, the pelvis is allowed to deviate from the - // forward direction to allow it to hold it's position while the torso - // and head turn. Once in motion, it must conform however. - BOOL self_in_mouselook = isSelf() && gAgentCamera.cameraMouselook(); - - LLVector3 pelvisDir( mRoot->getWorldMatrix().getFwdRow4().mV ); - - static LLCachedControl<F32> s_pelvis_rot_threshold_slow(gSavedSettings, "AvatarRotateThresholdSlow", 60.0); - static LLCachedControl<F32> s_pelvis_rot_threshold_fast(gSavedSettings, "AvatarRotateThresholdFast", 2.0); - - F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, s_pelvis_rot_threshold_slow, s_pelvis_rot_threshold_fast); - - if (self_in_mouselook) - { - pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR; - } - pelvis_rot_threshold *= DEG_TO_RAD; - - F32 angle = angle_between( pelvisDir, fwdDir ); - - // The avatar's root is allowed to have a yaw that deviates widely - // from the forward direction, but if roll or pitch are off even - // a little bit we need to correct the rotation. - if(root_roll < 1.f * DEG_TO_RAD - && root_pitch < 5.f * DEG_TO_RAD) - { - // smaller correction vector means pelvis follows prim direction more closely - if (!mTurning && angle > pelvis_rot_threshold*0.75f) - { - mTurning = TRUE; - } - - // use tighter threshold when turning - if (mTurning) - { - pelvis_rot_threshold *= 0.4f; - } - - // am I done turning? - if (angle < pelvis_rot_threshold) - { - mTurning = FALSE; - } - - LLVector3 correction_vector = (pelvisDir - fwdDir) * clamp_rescale(angle, pelvis_rot_threshold*0.75f, pelvis_rot_threshold, 1.0f, 0.0f); - fwdDir += correction_vector; - } - else - { - mTurning = FALSE; - } - - // Now compute the full world space rotation for the whole body (wQv) - LLVector3 leftDir = upDir % fwdDir; - leftDir.normalize(); - fwdDir = leftDir % upDir; - LLQuaternion wQv( fwdDir, leftDir, upDir ); - - if (isSelf() && mTurning) - { - if ((fwdDir % pelvisDir) * upDir > 0.f) - { - gAgent.setControlFlags(AGENT_CONTROL_TURN_RIGHT); - } - else - { - gAgent.setControlFlags(AGENT_CONTROL_TURN_LEFT); - } - } - - // Set the root rotation, but do so incrementally so that it - // lags in time by some fixed amount. - //F32 u = LLSmoothInterpolation::getInterpolant(PELVIS_LAG); - F32 pelvis_lag_time = 0.f; - if (self_in_mouselook) - { - pelvis_lag_time = PELVIS_LAG_MOUSELOOK; - } - else if (mInAir) - { - pelvis_lag_time = PELVIS_LAG_FLYING; - // increase pelvis lag time when moving slowly - pelvis_lag_time *= clamp_rescale(mSpeedAccum, 0.f, 15.f, 3.f, 1.f); - } - else - { - pelvis_lag_time = PELVIS_LAG_WALKING; - } - - F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f); - - mRoot->setWorldRotation( slerp(u, mRoot->getWorldRotation(), wQv) ); - + // AXON - should we always skip for control avatars? + // Rotation fixups for avatars in motion, some may be + // relevant. + updateOrientation(agent, speed, delta_time); } } else if (mDrawable.notNull()) { + // Sitting on an object - mRoot is slaved to mDrawable orientation. LLVector3 pos = mDrawable->getPosition(); pos += getHoverOffset() * mDrawable->getRotation(); // SL-315 mRoot->setPosition(pos); mRoot->setRotation(mDrawable->getRotation()); } +} + +//------------------------------------------------------------------------ +// updateCharacter() +// +// This is called for all avatars, so there are 4 possible situations: +// +// 1) Avatar is your own. In this case the class is LLVOAvatarSelf, +// isSelf() is true, and agent specifies the corresponding agent +// information for you. In all the other cases, agent is irrelevant +// and it would be less confusing if it were null or something. +// +// 2) Avatar is controlled by another resident. Class is LLVOAvatar, +// and isSelf() is false. +// +// 3) Avatar is the controller for an animated object. Class is +// LLControlAvatar and mIsDummy is true. Avatar is a purely +// viewer-side entity with no representation on the simulator. +// +// 4) Avatar is a "dummy" avatar used in some areas of the UI, such as +// when previewing uploaded animations. Class is LLVOAvatar, and +// mIsDummy is true. Avatar is purely viewer-side with no +// representation on the simulator. +// +//------------------------------------------------------------------------ +BOOL LLVOAvatar::updateCharacter(LLAgent &agent) +{ + updateDebugText(); + + if (!mIsBuilt) + { + return FALSE; + } + + BOOL visible = isVisible(); + bool is_control_avatar = isControlAvatar(); // capture state to simplify tracing + bool is_attachment = false; + if (is_control_avatar) + { + LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this); + is_attachment = cav && cav->mRootVolp && cav->mRootVolp->isAttachment(); // For attached animated objects + } + + LLScopedContextString str("updateCharacter " + getFullname() + " is_control_avatar " + + boost::lexical_cast<std::string>(is_control_avatar) + + " is_attachment " + boost::lexical_cast<std::string>(is_attachment)); + + // For fading out the names above heads, only let the timer + // run if we're visible. + if (mDrawable.notNull() && !visible) + { + mTimeVisible.reset(); + } + + //-------------------------------------------------------------------- + // The rest should only be done occasionally for far away avatars. + // Set mUpdatePeriod and visible based on distance and other criteria. + //-------------------------------------------------------------------- + computeUpdatePeriod(); + visible = (LLDrawable::getCurrentFrame()+mID.mData[0])%mUpdatePeriod == 0 ? TRUE : FALSE; + + //-------------------------------------------------------------------- + // Early out if not visible and not self + // don't early out for your own avatar, as we rely on your animations playing reliably + // for example, the "turn around" animation when entering customize avatar needs to trigger + // even when your avatar is offscreen + //-------------------------------------------------------------------- + if (!visible && !isSelf()) + { + updateMotions(LLCharacter::HIDDEN_UPDATE); + return FALSE; + } + + //-------------------------------------------------------------------- + // change animation time quanta based on avatar render load + // AXON how should control avs be handled here? + //-------------------------------------------------------------------- + // SL-763 the time step quantization does not currently work. + //updateTimeStep(); + + //-------------------------------------------------------------------- + // Update sitting state based on parent and active animation info. + //-------------------------------------------------------------------- + if (getParent() && !isSitting()) + { + sitOnObject((LLViewerObject*)getParent()); + } + else if (!getParent() && isSitting() && !isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED)) + { + getOffObject(); + } + + //-------------------------------------------------------------------- + // create local variables in world coords for region position values + //-------------------------------------------------------------------- + LLVector3 xyVel = getVelocity(); + xyVel.mV[VZ] = 0.0f; + F32 speed = xyVel.length(); + // remembering the value here prevents a display glitch if the + // animation gets toggled during this update. + bool was_sit_ground_constrained = isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED); + + //-------------------------------------------------------------------- + // This does a bunch of state updating, including figuring out + // whether av is in the air, setting mRoot position and rotation + // In some cases, calls updateOrientation() for a lot of the + // work + // -------------------------------------------------------------------- + updateRootPositionAndRotation(agent, speed, was_sit_ground_constrained); //------------------------------------------------------------------------- // Update character motions @@ -3904,7 +4163,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) } // Special handling for sitting on ground. - if (!getParent() && (mIsSitting || was_sit_ground_constrained)) + if (!getParent() && (isSitting() || was_sit_ground_constrained)) { F32 off_z = LLVector3d(getHoverOffset()).mdV[VZ]; @@ -3921,90 +4180,18 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) // update head position updateHeadOffset(); - //------------------------------------------------------------------------- - // Find the ground under each foot, these are used for a variety - // of things that follow - //------------------------------------------------------------------------- - LLVector3 ankle_left_pos_agent = mFootLeftp->getWorldPosition(); - LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition(); - - LLVector3 ankle_left_ground_agent = ankle_left_pos_agent; - LLVector3 ankle_right_ground_agent = ankle_right_pos_agent; - resolveHeightAgent(ankle_left_pos_agent, ankle_left_ground_agent, normal); - resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal); - - F32 leftElev = llmax(-0.2f, ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]); - F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]); - - if (!mIsSitting) - { - //------------------------------------------------------------------------- - // Figure out which foot is on ground - //------------------------------------------------------------------------- - if (!mInAir) - { - if ((leftElev < 0.0f) || (rightElev < 0.0f)) - { - ankle_left_pos_agent = mFootLeftp->getWorldPosition(); - ankle_right_pos_agent = mFootRightp->getWorldPosition(); - leftElev = ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]; - rightElev = ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]; - } - } - } - - //------------------------------------------------------------------------- // Generate footstep sounds when feet hit the ground - //------------------------------------------------------------------------- - const LLUUID AGENT_FOOTSTEP_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_LAND}; - const S32 NUM_AGENT_FOOTSTEP_ANIMS = LL_ARRAY_SIZE(AGENT_FOOTSTEP_ANIMS); - - if ( gAudiop && isAnyAnimationSignaled(AGENT_FOOTSTEP_ANIMS, NUM_AGENT_FOOTSTEP_ANIMS) ) - { - BOOL playSound = FALSE; - LLVector3 foot_pos_agent; - - BOOL onGroundLeft = (leftElev <= 0.05f); - BOOL onGroundRight = (rightElev <= 0.05f); - - // did left foot hit the ground? - if ( onGroundLeft && !mWasOnGroundLeft ) - { - foot_pos_agent = ankle_left_pos_agent; - playSound = TRUE; - } - - // did right foot hit the ground? - if ( onGroundRight && !mWasOnGroundRight ) - { - foot_pos_agent = ankle_right_pos_agent; - playSound = TRUE; - } - - mWasOnGroundLeft = onGroundLeft; - mWasOnGroundRight = onGroundRight; - - if ( playSound ) - { - const F32 STEP_VOLUME = 0.1f; - const LLUUID& step_sound_id = getStepSound(); - - LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent); - - if (LLViewerParcelMgr::getInstance()->canHearSound(foot_pos_global) - && !LLMuteList::getInstance()->isMuted(getID(), LLMute::flagObjectSounds)) - { - gAudiop->triggerSound(step_sound_id, getID(), STEP_VOLUME, LLAudioEngine::AUDIO_TYPE_AMBIENT, foot_pos_global); - } - } - } + updateFootstepSounds(); + // Update child joints as needed. mRoot->updateWorldMatrixChildren(); - //mesh vertices need to be reskinned - mNeedsSkin = TRUE; + // System avatar mesh vertices need to be reskinned. + mNeedsSkin = TRUE; + return TRUE; } + //----------------------------------------------------------------------------- // updateHeadOffset() //----------------------------------------------------------------------------- @@ -4019,7 +4206,7 @@ void LLVOAvatar::updateHeadOffset() { midEyePt = midEyePt * ~mDrawable->getWorldRotation(); } - if (mIsSitting) + if (isSitting()) { mHeadOffset = midEyePt; } @@ -4115,7 +4302,7 @@ void LLVOAvatar::updateVisibility() if (mIsDummy) { - visible = TRUE; + visible = FALSE; } else if (mDrawable.isNull()) { @@ -4229,7 +4416,8 @@ void LLVOAvatar::updateVisibility() } else { - if (mMeshValid && mMeshInvisibleTime.getElapsedTimeF32() > TIME_BEFORE_MESH_CLEANUP) + if (mMeshValid && + (isControlAvatar() || mMeshInvisibleTime.getElapsedTimeF32() > TIME_BEFORE_MESH_CLEANUP)) { releaseMeshData(); } @@ -4264,6 +4452,11 @@ U32 LLVOAvatar::renderSkinned() return num_indices; } + if (mDrawable.isNull()) + { + return num_indices; + } + LLFace* face = mDrawable->getFace(0); bool needs_rebuild = !face || !face->getVertexBuffer() || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY); @@ -4426,11 +4619,12 @@ U32 LLVOAvatar::renderSkinned() } BOOL first_pass = TRUE; + bool is_pure_dummy = mIsDummy && !isControlAvatar(); if (!LLDrawPoolAvatar::sSkipOpaque) { if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender) { - if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy) + if (isTextureVisible(TEX_HEAD_BAKED) || is_pure_dummy) { LLViewerJoint* head_mesh = getViewerJoint(MESH_ID_HEAD); if (head_mesh) @@ -4440,7 +4634,7 @@ U32 LLVOAvatar::renderSkinned() first_pass = FALSE; } } - if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy) + if (isTextureVisible(TEX_UPPER_BAKED) || is_pure_dummy) { LLViewerJoint* upper_mesh = getViewerJoint(MESH_ID_UPPER_BODY); if (upper_mesh) @@ -4450,7 +4644,7 @@ U32 LLVOAvatar::renderSkinned() first_pass = FALSE; } - if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy) + if (isTextureVisible(TEX_LOWER_BAKED) || is_pure_dummy) { LLViewerJoint* lower_mesh = getViewerJoint(MESH_ID_LOWER_BODY); if (lower_mesh) @@ -4507,18 +4701,15 @@ U32 LLVOAvatar::renderTransparent(BOOL first_pass) } first_pass = FALSE; } - // 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) - { - LLViewerJoint* hair_mesh = getViewerJoint(MESH_ID_HAIR); - if (hair_mesh) - { - num_indices += hair_mesh->render(mAdjustedPixelArea, first_pass, mIsDummy); - } - first_pass = FALSE; - } + if (isTextureVisible(TEX_HAIR_BAKED)) + { + LLViewerJoint* hair_mesh = getViewerJoint(MESH_ID_HAIR); + if (hair_mesh) + { + num_indices += hair_mesh->render(mAdjustedPixelArea, first_pass, mIsDummy); + } + first_pass = FALSE; + } if (LLPipeline::sImpostorRender) { gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); @@ -4558,7 +4749,9 @@ U32 LLVOAvatar::renderRigid() gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); } - if (isTextureVisible(TEX_EYES_BAKED) || mIsDummy) + bool is_pure_dummy = mIsDummy && !isControlAvatar(); + + if (isTextureVisible(TEX_EYES_BAKED) || is_pure_dummy) { LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT); LLViewerJoint* eyeball_right = getViewerJoint(MESH_ID_EYEBALL_RIGHT); @@ -5105,10 +5298,13 @@ void LLVOAvatar::processAnimationStateChanges() startMotion(ANIM_AGENT_WALK_ADJUST); stopMotion(ANIM_AGENT_FLY_ADJUST); } - else if (mInAir && !mIsSitting) + else if (mInAir && !isSitting()) { stopMotion(ANIM_AGENT_WALK_ADJUST); - startMotion(ANIM_AGENT_FLY_ADJUST); + if (mEnableDefaultMotions) + { + startMotion(ANIM_AGENT_FLY_ADJUST); + } } else { @@ -5118,13 +5314,19 @@ void LLVOAvatar::processAnimationStateChanges() if ( isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) ) { - startMotion(ANIM_AGENT_TARGET); + if (mEnableDefaultMotions) + { + startMotion(ANIM_AGENT_TARGET); + } stopMotion(ANIM_AGENT_BODY_NOISE); } else { stopMotion(ANIM_AGENT_TARGET); - startMotion(ANIM_AGENT_BODY_NOISE); + if (mEnableDefaultMotions) + { + startMotion(ANIM_AGENT_BODY_NOISE); + } } // clear all current animations @@ -5479,14 +5681,14 @@ bool LLVOAvatar::getRiggedMeshID(LLViewerObject* pVO, LLUUID& mesh_id) LLVOVolume* pVObj = pVO->mDrawable->getVOVolume(); if ( pVObj ) { - const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID(), pVObj ); + const LLMeshSkinInfo* pSkinData = pVObj->getSkinInfo(); if (pSkinData && pSkinData->mJointNames.size() > JOINT_COUNT_REQUIRED_FOR_FULLRIG // full rig && pSkinData->mAlternateBindMatrix.size() > 0 ) - { - mesh_id = pSkinData->mMeshID; - return true; - } + { + mesh_id = pSkinData->mMeshID; + return true; + } } } return false; @@ -5533,8 +5735,7 @@ bool LLVOAvatar::jointIsRiggedTo(const std::string& joint_name, const LLViewerOb return false; } - LLUUID currentId = vobj->getVolume()->getParams().getSculptID(); - const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId, vobj ); + const LLMeshSkinInfo* pSkinData = vobj->getSkinInfo(); if ( vobj && vobj->isAttachment() && vobj->isMesh() && pSkinData ) { @@ -5570,7 +5771,18 @@ void LLVOAvatar::rebuildAttachmentOverrides() { LLScopedContextString str("rebuildAttachmentOverrides " + getFullname()); - // Attachment points + // Handle the case that we're resetting the skeleton of an animated object. + LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this); + if (control_av) + { + LLVOVolume *volp = control_av->mRootVolp; + if (volp) + { + addAttachmentOverridesForObject(volp); + } + } + + // Attached objects for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) @@ -5581,24 +5793,30 @@ void LLVOAvatar::rebuildAttachmentOverrides() for (LLViewerJointAttachment::attachedobjs_vec_t::iterator at_it = attachment_pt->mAttachedObjects.begin(); at_it != attachment_pt->mAttachedObjects.end(); ++at_it) { - addAttachmentOverridesForObject(*at_it); + LLViewerObject *vo = *at_it; + // Attached animated objects affect joints in their control + // avs, not the avs to which they are attached. + if (!vo->isAnimatedObject()) + { + addAttachmentOverridesForObject(vo); + } } } } } + //----------------------------------------------------------------------------- // addAttachmentPosOverridesForObject //----------------------------------------------------------------------------- void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo) { - LLVOAvatar *av = vo->getAvatarAncestor(); - if (!av || (av != this)) - { + if (vo->getAvatar() != this && vo->getAvatarAncestor() != this) + { LL_WARNS("Avatar") << "called with invalid avatar" << LL_ENDL; return; - } + } - LLScopedContextString str("addAttachmentOverridesForObject " + av->getFullname()); + LLScopedContextString str("addAttachmentOverridesForObject " + vo->getAvatar()->getFullname()); // Process all children LLViewerObject::const_child_list_t& children = vo->getChildren(); @@ -5621,10 +5839,9 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo) { return; } - LLUUID currentId = vobj->getVolume()->getParams().getSculptID(); - const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId, vobj ); + const LLMeshSkinInfo* pSkinData = vobj->getSkinInfo(); - if ( vobj && vobj->isAttachment() && vobj->isMesh() && pSkinData ) + if ( vobj && vobj->isMesh() && pSkinData ) { const int bindCnt = pSkinData->mAlternateBindMatrix.size(); const int jointCnt = pSkinData->mJointNames.size(); @@ -5806,14 +6023,14 @@ void LLVOAvatar::showAttachmentOverrides(bool verbose) const } //----------------------------------------------------------------------------- -// resetJointsOnDetach +// removeAttachmentOverridesForObject //----------------------------------------------------------------------------- -void LLVOAvatar::resetJointsOnDetach(LLViewerObject *vo) +void LLVOAvatar::removeAttachmentOverridesForObject(LLViewerObject *vo) { - LLVOAvatar *av = vo->getAvatarAncestor(); - if (!av || (av != this)) + if (vo->getAvatar() != this && vo->getAvatarAncestor() != this) { LL_WARNS("Avatar") << "called with invalid avatar" << LL_ENDL; + return; } // Process all children @@ -5822,21 +6039,21 @@ void LLVOAvatar::resetJointsOnDetach(LLViewerObject *vo) it != children.end(); ++it) { LLViewerObject *childp = *it; - resetJointsOnDetach(childp); + removeAttachmentOverridesForObject(childp); } // Process self. LLUUID mesh_id; if (getRiggedMeshID(vo,mesh_id)) { - resetJointsOnDetach(mesh_id); + removeAttachmentOverridesForObject(mesh_id); } } //----------------------------------------------------------------------------- -// resetJointsOnDetach +// removeAttachmentOverridesForObject //----------------------------------------------------------------------------- -void LLVOAvatar::resetJointsOnDetach(const LLUUID& mesh_id) +void LLVOAvatar::removeAttachmentOverridesForObject(const LLUUID& mesh_id) { //Subsequent joints are relative to pelvis avatar_joint_list_t::iterator iter = mSkeleton.begin(); @@ -5914,6 +6131,7 @@ void LLVOAvatar::getGround(const LLVector3 &in_pos_agent, LLVector3 &out_pos_age LLVector3d z_vec(0.0f, 0.0f, 1.0f); LLVector3d p0_global, p1_global; + // AXON UPDATE FOR CONTROL AVS? if (mIsDummy) { outNorm.setVec(z_vec); @@ -5943,6 +6161,7 @@ F32 LLVOAvatar::getTimeDilation() //----------------------------------------------------------------------------- F32 LLVOAvatar::getPixelArea() const { + // AXON UPDATE FOR CONTROL AVATARS if (mIsDummy) { return 100000.f; @@ -6358,7 +6577,7 @@ void LLVOAvatar::removeChild(LLViewerObject *childp) LLViewerJointAttachment* LLVOAvatar::getTargetAttachmentPoint(LLViewerObject* viewer_object) { - S32 attachmentID = ATTACHMENT_ID_FROM_STATE(viewer_object->getState()); + S32 attachmentID = ATTACHMENT_ID_FROM_STATE(viewer_object->getAttachmentState()); // This should never happen unless the server didn't process the attachment point // correctly, but putting this check in here to be safe. @@ -6450,19 +6669,63 @@ U32 LLVOAvatar::getNumAttachments() const //----------------------------------------------------------------------------- // canAttachMoreObjects() +// Returns true if we can attach <n> more objects. //----------------------------------------------------------------------------- -BOOL LLVOAvatar::canAttachMoreObjects() const +BOOL LLVOAvatar::canAttachMoreObjects(U32 n) const { - return (getNumAttachments() < MAX_AGENT_ATTACHMENTS); + return (getNumAttachments() + n) <= MAX_AGENT_ATTACHMENTS; } //----------------------------------------------------------------------------- -// canAttachMoreObjects() -// Returns true if we can attach <n> more objects. +// getNumAnimatedObjectAttachments() //----------------------------------------------------------------------------- -BOOL LLVOAvatar::canAttachMoreObjects(U32 n) const +U32 LLVOAvatar::getNumAnimatedObjectAttachments() const { - return (getNumAttachments() + n) <= MAX_AGENT_ATTACHMENTS; + U32 num_attachments = 0; + for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); + ++iter) + { + const LLViewerJointAttachment *attachment_pt = (*iter).second; + num_attachments += attachment_pt->getNumAnimatedObjects(); + } + return num_attachments; +} + +//----------------------------------------------------------------------------- +// getMaxAnimatedObjectAttachments() +// Gets from simulator feature if available, otherwise 0. +//----------------------------------------------------------------------------- +S32 LLVOAvatar::getMaxAnimatedObjectAttachments() const +{ + S32 max_attach = 0; + // AXON REMOVE AFTER SERVER TESTING DONE + if (gSavedSettings.getBOOL("AnimatedObjectsIgnoreLimits")) + { + max_attach = MAX_AGENT_ATTACHMENTS; + } + else + { + if (gAgent.getRegion()) + { + LLSD features; + gAgent.getRegion()->getSimulatorFeatures(features); + if (features.has("AnimatedObjects")) + { + max_attach = features["AnimatedObjects"]["MaxAgentAnimatedObjectAttachments"].asInteger(); + } + } + } + return max_attach; +} + +//----------------------------------------------------------------------------- +// canAttachMoreAnimatedObjects() +// Returns true if we can attach <n> more animated objects. +//----------------------------------------------------------------------------- +BOOL LLVOAvatar::canAttachMoreAnimatedObjects(U32 n) const +{ + return (getNumAnimatedObjectAttachments() + n) <= getMaxAnimatedObjectAttachments(); } //----------------------------------------------------------------------------- @@ -6553,7 +6816,7 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) LLUUID mesh_id; if (getRiggedMeshID(pVO, mesh_id)) { - resetJointsOnDetach(mesh_id); + removeAttachmentOverridesForObject(mesh_id); if ( gAgentCamera.cameraCustomizeAvatar() ) { gAgent.unpauseAnimation(); @@ -6714,7 +6977,10 @@ void LLVOAvatar::getOffObject() mRoot->setRotation(cur_rotation_world); mRoot->getXform()->update(); - startMotion(ANIM_AGENT_BODY_NOISE); + if (mEnableDefaultMotions) + { + startMotion(ANIM_AGENT_BODY_NOISE); + } if (isSelf()) { @@ -6874,6 +7140,7 @@ void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color) BOOL LLVOAvatar::isVisible() const { + // AXON should we flag control avs as invisible? return mDrawable.notNull() && (!mOrphaned || isSelf()) && (mDrawable->isVisible() || mIsDummy); @@ -6882,6 +7149,11 @@ BOOL LLVOAvatar::isVisible() const // Determine if we have enough avatar data to render bool LLVOAvatar::getIsCloud() const { + if (mIsDummy) + { + return false; + } + return ( ((const_cast<LLVOAvatar*>(this))->visualParamWeightsAreDefault())// Do we have a shape? || ( !isTextureDefined(TEX_LOWER_BAKED) || !isTextureDefined(TEX_UPPER_BAKED) @@ -7161,9 +7433,9 @@ bool LLVOAvatar::isTooComplex() const // 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) - )); + && (mVisualComplexity > max_render_cost + || (max_attachment_area > 0.0f && mAttachmentSurfaceArea > max_attachment_area) + )); } return too_complex; @@ -8846,6 +9118,11 @@ void LLVOAvatar::updateFreezeCounter(S32 counter) BOOL LLVOAvatar::updateLOD() { + if (mDrawable.isNull()) + { + return FALSE; + } + if (isImpostor() && 0 != mDrawable->getNumFaces() && mDrawable->getFace(0)->hasGeometry()) { return TRUE; @@ -8886,10 +9163,10 @@ U32 LLVOAvatar::getPartitionType() const void LLVOAvatar::updateImpostors() { LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD; - LLCharacter::sAllowInstancesChange = FALSE; - for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin(); - iter != LLCharacter::sInstances.end(); ++iter) + std::vector<LLCharacter*> instances_copy = LLCharacter::sInstances; + for (std::vector<LLCharacter*>::iterator iter = instances_copy.begin(); + iter != instances_copy.end(); ++iter) { LLVOAvatar* avatar = (LLVOAvatar*) *iter; if (!avatar->isDead() && avatar->isVisible() @@ -8986,6 +9263,17 @@ void LLVOAvatar::updateImpostorRendering(U32 newMaxNonImpostorsValue) void LLVOAvatar::idleUpdateRenderComplexity() { + if (isControlAvatar()) + { + LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this); + bool is_attachment = cav && cav->mRootVolp && cav->mRootVolp->isAttachment(); // For attached animated objects + if (is_attachment) + { + // ARC for animated object attachments is accounted with the avatar they're attached to. + return; + } + } + // Render Complexity calculateUpdateRenderComplexity(); // Update mVisualComplexity if needed @@ -9033,10 +9321,16 @@ void LLVOAvatar::idleUpdateRenderComplexity() // 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_color.set(isImpostor() ? LLColor4::grey : (isControlAvatar() ? LLColor4::yellow : LLColor4::white)); info_style = LLFontGL::NORMAL; mText->addLine(info_line, info_color, info_style); + // Triangle count + mText->addLine(std::string("VisTris ") + LLStringOps::getReadableNumber(mAttachmentVisibleTriangleCount), + info_color, info_style); + mText->addLine(std::string("EstMaxTris ") + LLStringOps::getReadableNumber(mAttachmentEstTriangleCount), + info_color, info_style); + // Attachment Surface Area static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit", 1000.0f); info_line = llformat("%.0f m^2", mAttachmentSurfaceArea); @@ -9055,22 +9349,13 @@ void LLVOAvatar::idleUpdateRenderComplexity() 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; @@ -9078,6 +9363,136 @@ void LLVOAvatar::updateVisualComplexity() mVisualComplexityStale = true; } +// Account for the complexity of a single top-level object associated +// with an avatar. This will be either an attached object or an animated +// object. +void LLVOAvatar::accountRenderComplexityForObject( + const LLViewerObject *attached_object, + const F32 max_attachment_complexity, + LLVOVolume::texture_cost_t& textures, + U32& cost, + hud_complexity_list_t& hud_complexity_list) +{ + if (attached_object && !attached_object->isHUDAttachment()) + { + mAttachmentVisibleTriangleCount += attached_object->recursiveGetTriangleCount(); + mAttachmentEstTriangleCount += attached_object->recursiveGetEstTrianglesMax(); + mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea(); + + textures.clear(); + const LLDrawable* drawable = attached_object->mDrawable; + if (drawable) + { + const LLVOVolume* volume = drawable->getVOVolume(); + if (volume) + { + F32 attachment_total_cost = 0; + F32 attachment_volume_cost = 0; + F32 attachment_texture_cost = 0; + F32 attachment_children_cost = 0; + // AXON placeholder value, will revisit in testing. + const F32 animated_object_attachment_surcharge = 20000; + + if (attached_object->isAnimatedObject()) + { + attachment_volume_cost += animated_object_attachment_surcharge; + } + attachment_volume_cost += volume->getRenderCost(textures); + + const_child_list_t children = volume->getChildren(); + for (const_child_list_t::const_iterator child_iter = children.begin(); + child_iter != children.end(); + ++child_iter) + { + LLViewerObject* child_obj = *child_iter; + LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj ); + if (child) + { + attachment_children_cost += child->getRenderCost(textures); + } + } + + 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 + 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; + // Limit attachment complexity to avoid signed integer flipping of the wearer's ACI + cost += (U32)llclamp(attachment_total_cost, MIN_ATTACHMENT_COMPLEXITY, max_attachment_complexity); + } + } + } + if (isSelf() + && attached_object + && attached_object->isHUDAttachment() + && !attached_object->isTempAttachment() + && attached_object->mDrawable) + { + textures.clear(); + + mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea(); + + const LLVOVolume* volume = attached_object->mDrawable->getVOVolume(); + if (volume) + { + LLHUDComplexity hud_object_complexity; + hud_object_complexity.objectName = attached_object->getAttachmentItemName(); + hud_object_complexity.objectId = attached_object->getAttachmentItemID(); + std::string joint_name; + gAgentAvatarp->getAttachedPointName(attached_object->getAttachmentItemID(), joint_name); + hud_object_complexity.jointName = joint_name; + // get cost and individual textures + hud_object_complexity.objectsCost += volume->getRenderCost(textures); + hud_object_complexity.objectsCount++; + + LLViewerObject::const_child_list_t& child_list = attached_object->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); ++iter) + { + LLViewerObject* childp = *iter; + const LLVOVolume* chld_volume = dynamic_cast<LLVOVolume*>(childp); + if (chld_volume) + { + // get cost and individual textures + hud_object_complexity.objectsCost += chld_volume->getRenderCost(textures); + hud_object_complexity.objectsCount++; + } + } + + hud_object_complexity.texturesCount += textures.size(); + + for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin(); + volume_texture != textures.end(); + ++volume_texture) + { + // add the cost of each individual texture (ignores duplicates) + hud_object_complexity.texturesCost += volume_texture->second; + LLViewerFetchedTexture *tex = LLViewerTextureManager::getFetchedTexture(volume_texture->first); + if (tex) + { + // Note: Texture memory might be incorect since texture might be still loading. + hud_object_complexity.texturesMemoryTotal += tex->getTextureMemory(); + if (tex->getOriginalHeight() * tex->getOriginalWidth() >= HUD_OVERSIZED_TEXTURE_DATA_SIZE) + { + hud_object_complexity.largeTexturesCount++; + } + } + } + hud_complexity_list.push_back(hud_object_complexity); + } + } +} + // Calculations for mVisualComplexity value void LLVOAvatar::calculateUpdateRenderComplexity() { @@ -9116,7 +9531,25 @@ void LLVOAvatar::calculateUpdateRenderComplexity() } LL_DEBUGS("ARCdetail") << "Avatar body parts complexity: " << cost << LL_ENDL; + mAttachmentVisibleTriangleCount = 0; + mAttachmentEstTriangleCount = 0.f; + mAttachmentSurfaceArea = 0.f; + + // A standalone animated object needs to be accounted for + // using its associated volume. Attached animated objects + // will be covered by the subsequent loop over attachments. + LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this); + if (control_av) + { + LLVOVolume *volp = control_av->mRootVolp; + if (volp && !volp->isAttachment()) + { + accountRenderComplexityForObject(volp, max_attachment_complexity, + textures, cost, hud_complexity_list); + } + } + // Account for complexity of all attachments. for (attachment_map_t::const_iterator attachment_point = mAttachmentPoints.begin(); attachment_point != mAttachmentPoints.end(); ++attachment_point) @@ -9127,112 +9560,8 @@ void LLVOAvatar::calculateUpdateRenderComplexity() ++attachment_iter) { const LLViewerObject* attached_object = (*attachment_iter); - if (attached_object && !attached_object->isHUDAttachment()) - { - textures.clear(); - const LLDrawable* drawable = attached_object->mDrawable; - if (drawable) - { - const LLVOVolume* volume = drawable->getVOVolume(); - if (volume) - { - F32 attachment_total_cost = 0; - F32 attachment_volume_cost = 0; - F32 attachment_texture_cost = 0; - F32 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(); - child_iter != children.end(); - ++child_iter) - { - LLViewerObject* child_obj = *child_iter; - LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj ); - if (child) - { - attachment_children_cost += child->getRenderCost(textures); - } - } - - 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 - 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; - // Limit attachment complexity to avoid signed integer flipping of the wearer's ACI - cost += (U32)llclamp(attachment_total_cost, MIN_ATTACHMENT_COMPLEXITY, max_attachment_complexity); - } - } - } - if (isSelf() - && attached_object - && attached_object->isHUDAttachment() - && !attached_object->isTempAttachment() - && attached_object->mDrawable) - { - textures.clear(); - - const LLVOVolume* volume = attached_object->mDrawable->getVOVolume(); - if (volume) - { - LLHUDComplexity hud_object_complexity; - hud_object_complexity.objectName = attached_object->getAttachmentItemName(); - hud_object_complexity.objectId = attached_object->getAttachmentItemID(); - std::string joint_name; - gAgentAvatarp->getAttachedPointName(attached_object->getAttachmentItemID(), joint_name); - hud_object_complexity.jointName = joint_name; - // get cost and individual textures - hud_object_complexity.objectsCost += volume->getRenderCost(textures); - hud_object_complexity.objectsCount++; - - LLViewerObject::const_child_list_t& child_list = attached_object->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); ++iter) - { - LLViewerObject* childp = *iter; - const LLVOVolume* chld_volume = dynamic_cast<LLVOVolume*>(childp); - if (chld_volume) - { - // get cost and individual textures - hud_object_complexity.objectsCost += chld_volume->getRenderCost(textures); - hud_object_complexity.objectsCount++; - } - } - - hud_object_complexity.texturesCount += textures.size(); - - for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin(); - volume_texture != textures.end(); - ++volume_texture) - { - // add the cost of each individual texture (ignores duplicates) - hud_object_complexity.texturesCost += volume_texture->second; - LLViewerFetchedTexture *tex = LLViewerTextureManager::getFetchedTexture(volume_texture->first); - if (tex) - { - // Note: Texture memory might be incorect since texture might be still loading. - hud_object_complexity.texturesMemoryTotal += tex->getTextureMemory(); - if (tex->getOriginalHeight() * tex->getOriginalWidth() >= HUD_OVERSIZED_TEXTURE_DATA_SIZE) - { - hud_object_complexity.largeTexturesCount++; - } - } - } - hud_complexity_list.push_back(hud_object_complexity); - } - } + accountRenderComplexityForObject(attached_object, max_attachment_complexity, + textures, cost, hud_complexity_list); } } |