diff options
104 files changed, 6138 insertions, 1401 deletions
| diff --git a/etc/message.xml b/etc/message.xml index 6d8160abb5..b444fe6c11 100755 --- a/etc/message.xml +++ b/etc/message.xml @@ -236,6 +236,14 @@  					<boolean>false</boolean>  				</map> +				<key>ObjectAnimation</key> +				<map> +					<key>flavor</key> +					<string>template</string> +					<key>trusted-sender</key> +					<boolean>false</boolean> +				</map> +  				<key>AvatarAppearance</key>  				<map>  					<key>flavor</key> diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp index 60359ca304..92217c60ff 100644 --- a/indra/llappearance/llavatarappearance.cpp +++ b/indra/llappearance/llavatarappearance.cpp @@ -190,7 +190,8 @@ LLAvatarAppearance::LLAvatarAppearance(LLWearableData* wearable_data) :      mNumBones(0),      mNumCollisionVolumes(0),      mCollisionVolumes(NULL), -    mIsBuilt(FALSE) +    mIsBuilt(FALSE), +    mInitFlags(0)  {  	llassert_always(mWearableData);  	mBakedTextureDatas.resize(LLAvatarAppearanceDefines::BAKED_NUM_INDICES); @@ -281,6 +282,8 @@ void LLAvatarAppearance::initInstance()  	buildCharacter(); +    mInitFlags |= 1<<0; +  }  // virtual @@ -1724,7 +1727,7 @@ void LLAvatarAppearance::makeJointAliases(LLAvatarBoneInfo *bone_info)          }          mJointAliasMap[*i] = bone_name;      } -     +      LLAvatarBoneInfo::child_list_t::const_iterator iter;      for (iter = bone_info->mChildList.begin(); iter != bone_info->mChildList.end(); ++iter)      { @@ -1739,13 +1742,34 @@ const LLAvatarAppearance::joint_alias_map_t& LLAvatarAppearance::getJointAliases      {          LLAvatarSkeletonInfo::bone_info_list_t::const_iterator iter; -        for (iter = sAvatarSkeletonInfo->mBoneInfoList.begin(); iter != sAvatarSkeletonInfo->mBoneInfoList.end(); ++iter) +        for (iter = sAvatarSkeletonInfo->mBoneInfoList.begin();  +             iter != sAvatarSkeletonInfo->mBoneInfoList.end(); +             ++iter)          {              //LLAvatarBoneInfo *bone_info = *iter;              makeJointAliases( *iter );          } + +        LLAvatarXmlInfo::attachment_info_list_t::iterator attach_iter; +        for (attach_iter = sAvatarXmlInfo->mAttachmentInfoList.begin(); +             attach_iter != sAvatarXmlInfo->mAttachmentInfoList.end();  +             ++attach_iter) +        { +            LLAvatarXmlInfo::LLAvatarAttachmentInfo *info = *attach_iter; +            std::string bone_name = info->mName; +             +            // Also accept the name with spaces substituted with +            // underscores. This gives a mechanism for referencing such joints +            // in daes, which don't allow spaces. +            std::string sub_space_to_underscore = bone_name; +            LLStringUtil::replaceChar(sub_space_to_underscore, ' ', '_'); +            if (sub_space_to_underscore != bone_name) +            { +                mJointAliasMap[sub_space_to_underscore] = bone_name; +            } +        }      } -     +      return mJointAliasMap;  }  diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h index ccd6323ed8..7815c1844b 100644 --- a/indra/llappearance/llavatarappearance.h +++ b/indra/llappearance/llavatarappearance.h @@ -70,6 +70,7 @@ public:  	static void			initClass();  	static void			cleanupClass();	// Cleanup data that's only init'd once per class.  	virtual void 		initInstance(); // Called after construction to initialize the instance. +    S32					mInitFlags;  	virtual BOOL		loadSkeletonNode();  	BOOL				loadMeshNodes();  	BOOL				loadLayersets(); @@ -227,7 +228,7 @@ protected:   **                    RENDERING   **/  public: -	BOOL		mIsDummy; // for special views +	BOOL		mIsDummy; // for special views and animated object controllers; local to viewer  	//--------------------------------------------------------------------  	// Morph masks diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp index a3d5679f65..e2f512f86e 100644 --- a/indra/llcharacter/lljoint.cpp +++ b/indra/llcharacter/lljoint.cpp @@ -428,13 +428,6 @@ void LLJoint::addAttachmentPosOverride( const LLVector3& pos, const LLUUID& mesh  	{  		return;  	} -    // BENTO -    // Not clear pelvis overrides are meaningful/useful. -    //if (mName == "mPelvis") -    //{ -    //    return; -    //} -      LLVector3 before_pos;      LLUUID before_mesh_id;      bool has_active_override_before = hasAttachmentPosOverride( before_pos, before_mesh_id ); @@ -881,7 +874,7 @@ void LLJoint::setWorldRotation( const LLQuaternion& rot )  //--------------------------------------------------------------------  const LLVector3& LLJoint::getScale()  { -	return mXform.getScale();	 +    return mXform.getScale();  }  //-------------------------------------------------------------------- diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h index 0c8fbfebb0..8112d246f2 100644 --- a/indra/llcharacter/lljoint.h +++ b/indra/llcharacter/lljoint.h @@ -70,6 +70,16 @@ private:  	map_type m_map;  }; +inline bool operator==(const LLVector3OverrideMap& a, const LLVector3OverrideMap& b) +{ +    return a.getMap() == b.getMap(); +} + +inline bool operator!=(const LLVector3OverrideMap& a, const LLVector3OverrideMap& b) +{ +    return !(a == b); +} +  //-----------------------------------------------------------------------------  // class LLJoint  //----------------------------------------------------------------------------- diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index 35e76f1d9d..3116403616 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -135,7 +135,7 @@ LLMotionController::LLMotionController()  	  mLastTime(0.0f),  	  mHasRunOnce(FALSE),  	  mPaused(FALSE), -	  mPauseTime(0.f), +	  mPausedFrame(0),  	  mTimeStep(0.f),  	  mTimeStepCount(0),  	  mLastInterp(0.f), @@ -814,6 +814,10 @@ void LLMotionController::updateLoadingMotions()  //-----------------------------------------------------------------------------  void LLMotionController::updateMotions(bool force_update)  { +    // SL-763: "Distant animated objects run at super fast speed" +    // The use_quantum optimization or possibly the associated code in setTimeStamp() +    // does not work as implemented. +    // Currently setting mTimeStep to nonzero is disabled elsewhere.  	BOOL use_quantum = (mTimeStep != 0.f);  	// Always update mPrevTimerElapsed @@ -1125,6 +1129,7 @@ void LLMotionController::pauseAllMotions()  	{  		//LL_INFOS() << "Pausing animations..." << LL_ENDL;  		mPaused = TRUE; +        mPausedFrame = LLFrameTimer::getFrameCount();  	}  } diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index 9d9c64f4f0..637ee4d2bb 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -148,12 +148,16 @@ public:  	void pauseAllMotions();  	void unpauseAllMotions();  	BOOL isPaused() const { return mPaused; } +    S32 getPausedFrame() const { return mPausedFrame; }  	void setTimeStep(F32 step); +    F32 getTimeStep() const { return mTimeStep; }  	void setTimeFactor(F32 time_factor);  	F32 getTimeFactor() const { return mTimeFactor; } +    F32 getAnimTime() const { return mAnimTime; } +      	motion_list_t& getActiveMotions() { return mActiveMotions; }  	void incMotionCounts(S32& num_motions, S32& num_loading_motions, S32& num_loaded_motions, S32& num_active_motions, S32& num_deprecated_motions); @@ -218,7 +222,7 @@ protected:  	F32					mLastTime;  	BOOL				mHasRunOnce;  	BOOL				mPaused; -	F32					mPauseTime; +	S32					mPausedFrame;  	F32					mTimeStep;  	S32					mTimeStepCount;  	F32					mLastInterp; diff --git a/indra/llcommon/llcallstack.h b/indra/llcommon/llcallstack.h index 1f7a7689d7..5acf04a49f 100644 --- a/indra/llcommon/llcallstack.h +++ b/indra/llcommon/llcallstack.h @@ -78,3 +78,10 @@ struct LLContextStatus  };  LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLContextStatus& context_status); + +#define dumpStack(tag) \ +    if (debugLoggingEnabled(tag)) \ +    { \ +        LLCallStack cs; \ +        LL_DEBUGS(tag) << "STACK:\n" << "====================\n" << cs << "====================" << LL_ENDL; \ +    } diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 06c7aef8ab..49ed8b495d 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -29,6 +29,7 @@  #include "llerror.h"  #include "llerrorcontrol.h" +#include "llsdutil.h"  #include <cctype>  #ifdef __GNUC__ @@ -89,9 +90,14 @@ namespace {  		{  			closelog();  		} -		 + +        virtual bool enabled() override +        { +            return LLError::getEnabledLogTypesMask() & 0x01; +        } +          		virtual void recordMessage(LLError::ELevel level, -									const std::string& message) +									const std::string& message) override  		{  			int syslogPriority = LOG_CRIT;  			switch (level) { @@ -119,6 +125,13 @@ namespace {  			{  				LL_INFOS() << "Error setting log file to " << filename << LL_ENDL;  			} +            else +            { +                if (!LLError::getAlwaysFlush()) +                { +                    mFile.sync_with_stdio(false); +                } +            }  			mWantsTime = true;              mWantsTags = true;  		} @@ -128,12 +141,28 @@ namespace {  			mFile.close();  		} +        virtual bool enabled() override +        { +#ifdef LL_RELEASE_FOR_DOWNLOAD +            return 1; +#else +            return LLError::getEnabledLogTypesMask() & 0x02; +#endif +        } +          		bool okay() { return mFile.good(); }  		virtual void recordMessage(LLError::ELevel level, -									const std::string& message) +									const std::string& message) override  		{ -			mFile << message << std::endl; +            if (LLError::getAlwaysFlush()) +            { +                mFile << message << std::endl; +            } +            else +            { +                mFile << message << "\n"; +            }  		}  	private: @@ -149,8 +178,13 @@ namespace {  			mWantsTime = timestamp;  		} +        virtual bool enabled() override +        { +            return LLError::getEnabledLogTypesMask() & 0x04; +        } +          		virtual void recordMessage(LLError::ELevel level, -					   const std::string& message) +					   const std::string& message) override  		{  			if (ANSI_PROBE == mUseANSI)  				mUseANSI = (checkANSI() ? ANSI_YES : ANSI_NO); @@ -209,8 +243,13 @@ namespace {  	public:  		RecordToFixedBuffer(LLLineBuffer* buffer) : mBuffer(buffer) { } +        virtual bool enabled() override +        { +            return LLError::getEnabledLogTypesMask() & 0x08; +        } +          		virtual void recordMessage(LLError::ELevel level, -								   const std::string& message) +								   const std::string& message) override  		{  			mBuffer->addLine(message);  		} @@ -226,8 +265,13 @@ namespace {  		RecordToWinDebug()  		{} +        virtual bool enabled() override +        { +            return LLError::getEnabledLogTypesMask() & 0x10; +        } +          		virtual void recordMessage(LLError::ELevel level, -								   const std::string& message) +								   const std::string& message) override  		{  			debugger_print(message);  		} @@ -394,7 +438,7 @@ namespace  			 i != callSites.end();  			 ++i)  		{ -			(*i)->invalidate(); +            (*i)->invalidate();  		}  		callSites.clear(); @@ -413,7 +457,11 @@ namespace LLError  		bool                                mPrintLocation;  		LLError::ELevel                     mDefaultLevel; -		 + +        bool 								mLogAlwaysFlush; + +        U32 								mEnabledLogTypesMask; +  		LevelMap                            mFunctionLevelMap;  		LevelMap                            mClassLevelMap;  		LevelMap                            mFileLevelMap; @@ -454,6 +502,8 @@ namespace LLError  		: LLRefCount(),  		mPrintLocation(false),  		mDefaultLevel(LLError::LEVEL_DEBUG), +		mLogAlwaysFlush(true), +		mEnabledLogTypesMask(255),  		mFunctionLevelMap(),  		mClassLevelMap(),  		mFileLevelMap(), @@ -618,6 +668,8 @@ namespace  		LLError::Settings::getInstance()->reset();  		LLError::setDefaultLevel(LLError::LEVEL_INFO); +        LLError::setAlwaysFlush(true); +        LLError::setEnabledLogTypesMask(0xFFFFFFFF);  		LLError::setFatalFunction(LLError::crashAndLoop);  		LLError::setTimeFunction(LLError::utcTime); @@ -691,6 +743,30 @@ namespace LLError  		return s->mDefaultLevel;  	} +	void setAlwaysFlush(bool flush) +	{ +		SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); +		s->mLogAlwaysFlush = flush; +	} + +	bool getAlwaysFlush() +	{ +		SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); +		return s->mLogAlwaysFlush; +	} + +	void setEnabledLogTypesMask(U32 mask) +	{ +		SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); +		s->mEnabledLogTypesMask = mask; +	} + +	U32 getEnabledLogTypesMask() +	{ +		SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); +		return s->mEnabledLogTypesMask; +	} +  	void setFunctionLevel(const std::string& function_name, ELevel level)  	{  		Globals::getInstance()->invalidateCallSites(); @@ -771,7 +847,15 @@ namespace LLError  		setPrintLocation(config["print-location"]);  		setDefaultLevel(decodeLevel(config["default-level"])); -		 +        if (config.has("log-always-flush")) +        { +            setAlwaysFlush(config["log-always-flush"]); +        } +        if (config.has("enabled-log-types-mask")) +        { +            setEnabledLogTypesMask(config["enabled-log-types-mask"].asInteger()); +        } +          		LLSD sets = config["settings"];  		LLSD::array_const_iterator a, end;  		for (a = sets.beginArray(), end = sets.endArray(); a != end; ++a) @@ -954,7 +1038,12 @@ namespace  			++i)  		{  			LLError::RecorderPtr r = *i; -			 + +            if (!r->enabled()) +            { +                continue; +            } +              			std::ostringstream message_stream;  			if (r->wantsTime() && s->mTimeFunction != NULL) @@ -1088,6 +1177,7 @@ namespace {  namespace LLError  { +  	bool Log::shouldLog(CallSite& site)  	{  		LogLock lock; @@ -1553,18 +1643,16 @@ namespace LLError  bool debugLoggingEnabled(const std::string& tag)  { -    const char* tags[] = {tag.c_str()}; -    ::size_t tag_count = 1; -    LLError::CallSite _site(LLError::LEVEL_DEBUG, __FILE__, __LINE__,  -                            typeid(_LL_CLASS_TO_LOG), __FUNCTION__, false, tags, tag_count); -    if (LL_UNLIKELY(_site.shouldLog())) -    { -        return true; -    } -    else +    LogLock lock; +    if (!lock.ok())      {          return false;      } +         +    LLError::SettingsConfigPtr s = LLError::Settings::getInstance()->getSettingsConfig(); +    LLError::ELevel level = LLError::LEVEL_DEBUG; +    bool res = checkLevelMap(s->mTagLevelMap, tag, level); +    return res;  } diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index caf2ba72c2..1730f0c640 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -74,6 +74,10 @@ namespace LLError  	LL_COMMON_API void setPrintLocation(bool);  	LL_COMMON_API void setDefaultLevel(LLError::ELevel);  	LL_COMMON_API ELevel getDefaultLevel(); +	LL_COMMON_API void setAlwaysFlush(bool flush); +    LL_COMMON_API bool getAlwaysFlush(); +	LL_COMMON_API void setEnabledLogTypesMask(U32 mask); +	LL_COMMON_API U32 getEnabledLogTypesMask();  	LL_COMMON_API void setFunctionLevel(const std::string& function_name, LLError::ELevel);  	LL_COMMON_API void setClassLevel(const std::string& class_name, LLError::ELevel);  	LL_COMMON_API void setFileLevel(const std::string& file_name, LLError::ELevel); @@ -140,6 +144,8 @@ namespace LLError  		virtual void recordMessage(LLError::ELevel, const std::string& message) = 0;  			// use the level for better display, not for filtering +        virtual bool enabled() { return true; } +  		bool wantsTime();  		bool wantsTags();  		bool wantsLevel(); diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 5b17d9e3a4..f04ae5f5cb 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -60,6 +60,12 @@ class LLMutex ;  LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment);  #ifdef SHOW_ASSERT +// This is incredibly expensive - in profiling Windows RWD builds, 30% +// of CPU time was in aligment checks. +//#define ASSERT_ALIGNMENT +#endif + +#ifdef ASSERT_ALIGNMENT  #define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(uintptr_t(ptr),((U32)alignment))  #else  #define ll_assert_aligned(ptr,alignment) diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index c45db3b185..9a02fecd72 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -869,6 +869,25 @@ std::string LLStringOps::getDatetimeCode (std::string key)  	}  } +std::string LLStringOps::getReadableNumber(F64 num) +{ +    if (fabs(num)>=1e9) +    { +		return llformat("%.2lfB", num / 1e9); +    } +    else if (fabs(num)>=1e6) +    { +		return llformat("%.2lfM", num / 1e6); +    } +    else if (fabs(num)>=1e3) +    { +		return llformat("%.2lfK", num / 1e3); +    } +    else +    { +		return llformat("%.2lf", num); +    } +}  namespace LLStringFn  { diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index a4a5b393cb..68ee9db46b 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -211,6 +211,9 @@ public:  	static bool getPacificDaylightTime(void) { return sPacificDaylightTime;}  	static std::string getDatetimeCode (std::string key); + +    // Express a value like 1234567 as "1.23M"  +    static std::string getReadableNumber(F64 num);  };  /** diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 4c8bcdac91..379c3ee9ea 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -20,10 +20,12 @@ set(llmath_SOURCE_FILES      llcoordframe.cpp      llline.cpp      llmatrix3a.cpp +    llmatrix4a.cpp      llmodularmath.cpp      lloctree.cpp      llperlin.cpp      llquaternion.cpp +    llrigginginfo.cpp      llrect.cpp      llsphere.cpp      llvector4a.cpp @@ -70,6 +72,7 @@ set(llmath_HEADER_FILES      llquaternion2.h      llquaternion2.inl      llrect.h +    llrigginginfo.h      llsimdmath.h      llsimdtypes.h      llsimdtypes.inl diff --git a/indra/llmath/llmatrix4a.cpp b/indra/llmath/llmatrix4a.cpp new file mode 100644 index 0000000000..fe8f0b98f3 --- /dev/null +++ b/indra/llmath/llmatrix4a.cpp @@ -0,0 +1,80 @@ +/** +* @file llmatrix4a.cpp +* @brief  Functions for vectorized matrix/vector operations +* +* $LicenseInfo:firstyear=2018&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2018, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "llmath.h" +#include "llmatrix4a.h" + +// Convert a bounding box into other coordinate system. Should give +// the same results as transforming every corner of the bounding box +// and extracting the bounding box of that, although that's not +// necessarily the fastest way to implement. +void matMulBoundBox(const LLMatrix4a &mat, const LLVector4a *in_extents, LLVector4a *out_extents) +{ +		//get 8 corners of bounding box +		LLVector4Logical mask[6]; + +		for (U32 i = 0; i < 6; ++i) +		{ +			mask[i].clear(); +		} + +		mask[0].setElement<2>(); //001 +		mask[1].setElement<1>(); //010 +		mask[2].setElement<1>(); //011 +		mask[2].setElement<2>(); +		mask[3].setElement<0>(); //100 +		mask[4].setElement<0>(); //101 +		mask[4].setElement<2>(); +		mask[5].setElement<0>(); //110 +		mask[5].setElement<1>(); + +		LLVector4a v[8]; + +		v[6] = in_extents[0]; +		v[7] = in_extents[1]; + +		for (U32 i = 0; i < 6; ++i) +		{ +			v[i].setSelectWithMask(mask[i], in_extents[0], in_extents[1]); +		} + +		LLVector4a tv[8]; + +		//transform bounding box into drawable space +		for (U32 i = 0; i < 8; ++i) +		{ +			mat.affineTransform(v[i], tv[i]); +		} +	 +		//find bounding box +		out_extents[0] = out_extents[1] = tv[0]; + +		for (U32 i = 1; i < 8; ++i) +		{ +			out_extents[0].setMin(out_extents[0], tv[i]); +			out_extents[1].setMax(out_extents[1], tv[i]); +		} +} diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h index 216334752a..7ba347062f 100644 --- a/indra/llmath/llmatrix4a.h +++ b/indra/llmath/llmatrix4a.h @@ -121,7 +121,7 @@ public:  		res.add(z);  	} -	inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res) +	inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res) const  	{  		LLVector4a x,y,z; @@ -138,7 +138,7 @@ public:  		res.setAdd(x,z);  	} -    inline void affineTransformNonSSE(const LLVector4a& v, LLVector4a& res) +    inline void affineTransformNonSSE(const LLVector4a& v, LLVector4a& res) const      {          F32 x = v[0] * mMatrix[0][0] + v[1] * mMatrix[1][0] + v[2] * mMatrix[2][0] + mMatrix[3][0];          F32 y = v[0] * mMatrix[0][1] + v[1] * mMatrix[1][1] + v[2] * mMatrix[2][1] + mMatrix[3][1]; @@ -147,7 +147,7 @@ public:          res.set(x,y,z,w);      } -	inline void affineTransform(const LLVector4a& v, LLVector4a& res) +	inline void affineTransform(const LLVector4a& v, LLVector4a& res) const      {          affineTransformSSE(v,res);      } @@ -176,4 +176,12 @@ inline void matMul(const LLMatrix4a &a, const LLMatrix4a &b, LLMatrix4a &res)      res.mMatrix[3] = row3;  } +inline std::ostream& operator<<(std::ostream& s, const LLMatrix4a& m) +{ +    s << "[" << m.mMatrix[0] << ", " << m.mMatrix[1] << ", " << m.mMatrix[2] << ", " << m.mMatrix[3] << "]"; +    return s; +}  + +void matMulBoundBox(const LLMatrix4a &a, const LLVector4a *in_extents, LLVector4a *out_extents); +  #endif diff --git a/indra/llmath/llrigginginfo.cpp b/indra/llmath/llrigginginfo.cpp new file mode 100644 index 0000000000..0de07950c1 --- /dev/null +++ b/indra/llmath/llrigginginfo.cpp @@ -0,0 +1,159 @@ +/** +* @file llrigginginfo.cpp +* @brief  Functions for tracking rigged box extents +* +* $LicenseInfo:firstyear=2018&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2018, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +#include "llmath.h" +#include "llrigginginfo.h" + +//----------------------------------------------------------------------------- +// LLJointRiggingInfo +//----------------------------------------------------------------------------- +LLJointRiggingInfo::LLJointRiggingInfo() +{ +    mRiggedExtents[0].clear(); +    mRiggedExtents[1].clear(); +    mIsRiggedTo = false; +} + +bool LLJointRiggingInfo::isRiggedTo() const +{ +    return mIsRiggedTo; +} + +void LLJointRiggingInfo::setIsRiggedTo(bool val) +{ +    mIsRiggedTo = val; +} +     +LLVector4a *LLJointRiggingInfo::getRiggedExtents() +{ +    return mRiggedExtents; +} + +const LLVector4a *LLJointRiggingInfo::getRiggedExtents() const +{ +    return mRiggedExtents; +} + +// Combine two rigging info states. +// - isRiggedTo if either of the source infos are rigged to +// - box is union of the two sources +void LLJointRiggingInfo::merge(const LLJointRiggingInfo& other) +{ +    if (other.mIsRiggedTo) +    { +        if (mIsRiggedTo) +        { +            // Combine existing boxes +            update_min_max(mRiggedExtents[0], mRiggedExtents[1], other.mRiggedExtents[0]); +            update_min_max(mRiggedExtents[0], mRiggedExtents[1], other.mRiggedExtents[1]); +        } +        else +        { +            // Initialize box +            mIsRiggedTo = true; +            mRiggedExtents[0] = other.mRiggedExtents[0]; +            mRiggedExtents[1] = other.mRiggedExtents[1]; +        } +    } +} + +LLJointRiggingInfoTab::LLJointRiggingInfoTab(): +    mRigInfoPtr(NULL), +    mSize(0), +    mNeedsUpdate(true) +{ +} + +LLJointRiggingInfoTab::~LLJointRiggingInfoTab() +{ +    clear(); +} + +// This doesn't preserve data if the size changes. In practice +// this doesn't matter because the size is always either +// LL_CHARACTER_MAX_ANIMATED_JOINTS or 0. +void LLJointRiggingInfoTab::resize(S32 size) +{ +    if (size != mSize) +    { +        clear(); +        if (size > 0) +        { +            mRigInfoPtr = new LLJointRiggingInfo[size]; +            mSize = size; +        } +    } +} + +void LLJointRiggingInfoTab::clear() +{ +    if (mRigInfoPtr) +    { +        delete[](mRigInfoPtr); +        mRigInfoPtr = NULL; +        mSize = 0; +    } +} + +void showDetails(const LLJointRiggingInfoTab& src, const std::string& str) +{ +	S32 count_rigged = 0; +	S32 count_box = 0; +    LLVector4a zero_vec; +    zero_vec.clear(); +    for (S32 i=0; i<src.size(); i++) +    { +        if (src[i].isRiggedTo()) +        { +            count_rigged++; +            if ((!src[i].getRiggedExtents()[0].equals3(zero_vec)) || +                (!src[i].getRiggedExtents()[1].equals3(zero_vec))) +            { +                count_box++; +            } +       } +    } +    LL_DEBUGS("RigSpammish") << "details: " << str << " has " << count_rigged << " rigged joints, of which " << count_box << " are non-empty" << LL_ENDL; +} + +void LLJointRiggingInfoTab::merge(const LLJointRiggingInfoTab& src) +{ +    //showDetails(*this, "input this"); +    // Size should be either LL_CHARACTER_MAX_ANIMATED_JOINTS, or 0 if +    // no data. Not necessarily the same for both inputs. +    if (src.size() > size()) +    { +        resize(src.size()); +    } +    S32 min_size = llmin(size(), src.size()); +    for (S32 i=0; i<min_size; i++) +    { +        (*this)[i].merge(src[i]); +    } +    //showDetails(src, "input src"); +    //showDetails(*this, "output this"); + +} diff --git a/indra/llmath/llrigginginfo.h b/indra/llmath/llrigginginfo.h new file mode 100644 index 0000000000..b3d6bc2d19 --- /dev/null +++ b/indra/llmath/llrigginginfo.h @@ -0,0 +1,100 @@ +/** +* @file llrigginginfo.h +* @brief  Functions for tracking rigged box extents +* +* $LicenseInfo:firstyear=2018&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2018, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA +* $/LicenseInfo$ +*/ + +// Stores information related to associated rigged mesh vertices +// This lives in llmath because llvolume lives in llmath. + +#ifndef	LL_LLRIGGINGINFO_H +#define	LL_LLRIGGINGINFO_H + +#include "llvector4a.h" + +// Extents are in joint space +// isRiggedTo is based on the state of all currently associated rigged meshes +LL_ALIGN_PREFIX(16) +class LLJointRiggingInfo +{ +public: +    LLJointRiggingInfo(); +    bool isRiggedTo() const; +    void setIsRiggedTo(bool val); +    LLVector4a *getRiggedExtents(); +    const LLVector4a *getRiggedExtents() const; +    void merge(const LLJointRiggingInfo& other); + +	void* operator new(size_t size) +	{ +		return ll_aligned_malloc_16(size); +	} + +	void operator delete(void* ptr) +	{ +		ll_aligned_free_16(ptr); +	} + +	void* operator new[](size_t size) +	{ +		return ll_aligned_malloc_16(size); +	} + +	void operator delete[](void* ptr) +	{ +		ll_aligned_free_16(ptr); +	} + + +private: +	LL_ALIGN_16(LLVector4a mRiggedExtents[2]); +    bool mIsRiggedTo; +} LL_ALIGN_POSTFIX(16); + +// For storing all the rigging info associated with a given avatar or +// object, keyed by joint_num. +// Using direct memory management instead of std::vector<> to avoid alignment issues. +class LLJointRiggingInfoTab +{ +public: +    LLJointRiggingInfoTab(); +    ~LLJointRiggingInfoTab(); +    void resize(S32 size); +    void clear(); +    S32 size() const { return mSize; } +    void merge(const LLJointRiggingInfoTab& src); +    LLJointRiggingInfo& operator[](S32 i) { return mRigInfoPtr[i]; } +    const LLJointRiggingInfo& operator[](S32 i) const { return mRigInfoPtr[i]; }; +    bool needsUpdate() { return mNeedsUpdate; } +    void setNeedsUpdate(bool val) { mNeedsUpdate = val; } +private: +    // Not implemented +    LLJointRiggingInfoTab& operator=(const LLJointRiggingInfoTab& src); +    LLJointRiggingInfoTab(const LLJointRiggingInfoTab& src); + +    LLJointRiggingInfo *mRigInfoPtr; +    S32 mSize; +    bool mNeedsUpdate; +}; + +#endif diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h index 79d0a44551..222f3cf235 100644 --- a/indra/llmath/llvector4a.h +++ b/indra/llmath/llvector4a.h @@ -320,7 +320,7 @@ public:  	inline const LLVector4a& operator= ( const LLQuad& rhs );  	inline operator LLQuad() const;	 - +      private:  	LLQuad mQ;  } LL_ALIGN_POSTFIX(16); @@ -331,4 +331,9 @@ inline void update_min_max(LLVector4a& min, LLVector4a& max, const LLVector4a& p  	max.setMax(max, p);  } +inline std::ostream& operator<<(std::ostream& s, const LLVector4a& v) +{ +    s << "(" << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << ")"; +    return s; +}  #endif diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 6b6cd65ce2..ba284574c8 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -1,5 +1,4 @@  /**  -   * @file llvolume.cpp   *   * $LicenseInfo:firstyear=2002&license=viewerlgpl$ @@ -2639,6 +2638,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)  			}  			//calculate bounding box +			// VFExtents change  			LLVector4a& min = face.mExtents[0];  			LLVector4a& max = face.mExtents[1]; @@ -4768,6 +4768,7 @@ LLVolumeFace::~LLVolumeFace()  {  	ll_aligned_free_16(mExtents);  	mExtents = NULL; +	mCenter = NULL;  	freeData();  } @@ -4949,7 +4950,7 @@ void LLVolumeFace::optimize(F32 angle_cutoff)  	//  	if (new_face.mNumVertices <= mNumVertices)  	{ -	llassert(new_face.mNumIndices == mNumIndices); +        llassert(new_face.mNumIndices == mNumIndices);  		swapData(new_face);  	} @@ -5570,7 +5571,7 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)  	// S32 i;  	S32	grid_size = (profile.size()-1)/4; - +	// VFExtents change  	LLVector4a& min = mExtents[0];  	LLVector4a& max = mExtents[1]; @@ -5847,7 +5848,7 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)  	LLVector2 cuv;  	LLVector2 min_uv, max_uv; - +	// VFExtents change  	LLVector4a& min = mExtents[0];  	LLVector4a& max = mExtents[1]; @@ -6286,6 +6287,9 @@ void LLVolumeFace::resizeVertices(S32 num_verts)  	mNumVertices = num_verts;  	mNumAllocatedVertices = num_verts; + +    // Force update +    mJointRiggingInfoTab.clear();  }  void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv) @@ -6407,96 +6411,6 @@ void LLVolumeFace::fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v,  	}  } -void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat_in, LLMatrix4& norm_mat_in) -{ -	U16 offset = mNumVertices; - -	S32 new_count = face.mNumVertices + mNumVertices; - -	if (new_count > 65536) -	{ -		LL_ERRS() << "Cannot append face -- 16-bit overflow will occur." << LL_ENDL; -	} -	 -	if (face.mNumVertices == 0) -	{ -		LL_ERRS() << "Cannot append empty face." << LL_ENDL; -	} - -	U32 old_vsize = mNumVertices*16; -	U32 new_vsize = new_count * 16; -	U32 old_tcsize = (mNumVertices*sizeof(LLVector2)+0xF) & ~0xF; -	U32 new_tcsize = (new_count*sizeof(LLVector2)+0xF) & ~0xF; -	U32 new_size = new_vsize * 2 + new_tcsize; - -	//allocate new buffer space -	LLVector4a* old_buf = mPositions; -	mPositions = (LLVector4a*) ll_aligned_malloc<64>(new_size); -	mNormals = mPositions + new_count; -	mTexCoords = (LLVector2*) (mNormals+new_count); - -	mNumAllocatedVertices = new_count; - -	LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) old_buf, old_vsize); -	LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) (old_buf+mNumVertices), old_vsize); -	LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) (old_buf+mNumVertices*2), old_tcsize); -	 -	mNumVertices = new_count; - -	//get destination address of appended face -	LLVector4a* dst_pos = mPositions+offset; -	LLVector2* dst_tc = mTexCoords+offset; -	LLVector4a* dst_norm = mNormals+offset; - -	//get source addresses of appended face -	const LLVector4a* src_pos = face.mPositions; -	const LLVector2* src_tc = face.mTexCoords; -	const LLVector4a* src_norm = face.mNormals; - -	//load aligned matrices -	LLMatrix4a mat, norm_mat; -	mat.loadu(mat_in); -	norm_mat.loadu(norm_mat_in); - -	for (U32 i = 0; i < face.mNumVertices; ++i) -	{ -		//transform appended face position and store -		mat.affineTransform(src_pos[i], dst_pos[i]); - -		//transform appended face normal and store -		norm_mat.rotate(src_norm[i], dst_norm[i]); -		dst_norm[i].normalize3fast(); - -		//copy appended face texture coordinate -		dst_tc[i] = src_tc[i]; - -		if (offset == 0 && i == 0) -		{ //initialize bounding box -			mExtents[0] = mExtents[1] = dst_pos[i]; -		} -		else -		{ -			//stretch bounding box -			update_min_max(mExtents[0], mExtents[1], dst_pos[i]); -		} -	} - - -	new_count = mNumIndices + face.mNumIndices; - -	//allocate new index buffer -	mIndices = (U16*) ll_aligned_realloc_16(mIndices, (new_count*sizeof(U16)+0xF) & ~0xF, (mNumIndices*sizeof(U16)+0xF) & ~0xF); -	 -	//get destination address into new index buffer -	U16* dst_idx = mIndices+mNumIndices; -	mNumIndices = new_count; - -	for (U32 i = 0; i < face.mNumIndices; ++i) -	{ //copy indices, offsetting by old vertex count -		dst_idx[i] = face.mIndices[i]+offset; -	} -} -  BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)  {  	LL_CHECK_MEMORY @@ -6642,7 +6556,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)  	{  		update_min_max(face_min, face_max, *cur_pos++);  	} - +	// VFExtents change  	mExtents[0] = face_min;  	mExtents[1] = face_max; diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index d3c1ac46fe..1d6d35c432 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -57,6 +57,7 @@ class LLVolumeTriangle;  #include "llpointer.h"  #include "llfile.h"  #include "llalignedarray.h" +#include "llrigginginfo.h"  //============================================================================ @@ -871,8 +872,6 @@ public:  	BOOL create(LLVolume* volume, BOOL partial_build = FALSE);  	void createTangents(); -	void appendFace(const LLVolumeFace& face, LLMatrix4& transform, LLMatrix4& normal_tranform); -  	void resizeVertices(S32 num_verts);  	void allocateTangents(S32 num_verts);  	void allocateWeights(S32 num_verts); @@ -958,6 +957,10 @@ public:  	LLVector4a* mWeights;      mutable BOOL mWeightsScrubbed; + +    // Which joints are rigged to, and the bounding box of any rigged +    // vertices per joint. +    LLJointRiggingInfoTab mJointRiggingInfoTab;  	LLOctreeNode<LLVolumeTriangle>* mOctree; diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp index e7107dee16..b04c67d926 100644 --- a/indra/llmath/v3math.cpp +++ b/indra/llmath/v3math.cpp @@ -369,3 +369,39 @@ BOOL LLVector3::parseVector3(const std::string& buf, LLVector3* value)  	return FALSE;  } + +// Displacement from query point to nearest neighbor point on bounding box. +// Returns zero vector for points within or on the box. +LLVector3 point_to_box_offset(LLVector3& pos, const LLVector3* box) +{ +    LLVector3 offset; +    for (S32 k=0; k<3; k++) +    { +        offset[k] = 0; +        if (pos[k] < box[0][k]) +        { +            offset[k] = pos[k] - box[0][k]; +        } +        else if (pos[k] > box[1][k]) +        { +            offset[k] = pos[k] - box[1][k]; +        } +    } +    return offset; +} + +bool box_valid_and_non_zero(const LLVector3* box) +{ +    if (!box[0].isFinite() || !box[1].isFinite()) +    { +        return false; +    } +    LLVector3 zero_vec; +    zero_vec.clear(); +    if ((box[0] != zero_vec) || (box[1] != zero_vec)) +    { +        return true; +    } +    return false; +} + diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h index f3fbce4843..6f857d7061 100644 --- a/indra/llmath/v3math.h +++ b/indra/llmath/v3math.h @@ -163,6 +163,8 @@ LLVector3 inverse_projected_vec(const LLVector3 &a, const LLVector3 &b); // Retu  LLVector3 parallel_component(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b (same as projected_vec)  LLVector3 orthogonal_component(const LLVector3 &a, const LLVector3 &b); // Returns component of vector a not parallel to vector b (same as projected_vec)  LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u); // Returns a vector that is a linear interpolation between a and b +LLVector3 point_to_box_offset(LLVector3& pos, const LLVector3* box); // Displacement from query point to nearest point on bounding box. +bool box_valid_and_non_zero(const LLVector3* box);  inline LLVector3::LLVector3(void)  { diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index 1ae8a6ac15..f8e11e324e 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -818,6 +818,7 @@ char const* const _PREHASH_StateSave = LLMessageStringTable::getInstance()->getS  char const* const _PREHASH_RoleData = LLMessageStringTable::getInstance()->getString("RoleData");  char const* const _PREHASH_AgentAnimation = LLMessageStringTable::getInstance()->getString("AgentAnimation");  char const* const _PREHASH_AvatarAnimation = LLMessageStringTable::getInstance()->getString("AvatarAnimation"); +char const* const _PREHASH_ObjectAnimation = LLMessageStringTable::getInstance()->getString("ObjectAnimation");  char const* const _PREHASH_LogDwellTime = LLMessageStringTable::getInstance()->getString("LogDwellTime");  char const* const _PREHASH_ParcelGodMarkAsContent = LLMessageStringTable::getInstance()->getString("ParcelGodMarkAsContent");  char const* const _PREHASH_UsePhysics = LLMessageStringTable::getInstance()->getString("UsePhysics"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 7910fde305..334236fb25 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -818,6 +818,7 @@ extern char const* const _PREHASH_StateSave;  extern char const* const _PREHASH_RoleData;  extern char const* const _PREHASH_AgentAnimation;  extern char const* const _PREHASH_AvatarAnimation; +extern char const* const _PREHASH_ObjectAnimation;  extern char const* const _PREHASH_LogDwellTime;  extern char const* const _PREHASH_ParcelGodMarkAsContent;  extern char const* const _PREHASH_UsePhysics; diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 8401cb976e..8f75d89e5a 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -192,7 +192,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa  		{  			return LLModel::BAD_ELEMENT;  		} - +		// VFExtents change  		face.mExtents[0].set(v[0], v[1], v[2]);  		face.mExtents[1].set(v[0], v[1], v[2]);  	} @@ -254,6 +254,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa  		if (!found)  		{ +			// VFExtents change  			update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());  			verts.push_back(cv);  			if (verts.size() >= 65535) @@ -305,6 +306,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa  			}  			face = LLVolumeFace(); +			// VFExtents change  			face.mExtents[0].set(v[0], v[1], v[2]);  			face.mExtents[1].set(v[0], v[1], v[2]);  			point_map.clear(); @@ -383,6 +385,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac  	if (pos_source)  	{  		v = pos_source->getFloat_array()->getValue(); +		// VFExtents change  		face.mExtents[0].set(v[0], v[1], v[2]);  		face.mExtents[1].set(v[0], v[1], v[2]);  	} @@ -482,6 +485,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac  			if (!found)  			{ +				// VFExtents change  				update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());  				verts.push_back(cv);  				if (verts.size() >= 65535) @@ -551,6 +555,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac  				}  				face = LLVolumeFace(); +				// VFExtents change  				face.mExtents[0].set(v[0], v[1], v[2]);  				face.mExtents[1].set(v[0], v[1], v[2]);  				verts.clear(); @@ -734,7 +739,7 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac  	{  		return LLModel::NO_ERRORS;  	} - +	// VFExtents change  	face.mExtents[0] = verts[0].getPosition();  	face.mExtents[1] = verts[0].getPosition(); @@ -758,6 +763,7 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac  	for (std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter)  	{  		new_verts[iter->second] = iter->first; +		// VFExtents change  		update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition());  	} diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 8fbb4f6b96..37548e3fe3 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -276,6 +276,7 @@ void LLModel::normalizeVolumeFaces()  			// We shrink the extents so  			// that they fall within  			// the unit cube. +			// VFExtents change  			face.mExtents[0].add(trans);  			face.mExtents[0].mul(scale); @@ -400,40 +401,6 @@ void LLModel::setVolumeFaceData(  	LLVector4a::memcpyNonAliased16((F32*) face.mIndices, (F32*) ind.get(), size);  } -void LLModel::appendFaces(LLModel *model, LLMatrix4 &transform, LLMatrix4& norm_mat) -{ -	if (mVolumeFaces.empty()) -	{ -		setNumVolumeFaces(1); -	} - -	LLVolumeFace& face = mVolumeFaces[mVolumeFaces.size()-1]; - - -	for (S32 i = 0; i < model->getNumFaces(); ++i) -	{ -		face.appendFace(model->getVolumeFace(i), transform, norm_mat); -	} - -} - -void LLModel::appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat) -{ -	S32 rindex = getNumVolumeFaces()-1;  -	if (rindex == -1 ||  -		mVolumeFaces[rindex].mNumVertices + src_face.mNumVertices >= 65536) -	{ //empty or overflow will occur, append new face -		LLVolumeFace cur_face; -		cur_face.appendFace(src_face, mat, norm_mat); -		addFace(cur_face); -		mMaterialList.push_back(src_material); -	} -	else -	{ //append to existing end face -		mVolumeFaces.rbegin()->appendFace(src_face, mat, norm_mat); -	} -} -  void LLModel::addFace(const LLVolumeFace& face)  {  	if (face.mNumVertices == 0) @@ -1391,14 +1358,16 @@ bool LLModel::loadDecomposition(LLSD& header, std::istream& is)  LLMeshSkinInfo::LLMeshSkinInfo():      mPelvisOffset(0.0),      mLockScaleIfJointPosition(false), -    mInvalidJointsScrubbed(false) +    mInvalidJointsScrubbed(false), +    mJointNumsInitialized(false)  {  }  LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin):      mPelvisOffset(0.0),      mLockScaleIfJointPosition(false), -    mInvalidJointsScrubbed(false) +    mInvalidJointsScrubbed(false), +    mJointNumsInitialized(false)  {  	fromLLSD(skin);  } diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index 097558ef67..cf3288489a 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -38,7 +38,6 @@ class domMesh;  #define MAX_MODEL_FACES 8 -  class LLMeshSkinInfo   {  public: @@ -57,6 +56,7 @@ public:  	float mPelvisOffset;      bool mLockScaleIfJointPosition;      bool mInvalidJointsScrubbed; +    bool mJointNumsInitialized;  };  class LLModel : public LLVolume @@ -158,9 +158,6 @@ public:  	EModelStatus getStatus() const {return mStatus;}  	static std::string getStatusString(U32 status) ; -	void appendFaces(LLModel* model, LLMatrix4& transform, LLMatrix4& normal_transform); -	void appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat); -  	void setNumVolumeFaces(S32 count);  	void setVolumeFaceData(  		S32 f,  diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h index d64e0a0773..643c45a6d8 100644 --- a/indra/llprimitive/llmodelloader.h +++ b/indra/llprimitive/llmodelloader.h @@ -81,6 +81,7 @@ public:  		GENERATING_VERTEX_BUFFERS,  		GENERATING_LOD,  		DONE, +		WARNING_BIND_SHAPE_ORIENTATION,  		ERROR_PARSING, //basically loading failed  		ERROR_MATERIALS,  		ERROR_PASSWORD_REQUIRED, diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index edf7c41e40..c69c059880 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1609,6 +1609,8 @@ BOOL LLNetworkData::isValid(U16 param_type, U32 size)  		return (size == 17);  	case PARAMS_LIGHT_IMAGE:  		return (size == 28); +    case PARAMS_EXTENDED_MESH: +        return (size == 4);  	}  	return FALSE; @@ -2036,3 +2038,67 @@ bool LLLightImageParams::fromLLSD(LLSD& sd)  	return false;  } + +//============================================================================ + +LLExtendedMeshParams::LLExtendedMeshParams() +{ +    mType = PARAMS_EXTENDED_MESH; +	mFlags = 0; +} + +BOOL LLExtendedMeshParams::pack(LLDataPacker &dp) const +{ +	dp.packU32(mFlags, "flags"); + +	return TRUE; +} + +BOOL LLExtendedMeshParams::unpack(LLDataPacker &dp) +{ +	dp.unpackU32(mFlags, "flags"); +	 +	return TRUE; +} + +bool LLExtendedMeshParams::operator==(const LLNetworkData& data) const +{ +	if (data.mType != PARAMS_EXTENDED_MESH) +	{ +		return false; +	} +	 +	const LLExtendedMeshParams *param = (const LLExtendedMeshParams*)&data; +	if ( (param->mFlags != mFlags) ) +	{ +		return false; +	} + +	return true; +} + +void LLExtendedMeshParams::copy(const LLNetworkData& data) +{ +	const LLExtendedMeshParams *param = (LLExtendedMeshParams*)&data; +	mFlags = param->mFlags; +} + +LLSD LLExtendedMeshParams::asLLSD() const +{ +	LLSD sd; +	 +	sd["flags"] = LLSD::Integer(mFlags); +		 +	return sd; +} + +bool LLExtendedMeshParams::fromLLSD(LLSD& sd) +{ +	if (sd.has("flags")) +	{ +		setFlags( sd["flags"].asInteger()); +		return true; +	}  +	 +	return false; +} diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index 99a32e614c..c138c2ac2b 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -106,6 +106,7 @@ public:  		PARAMS_LIGHT_IMAGE = 0x40,  		PARAMS_RESERVED = 0x50, // Used on server-side  		PARAMS_MESH     = 0x60, +        PARAMS_EXTENDED_MESH = 0x70,  	};  public: @@ -288,6 +289,27 @@ public:  }; +class LLExtendedMeshParams : public LLNetworkData +{ +protected: +	U32 mFlags; +	 +public: +	static const U32 ANIMATED_MESH_ENABLED_FLAG = 0x1 << 0; + +	LLExtendedMeshParams(); +	/*virtual*/ BOOL pack(LLDataPacker &dp) const; +	/*virtual*/ BOOL unpack(LLDataPacker &dp); +	/*virtual*/ bool operator==(const LLNetworkData& data) const; +	/*virtual*/ void copy(const LLNetworkData& data); +	LLSD asLLSD() const; +	operator LLSD() const { return asLLSD(); } +	bool fromLLSD(LLSD& sd); + +	void setFlags(const U32& flags) { mFlags = flags; } +    U32 getFlags() const { return mFlags; } +	 +};  // This code is not naming-standards compliant. Leaving it like this for  // now to make the connection to code in diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 2fc722d4c3..ce8b662231 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -151,6 +151,7 @@ set(viewer_SOURCE_FILES      llcommunicationchannel.cpp      llcompilequeue.cpp      llconfirmationmanager.cpp +    llcontrolavatar.cpp      llconversationlog.cpp      llconversationloglist.cpp      llconversationloglistitem.cpp @@ -604,6 +605,7 @@ set(viewer_SOURCE_FILES      lltransientfloatermgr.cpp      lltranslate.cpp      lltwitterconnect.cpp +    lluiavatar.cpp      lluilistener.cpp      lluploaddialog.cpp      llurl.cpp @@ -773,6 +775,7 @@ set(viewer_HEADER_FILES      llcommunicationchannel.h      llcompilequeue.h      llconfirmationmanager.h +    llcontrolavatar.h      llconversationlog.h      llconversationloglist.h      llconversationloglistitem.h @@ -1220,6 +1223,7 @@ set(viewer_HEADER_FILES      lltranslate.h      lltwitterconnect.h      lluiconstants.h +    lluiavatar.h      lluilistener.h      lluploaddialog.h      lluploadfloaterobservers.h diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 48ea19be07..09b254e90c 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -5.1.10 +6.0.0 diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index 8ced81fdb3..482012cdd6 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -2,6 +2,22 @@  	<map>  		<!-- default-level can be ALL, DEBUG, INFO, WARN, ERROR, or NONE -->  		<key>default-level</key>    <string>INFO</string> +		<key>print-location</key>   <boolean>false</boolean> +		<key>log-always-flush</key>   <boolean>true</boolean> +		<!-- All log types are enabled by default. Can be toggled individually; +             bitwise-or all the ones you want to enable. +             Log types and their masks are: + +             1  - RecordToSyslog (not used by viewer) +             2  - RecordToFile (SecondLife.log) +             4  - RecordToStderr (this will appear in the console window, if there is one) +             8  - RecordToFixedBuffer (viewer debug console) +             16 - RecordToWinDebug (on windows, output to VS IDE window) + +             For example, value of 10 = 2|8 would enable logging only to SecondLife.log and the viewer debug console. +             Note: RecordToFile is always enabled in release builds. +        --> +		<key>enabled-log-types-mask</key>   <integer>255</integer>  		<key>settings</key>  			<array>  				<!-- Suppress anything but ERROR for some very verbose components --> @@ -50,6 +66,7 @@  					<key>tags</key>  						<array>  						<!-- sample entry for debugging specific items	 +                             <string>AnimatedObjects</string>  						     <string>Avatar</string>  						     <string>Inventory</string>  						     <string>SceneLoadTiming</string> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 8e8cce5787..3ad8b6cded 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2182,6 +2182,94 @@        <key>Value</key>        <string />      </map> +  <key>DebugAnimatedObjects</key> +  <map> +    <key>Comment</key> +    <string>Show info related to animated objects</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Boolean</string> +    <key>Value</key> +    <integer>0</integer> +  </map> +  <key>DebugObjectLODs</key> +  <map> +    <key>Comment</key> +    <string>Show info related to lod calculations for attached or animated objects</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Boolean</string> +    <key>Value</key> +    <integer>0</integer> +  </map> +  <key>AnimatedObjectsIgnoreLimits</key> +  <map> +    <key>Comment</key> +    <string>Ignore server-enforced limits on animated objects. This is only useful for server testing.</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Boolean</string> +    <key>Value</key> +    <integer>0</integer> +  </map> +  <key>AnimatedObjectsAllowLeftClick</key> +  <map> +    <key>Comment</key> +    <string>Allow left-click interaction with animated objects. Uncertain how much performance impact this will have.</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Boolean</string> +    <key>Value</key> +    <integer>0</integer> +  </map> +  <key>AnimatedObjectsGlobalScale</key> +  <map> +    <key>Comment</key> +    <string>Temporary testing: allow an extra scale factor to be forced on animated objects.</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>F32</string> +    <key>Value</key> +    <real>1.00</real> +  </map> +  <key>AnimatedObjectsMaxLegalOffset</key> +  <map> +    <key>Comment</key> +    <string>Max visual offset between object position and rendered position</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>F32</string> +    <key>Value</key> +    <real>3.0</real> +  </map> +  <key>AnimatedObjectsMaxLegalSize</key> +  <map> +    <key>Comment</key> +    <string>Max bounding box size for animated object's rendered position</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>F32</string> +    <key>Value</key> +    <real>64.0</real> +  </map> +  <key>AvatarBoundingBoxComplexity</key> +  <map> +    <key>Comment</key> +    <string>How many aspects to consider for avatar bounding box</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>S32</string> +    <key>Value</key> +    <integer>3</integer> +  </map>    <key>DebugAvatarAppearanceMessage</key>    <map>      <key>Comment</key> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 9b8cc62aa8..ba250fa471 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -881,6 +881,16 @@ void LLAgent::setRegion(LLViewerRegion *regionp)  			{  				gSky.mVOGroundp->setRegion(regionp);  			} + +            if (regionp->capabilitiesReceived()) +            { +                regionp->requestSimulatorFeatures(); +            } +            else +            { +                regionp->setCapabilitiesReceivedCallback(boost::bind(&LLViewerRegion::requestSimulatorFeatures, regionp)); +            } +  		}  		else  		{ diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index c0f88ef704..08daeb0f59 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -880,7 +880,10 @@ void LLWearableHoldingPattern::onAllComplete()  			 ++it)  		{  			LLViewerObject *objectp = *it; -			gAgentAvatarp->addAttachmentOverridesForObject(objectp); +            if (!objectp->isAnimatedObject()) +            { +                gAgentAvatarp->addAttachmentOverridesForObject(objectp); +            }  		}  		// Add new attachments to match those requested. diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 189f7c1426..03927f2d7c 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1086,7 +1086,7 @@ bool LLAppViewer::init()  // don't nag developers who need to run the executable directly  #if LL_RELEASE_FOR_DOWNLOAD  	// MAINT-8305: If we're processing a SLURL, skip the launcher check. -	if (gSavedSettings.getString("CmdLineLoginLocation").empty()) +	if (gSavedSettings.getString("CmdLineLoginLocation").empty() && !beingDebugged())  	{  		const char* PARENT = getenv("PARENT");  		if (! (PARENT && std::string(PARENT) == "SL_Launcher")) diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index f0a4a54fbf..3942613ee0 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -646,6 +646,11 @@ bool LLAppViewerWin32::initParseCommandLine(LLCommandLineParser& clp)  	return true;  } +bool LLAppViewerWin32::beingDebugged() +{ +    return IsDebuggerPresent(); +} +  bool LLAppViewerWin32::restoreErrorTrap()  {	  	return true; diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h index 59d1ddaa3d..c5fae6a3a3 100644 --- a/indra/newview/llappviewerwin32.h +++ b/indra/newview/llappviewerwin32.h @@ -49,6 +49,7 @@ protected:  	virtual bool initHardwareTest(); // Win32 uses DX9 to test hardware.  	virtual bool initParseCommandLine(LLCommandLineParser& clp); +	virtual bool beingDebugged();  	virtual bool restoreErrorTrap();  	virtual void initCrashReporting(bool reportFreeze);  diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp index 7c7f55f68c..ca83afb5ab 100644 --- a/indra/newview/llavatarrenderinfoaccountant.cpp +++ b/indra/newview/llavatarrenderinfoaccountant.cpp @@ -112,8 +112,13 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro(std::string url, U64                   )              {                  LLUUID target_agent_id = LLUUID(agent_iter->first); -                LLViewerObject* avatarp = gObjectList.findObject(target_agent_id); -                if (avatarp && avatarp->isAvatar()) +                LLViewerObject* vobjp = gObjectList.findObject(target_agent_id); +                LLVOAvatar *avatarp = NULL; +                if (vobjp) +                { +                    avatarp = vobjp->asAvatar(); +                } +                if (avatarp && !avatarp->isControlAvatar())                  {                      const LLSD & agent_info_map = agent_iter->second;                      if (agent_info_map.isMap()) @@ -123,7 +128,7 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro(std::string url, U64                          if (agent_info_map.has(KEY_WEIGHT))                          { -                            ((LLVOAvatar *) avatarp)->setReportedVisualComplexity(agent_info_map[KEY_WEIGHT].asInteger()); +                            avatarp->setReportedVisualComplexity(agent_info_map[KEY_WEIGHT].asInteger());                          }                      }                      else @@ -201,6 +206,7 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro(std::string url, U          if (avatar &&              avatar->getRezzedStatus() >= 2 &&					// Mostly rezzed (maybe without baked textures downloaded)              !avatar->isDead() &&								// Not dead yet +            !avatar->isControlAvatar() &&						// Not part of an animated object              avatar->getObjectHost() == regionp->getHost())		// Ensure it's on the same region          {              avatar->calculateUpdateRenderComplexity();			// Make sure the numbers are up-to-date diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp new file mode 100644 index 0000000000..a620f2abe9 --- /dev/null +++ b/indra/newview/llcontrolavatar.cpp @@ -0,0 +1,576 @@ +/** + * @file llcontrolavatar.cpp + * @brief Implementation for special dummy avatar used to drive rigged meshes. + * + * $LicenseInfo:firstyear=2017&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2017, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llcontrolavatar.h" +#include "llagent.h" //  Get state values from here +#include "llviewerobjectlist.h" +#include "pipeline.h" +#include "llanimationstates.h" +#include "llviewercontrol.h" +#include "llmeshrepository.h" +#include "llviewerregion.h" +#include "llskinningutil.h" + +const F32 LLControlAvatar::MAX_LEGAL_OFFSET = 3.0f; +const F32 LLControlAvatar::MAX_LEGAL_SIZE = 64.0f; + +LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) : +    LLVOAvatar(id, pcode, regionp), +    mPlaying(false), +    mGlobalScale(1.0f), +    mMarkedForDeath(false), +    mRootVolp(NULL), +    mScaleConstraintFixup(1.0) +{ +    mIsDummy = TRUE; +    mIsControlAvatar = true; +    mEnableDefaultMotions = false; +} + +// virtual +LLControlAvatar::~LLControlAvatar() +{ +} + +// virtual +void LLControlAvatar::initInstance() +{ +	// Potential optimizations here: avoid creating system +	// avatar mesh content since it's not used. For now we just clean some +	// things up after the fact in releaseMeshData(). +    LLVOAvatar::initInstance(); + +	createDrawable(&gPipeline); +	updateJointLODs(); +	updateGeometry(mDrawable); +	hideSkirt(); + +    mInitFlags |= 1<<4; +} + +void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_scale_fixup) const +{ + +    F32 max_legal_offset = MAX_LEGAL_OFFSET; +    if (gSavedSettings.getControl("AnimatedObjectsMaxLegalOffset")) +    { +        max_legal_offset = gSavedSettings.getF32("AnimatedObjectsMaxLegalOffset"); +    } +    F32 max_legal_size = MAX_LEGAL_SIZE; +    if (gSavedSettings.getControl("AnimatedObjectsMaxLegalSize")) +    { +        max_legal_size = gSavedSettings.getF32("AnimatedObjectsMaxLegalSize"); +    } +     +    new_pos_fixup = LLVector3(); +    new_scale_fixup = 1.0f; +    LLVector3 vol_pos = mRootVolp->getRenderPosition(); + +    // Fix up position if needed to prevent visual encroachment +    if (box_valid_and_non_zero(getLastAnimExtents())) // wait for state to settle down +    { +        // The goal here is to ensure that the extent of the avatar's  +        // bounding box does not wander too far from the +        // official position of the corresponding volume. We +        // do this by tracking the distance and applying a +        // correction to the control avatar position if +        // needed. +        const LLVector3 *extents = getLastAnimExtents(); +        LLVector3 box_dims = extents[1]-extents[0]; +        F32 max_size = llmax(box_dims[0],box_dims[1],box_dims[2]); +        LLVector3 pos_box_offset = point_to_box_offset(vol_pos, extents); +        F32 offset_dist = pos_box_offset.length(); +        if (offset_dist > max_legal_offset) +        { +            F32 target_dist = (offset_dist - max_legal_offset); +            new_pos_fixup = mPositionConstraintFixup + (target_dist/offset_dist)*pos_box_offset; +            LL_DEBUGS("ConstraintFix") << getFullname() << " pos fix, offset_dist " << offset_dist << " pos fixup "  +                                      << new_pos_fixup << " was " << mPositionConstraintFixup << LL_ENDL; +        } +        else if (offset_dist < max_legal_offset-1 && mPositionConstraintFixup.length()>0.01f) +        { +            new_pos_fixup = mPositionConstraintFixup * 0.9; +            LL_DEBUGS("ConstraintFix") << getFullname() << " pos fixup reduced "  +                                      << new_pos_fixup << " was " << mPositionConstraintFixup << LL_ENDL; +        } +        else +        { +            new_pos_fixup = mPositionConstraintFixup; +        } +        if (max_size/mScaleConstraintFixup > max_legal_size) +        { +            new_scale_fixup = mScaleConstraintFixup*max_legal_size/max_size; +            LL_DEBUGS("ConstraintFix") << getFullname() << " scale fix, max_size " << max_size << " fixup "  +                                      << mScaleConstraintFixup << " -> " << new_scale_fixup << LL_ENDL; +        } +    } +} + +void LLControlAvatar::matchVolumeTransform() +{ +    if (mRootVolp) +    { +        if (mRootVolp->isAttachment()) +        { +            LLVOAvatar *attached_av = mRootVolp->getAvatarAncestor(); +            if (attached_av) +            { +                LLViewerJointAttachment *attach = attached_av->getTargetAttachmentPoint(mRootVolp); +                setPositionAgent(mRootVolp->getRenderPosition()); +				attach->updateWorldPRSParent(); +                LLVector3 joint_pos = attach->getWorldPosition(); +                LLQuaternion joint_rot = attach->getWorldRotation(); +                LLVector3 obj_pos = mRootVolp->mDrawable->getPosition(); +                LLQuaternion obj_rot = mRootVolp->mDrawable->getRotation(); +                obj_pos.rotVec(joint_rot); +                mRoot->setWorldPosition(obj_pos + joint_pos); +                mRoot->setWorldRotation(obj_rot * joint_rot); +                setRotation(mRoot->getRotation()); +            } +            else +            { +                LL_WARNS_ONCE() << "can't find attached av!" << LL_ENDL; +            } +        } +        else +        { +            LLVector3 vol_pos = mRootVolp->getRenderPosition(); + +            LLVector3 new_pos_fixup; +            F32 new_scale_fixup; +            getNewConstraintFixups(new_pos_fixup, new_scale_fixup); + +            mPositionConstraintFixup = new_pos_fixup; +            mScaleConstraintFixup = new_scale_fixup; +             +            // FIXME: Currently if you're doing something like playing an +            // animation that moves the pelvis (on an avatar or +            // animated object), the name tag and debug text will be +            // left behind. Ideally setPosition() would follow the +            // skeleton around in a smarter way, so name tags, +            // complexity info and such line up better. Should defer +            // this until avatars also get fixed. + +            LLQuaternion obj_rot; +            if (mRootVolp->mDrawable) +            { +                obj_rot = mRootVolp->mDrawable->getRotation(); +            } +            else +            { +                obj_rot = mRootVolp->getRotation(); +            } +             +			LLMatrix3 bind_mat; + +            LLQuaternion bind_rot; +#define MATCH_BIND_SHAPE +#ifdef MATCH_BIND_SHAPE +            // MAINT-8671 - based on a patch from Beq Janus +	        const LLMeshSkinInfo* skin_info = mRootVolp->getSkinInfo(); +			if (skin_info) +			{ +                LL_DEBUGS("BindShape") << getFullname() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL; +                bind_rot = LLSkinningUtil::getUnscaledQuaternion(skin_info->mBindShapeMatrix); +			} +#endif +			setRotation(bind_rot*obj_rot); +            mRoot->setWorldRotation(bind_rot*obj_rot); +			setPositionAgent(vol_pos); +			mRoot->setPosition(vol_pos + mPositionConstraintFixup); + +            F32 global_scale = gSavedSettings.getF32("AnimatedObjectsGlobalScale"); +            setGlobalScale(global_scale * mScaleConstraintFixup); +        } +    } +} + +void LLControlAvatar::setGlobalScale(F32 scale) +{ +    if (scale <= 0.0) +    { +        LL_WARNS() << "invalid global scale " << scale << LL_ENDL; +        return; +    } +    if (scale != mGlobalScale) +    { +        F32 adjust_scale = scale/mGlobalScale; +        LL_INFOS() << "scale " << scale << " adjustment " << adjust_scale << LL_ENDL; +        // should we be scaling from the pelvis or the root? +        recursiveScaleJoint(mPelvisp,adjust_scale); +        mGlobalScale = scale; +    } +} + +void LLControlAvatar::recursiveScaleJoint(LLJoint* joint, F32 factor) +{ +    joint->setScale(factor * joint->getScale()); +     +	for (LLJoint::child_list_t::iterator iter = joint->mChildren.begin(); +		 iter != joint->mChildren.end(); ++iter) +	{ +		LLJoint* child = *iter; +		recursiveScaleJoint(child, factor); +	} +} + +// Based on LLViewerJointAttachment::setupDrawable(), without the attaching part. +void LLControlAvatar::updateVolumeGeom() +{ +	if (!mRootVolp->mDrawable) +		return; +	if (mRootVolp->mDrawable->isActive()) +	{ +		mRootVolp->mDrawable->makeStatic(FALSE); +	} +	mRootVolp->mDrawable->makeActive(); +	gPipeline.markMoved(mRootVolp->mDrawable); +	gPipeline.markTextured(mRootVolp->mDrawable); // face may need to change draw pool to/from POOL_HUD +	mRootVolp->mDrawable->setState(LLDrawable::USE_BACKLIGHT); + +	LLViewerObject::const_child_list_t& child_list = mRootVolp->getChildren(); +	for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); +		 iter != child_list.end(); ++iter) +	{ +		LLViewerObject* childp = *iter; +		if (childp && childp->mDrawable.notNull()) +		{ +			childp->mDrawable->setState(LLDrawable::USE_BACKLIGHT); +			gPipeline.markTextured(childp->mDrawable); // face may need to change draw pool to/from POOL_HUD +			gPipeline.markMoved(childp->mDrawable); +        } +    } + +    gPipeline.markRebuild(mRootVolp->mDrawable, LLDrawable::REBUILD_ALL, TRUE); +    mRootVolp->markForUpdate(TRUE); + +    // Note that attachment overrides aren't needed here, have already +    // been applied at the time the mControlAvatar was created, in +    // llvovolume.cpp. + +    matchVolumeTransform(); + +    // Initial exploration of allowing scaling skeleton to match root +    // prim bounding box. If enabled, would probably be controlled by +    // an additional checkbox and default to off. Not enabled for +    // initial release. + +    // What should the scale be? What we really want is the ratio +    // between the scale at which the object was originally designed +    // and rigged, and the scale to which it has been subsequently +    // modified - for example, if the object has been scaled down by a +    // factor of 2 then we should use 0.5 as the global scale. But we +    // don't have the original scale stored anywhere, just the current +    // scale. Possibilities - 1) remember the original scale +    // somewhere, 2) add another field to let the user specify the +    // global scale, 3) approximate the original scale by looking at +    // the proportions of the skeleton after joint positions have +    // been applied +     +    //LLVector3 obj_scale = obj->getScale(); +    //F32 obj_scale_z = llmax(obj_scale[2],0.1f); +    //setGlobalScale(obj_scale_z/2.0f); // roughly fit avatar height range (2m) into object height +} + +LLControlAvatar *LLControlAvatar::createControlAvatar(LLVOVolume *obj) +{ +	LLControlAvatar *cav = (LLControlAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), CO_FLAG_CONTROL_AVATAR); + +    cav->mRootVolp = obj; + +    // Sync up position/rotation with object +    cav->matchVolumeTransform(); + +    return cav; +} + +void LLControlAvatar::markForDeath() +{ +    mMarkedForDeath = true; +} + +void LLControlAvatar::idleUpdate(LLAgent &agent, const F64 &time) +{ +    if (mMarkedForDeath) +    { +        markDead(); +        mMarkedForDeath = false; +    } +    else +    { +        LLVOAvatar::idleUpdate(agent,time); +    } +} + +BOOL LLControlAvatar::updateCharacter(LLAgent &agent) +{ +    return LLVOAvatar::updateCharacter(agent); +} + +//virtual +void LLControlAvatar::updateDebugText() +{ +	if (gSavedSettings.getBOOL("DebugAnimatedObjects")) +    { +        S32 total_linkset_count = 0; +        if (mRootVolp) +        { +            total_linkset_count = 1 + mRootVolp->getChildren().size(); +        } +        std::vector<LLVOVolume*> volumes; +        getAnimatedVolumes(volumes); +        S32 animated_volume_count = volumes.size(); +        std::string active_string; +        std::string type_string; +        std::string lod_string; +        std::string animated_object_flag_string; +        S32 total_tris = 0; +        S32 total_verts = 0; +        F32 est_tris = 0.f; +        F32 est_streaming_tris = 0.f; +        F32 streaming_cost = 0.f; +        std::string cam_dist_string = ""; +        S32 cam_dist_count = 0; +        F32 lod_radius = mRootVolp->mLODRadius; + +        for (std::vector<LLVOVolume*>::iterator it = volumes.begin(); +             it != volumes.end(); ++it) +        { +            LLVOVolume *volp = *it; +            S32 verts = 0; +            total_tris += volp->getTriangleCount(&verts); +            total_verts += verts; +            est_tris += volp->getEstTrianglesMax(); +            est_streaming_tris += volp->getEstTrianglesStreamingCost(); +            streaming_cost += volp->getStreamingCost(); +            lod_string += llformat("%d",volp->getLOD()); +            if (volp && volp->mDrawable) +            { +                bool is_animated_flag = volp->getExtendedMeshFlags() & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG; +                if (is_animated_flag) +                { +                    animated_object_flag_string += "1"; +                } +                else +                { +                    animated_object_flag_string += "0"; +                } +                if (volp->mDrawable->isActive()) +                { +                    active_string += "A"; +                } +                else +                { +                    active_string += "S"; +                } +                if (volp->isRiggedMesh()) +                { +                    // Rigged/animatable mesh +                    type_string += "R"; +                    lod_radius = volp->mLODRadius; +                } +                else if (volp->isMesh()) +                { +                    // Static mesh +                    type_string += "M"; +                } +                else +                { +                    // Any other prim +                    type_string += "P"; +                } +                if (cam_dist_count < 4) +                { +                    cam_dist_string += LLStringOps::getReadableNumber(volp->mLODDistance) + "/" + +                        LLStringOps::getReadableNumber(volp->mLODAdjustedDistance) + " "; +                    cam_dist_count++; +                } +            } +            else +            { +                active_string += "-"; +                type_string += "-"; +            } +        } +        addDebugText(llformat("CAV obj %d anim %d active %s impost %d strcst %f", +                              total_linkset_count, animated_volume_count,  +                              active_string.c_str(), (S32) isImpostor(), streaming_cost)); +        addDebugText(llformat("types %s lods %s", type_string.c_str(), lod_string.c_str())); +        addDebugText(llformat("flags %s", animated_object_flag_string.c_str())); +        addDebugText(llformat("tris %d (est %.1f, streaming %.1f), verts %d", total_tris, est_tris, est_streaming_tris, total_verts)); +        addDebugText(llformat("pxarea %s rank %d", LLStringOps::getReadableNumber(getPixelArea()).c_str(), getVisibilityRank())); +        addDebugText(llformat("lod_radius %s dists %s", LLStringOps::getReadableNumber(lod_radius).c_str(),cam_dist_string.c_str())); +        if (mPositionConstraintFixup.length() > 0.0f || mScaleConstraintFixup != 1.0f) +        { +            addDebugText(llformat("pos fix (%.1f %.1f %.1f) scale %f",  +                                  mPositionConstraintFixup[0],  +                                  mPositionConstraintFixup[1], +                                  mPositionConstraintFixup[2], +                                  mScaleConstraintFixup)); +        } +         +#if 0 +        std::string region_name = "no region"; +        if (mRootVolp->getRegion()) +        { +            region_name = mRootVolp->getRegion()->getName(); +        } +        std::string skel_region_name = "skel no region"; +        if (getRegion()) +        { +            skel_region_name = getRegion()->getName(); +        } +        addDebugText(llformat("region %x %s skel %x %s", +                              mRootVolp->getRegion(), region_name.c_str(), +                              getRegion(), skel_region_name.c_str())); +#endif +         +    } +    LLVOAvatar::updateDebugText(); +} + +void LLControlAvatar::getAnimatedVolumes(std::vector<LLVOVolume*>& volumes) +{ +    if (!mRootVolp) +    { +        return; +    } + +    volumes.push_back(mRootVolp); +     +	LLViewerObject::const_child_list_t& child_list = mRootVolp->getChildren(); +	for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin(); +		 iter != child_list.end(); ++iter) +	{ +		LLViewerObject* childp = *iter; +        LLVOVolume *child_volp = dynamic_cast<LLVOVolume*>(childp); +        if (child_volp && child_volp->isAnimatedObject()) +        { +            volumes.push_back(child_volp); +        } +    } +} + +// This is called after an associated object receives an animation +// message. Combine the signaled animations for all associated objects +// and process any resulting state changes. +void LLControlAvatar::updateAnimations() +{ +    if (!mRootVolp) +    { +        LL_WARNS_ONCE("AnimatedObjectsNotify") << "No root vol" << LL_ENDL; +        return; +    } + +    std::vector<LLVOVolume*> volumes; +    getAnimatedVolumes(volumes); +     +    // Rebuild mSignaledAnimations from the associated volumes. +	std::map<LLUUID, S32> anims; +    for (std::vector<LLVOVolume*>::iterator vol_it = volumes.begin(); vol_it != volumes.end(); ++vol_it) +    { +        LLVOVolume *volp = *vol_it; +        //LL_INFOS("AnimatedObjects") << "updating anim for vol " << volp->getID() << " root " << mRootVolp->getID() << LL_ENDL; +        signaled_animation_map_t& signaled_animations = LLObjectSignaledAnimationMap::instance().getMap()[volp->getID()]; +        for (std::map<LLUUID,S32>::iterator anim_it = signaled_animations.begin(); +             anim_it != signaled_animations.end(); +             ++anim_it) +        { +            std::map<LLUUID,S32>::iterator found_anim_it = anims.find(anim_it->first); +            if (found_anim_it != anims.end()) +            { +                // Animation already present, use the larger sequence id +                anims[anim_it->first] = llmax(found_anim_it->second, anim_it->second); +            } +            else +            { +                // Animation not already present, use this sequence id. +                anims[anim_it->first] = anim_it->second; +            } +            LL_DEBUGS("AnimatedObjectsNotify") << "found anim for vol " << volp->getID() << " anim " << anim_it->first << " root " << mRootVolp->getID() << LL_ENDL; +        } +    } +    if (!mPlaying) +    { +        mPlaying = true; +        //if (!mRootVolp->isAnySelected()) +        { +            updateVolumeGeom(); +            mRootVolp->recursiveMarkForUpdate(TRUE); +        } +    } + +    mSignaledAnimations = anims; +    processAnimationStateChanges(); +} + +// virtual +LLViewerObject* LLControlAvatar::lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end, +									  S32 face, +									  BOOL pick_transparent, +									  BOOL pick_rigged, +									  S32* face_hit, +									  LLVector4a* intersection, +									  LLVector2* tex_coord, +									  LLVector4a* normal, +									  LLVector4a* tangent) +{ +	LLViewerObject* hit = NULL; + +	if (lineSegmentBoundingBox(start, end)) +	{ +		LLVector4a local_end = end; +		LLVector4a local_intersection; + +        if (mRootVolp && +            mRootVolp->lineSegmentIntersect(start, local_end, face, pick_transparent, pick_rigged, face_hit, &local_intersection, tex_coord, normal, tangent)) +        { +            local_end = local_intersection; +            if (intersection) +            { +                *intersection = local_intersection; +            } +			 +            hit = mRootVolp; +        } +	} +		 +	return hit; +} + +// virtual +std::string LLControlAvatar::getFullname() const +{ +    if (mRootVolp) +    { +        return "AO_" + mRootVolp->getID().getString(); +    } +    else +    { +        return "AO_no_root_vol"; +    } +} diff --git a/indra/newview/llcontrolavatar.h b/indra/newview/llcontrolavatar.h new file mode 100644 index 0000000000..9924697938 --- /dev/null +++ b/indra/newview/llcontrolavatar.h @@ -0,0 +1,106 @@ +/** + * @file llcontrolavatar.h + * @brief Special dummy avatar used to drive rigged meshes. + * + * $LicenseInfo:firstyear=2017&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2017, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_CONTROLAVATAR_H +#define LL_CONTROLAVATAR_H + +#include "llvoavatar.h" +#include "llvovolume.h" + +class LLControlAvatar: +    public LLVOAvatar +{ +    LOG_CLASS(LLControlAvatar); + +public: +    LLControlAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); +	virtual void 			initInstance(); // Called after construction to initialize the class. +	virtual	~LLControlAvatar(); + +    void getNewConstraintFixups(LLVector3& new_pos_constraint, F32& new_scale_constraint) const; +    void matchVolumeTransform(); +    void updateVolumeGeom(); + +    void setGlobalScale(F32 scale); +    void recursiveScaleJoint(LLJoint *joint, F32 factor); +    static LLControlAvatar *createControlAvatar(LLVOVolume *obj); + +    // Delayed kill so we don't make graphics pipeline unhappy calling +    // markDead() inside other graphics pipeline operations. +    void markForDeath(); + +    virtual void idleUpdate(LLAgent &agent, const F64 &time); +	virtual BOOL updateCharacter(LLAgent &agent); + +    void getAnimatedVolumes(std::vector<LLVOVolume*>& volumes); +    void updateAnimations();   +     +	virtual LLViewerObject*	lineSegmentIntersectRiggedAttachments( +        const LLVector4a& start, const LLVector4a& end, +        S32 face = -1,                    // which face to check, -1 = ALL_SIDES +        BOOL pick_transparent = FALSE, +        BOOL pick_rigged = FALSE, +        S32* face_hit = NULL,             // which face was hit +        LLVector4a* intersection = NULL,   // return the intersection point +        LLVector2* tex_coord = NULL,      // return the texture coordinates of the intersection point +        LLVector4a* normal = NULL,         // return the surface normal at the intersection point +        LLVector4a* tangent = NULL);     // return the surface tangent at the intersection point + +	virtual void	updateDebugText(); + +    virtual std::string getFullname() const; + +    bool mPlaying; + +    F32 mGlobalScale; + +    LLVOVolume *mRootVolp; + +    bool mMarkedForDeath; + +    LLVector3 mPositionConstraintFixup; +    F32 mScaleConstraintFixup; + +    static const F32 MAX_LEGAL_OFFSET; +    static const F32 MAX_LEGAL_SIZE; +     +}; + +typedef std::map<LLUUID, S32> signaled_animation_map_t; +typedef std::map<LLUUID, signaled_animation_map_t> object_signaled_animation_map_t; + +// Stores information about previously requested animations, by object id. +class LLObjectSignaledAnimationMap: public LLSingleton<LLObjectSignaledAnimationMap> +{ +    LLSINGLETON_EMPTY_CTOR(LLObjectSignaledAnimationMap);  + +public: +    object_signaled_animation_map_t mMap; + +    object_signaled_animation_map_t& getMap() { return mMap; } +}; + +#endif //LL_CONTROLAVATAR_H diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index 6ca8f1ae9c..8c6cbc020b 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -32,6 +32,7 @@  #include "material_codes.h"  // viewer includes +#include "llagent.h"  #include "llcriticaldamp.h"  #include "llface.h"  #include "lllightconstants.h" @@ -50,6 +51,7 @@  #include "llviewerobjectlist.h"  #include "llviewerwindow.h"  #include "llvocache.h" +#include "llcontrolavatar.h"  #include "lldrawpoolavatar.h"  const F32 MIN_INTERPOLATE_DISTANCE_SQUARED = 0.001f * 0.001f; @@ -573,7 +575,8 @@ void LLDrawable::makeStatic(BOOL warning_enabled)  	if (isState(ACTIVE) &&   		!isState(ACTIVE_CHILD) &&   		!mVObjp->isAttachment() &&  -		!mVObjp->isFlexible()) +		!mVObjp->isFlexible() && +        !mVObjp->isAnimatedObject())  	{  		clearState(ACTIVE | ANIMATED_CHILD); @@ -726,6 +729,10 @@ F32 LLDrawable::updateXform(BOOL undamped)  	mXform.setRotation(target_rot);  	mXform.setScale(LLVector3(1,1,1)); //no scale in drawable transforms (IT'S A RULE!)  	mXform.updateMatrix(); +    if (isRoot() && mVObjp->isAnimatedObject() && mVObjp->getControlAvatar()) +    { +        mVObjp->getControlAvatar()->matchVolumeTransform(); +    }  	if (mSpatialBridge)  	{ @@ -897,6 +904,30 @@ void LLDrawable::updateDistance(LLCamera& camera, bool force_update)  					}  				}  			}	 + + +            // MAINT-7926 Handle volumes in an animated object as a special case +            // SL-937: add dynamic box handling for rigged mesh on regular avatars. +            //if (volume->getAvatar() && volume->getAvatar()->isControlAvatar()) +            if (volume->getAvatar()) +            { +                const LLVector3* av_box = volume->getAvatar()->getLastAnimExtents(); +                LLVector3d cam_pos = gAgent.getPosGlobalFromAgent(LLViewerCamera::getInstance()->getOrigin()); +                LLVector3 cam_region_pos = LLVector3(cam_pos - volume->getRegion()->getOriginGlobal()); +                 +                LLVector3 cam_to_box_offset = point_to_box_offset(cam_region_pos, av_box); +                mDistanceWRTCamera = llmax(0.01f, ll_round(cam_to_box_offset.magVec(), 0.01f)); +                LL_DEBUGS("DynamicBox") << volume->getAvatar()->getFullname()  +                                        << " pos (ignored) " << pos +                                        << " cam pos " << cam_pos +                                        << " cam region pos " << cam_region_pos +                                        << " box " << av_box[0] << "," << av_box[1]  +                                        << " -> dist " << mDistanceWRTCamera +                                        << LL_ENDL; +                mVObjp->updateLOD(); +                return; +            } +              		}  		else  		{ @@ -1113,7 +1144,8 @@ void LLDrawable::setGroup(LLViewerOctreeGroup *groupp)  	llassert(!groupp || (LLSpatialGroup*)groupp->hasElement(this));  	if (cur_groupp != groupp && getVOVolume()) -	{ //NULL out vertex buffer references for volumes on spatial group change to maintain +	{ +		//NULL out vertex buffer references for volumes on spatial group change to maintain  		//requirement that every face vertex buffer is either NULL or points to a vertex buffer  		//contained by its drawable's spatial group  		for (S32 i = 0; i < getNumFaces(); ++i) diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index 075375082d..82888b2df6 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -141,7 +141,7 @@ LLViewerTexture *LLDrawPool::getDebugTexture()  	return NULL;  } -//virtual +//virtuals  void LLDrawPool::beginRenderPass( S32 pass )  {  } diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index ef69990170..905428b090 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -109,6 +109,32 @@ LLDrawPoolAvatar::LLDrawPoolAvatar() :  {  } +LLDrawPoolAvatar::~LLDrawPoolAvatar() +{ +    if (!isDead()) +    { +        LL_WARNS() << "Destroying avatar drawpool that still contains faces" << LL_ENDL; +    } +} + +// virtual +BOOL LLDrawPoolAvatar::isDead() +{ +    if (!LLFacePool::isDead()) +    { +        return FALSE; +    } +     +	for (U32 i = 0; i < NUM_RIGGED_PASSES; ++i) +    { +        if (mRiggedFace[i].size() > 0) +        { +            return FALSE; +        } +    } +    return TRUE; +} +   //-----------------------------------------------------------------------------  // instancePool()  //----------------------------------------------------------------------------- @@ -467,7 +493,7 @@ void LLDrawPoolAvatar::renderShadow(S32 pass)  	}  	LLVOAvatar *avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get(); -	if (avatarp->isDead() || avatarp->mIsDummy || avatarp->mDrawable.isNull()) +	if (avatarp->isDead() || avatarp->isUIAvatar() || avatarp->mDrawable.isNull())  	{  		return;  	} @@ -1714,13 +1740,7 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)  			continue;  		} -		LLUUID mesh_id = volume->getParams().getSculptID(); -		if (mesh_id.isNull()) -		{ -			continue; -		} - -		const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(mesh_id, vobj); +		const LLMeshSkinInfo* skin = vobj->getSkinInfo();  		if (!skin)  		{  			continue; @@ -1934,13 +1954,7 @@ void LLDrawPoolAvatar::updateRiggedVertexBuffers(LLVOAvatar* avatar)  				continue;  			} -			LLUUID mesh_id = volume->getParams().getSculptID(); -			if (mesh_id.isNull()) -			{ -				continue; -			} - -			const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(mesh_id, vobj); +			const LLMeshSkinInfo* skin = vobj->getSkinInfo();  			if (!skin)  			{  				continue; @@ -2061,11 +2075,16 @@ LLColor3 LLDrawPoolAvatar::getDebugColor() const  void LLDrawPoolAvatar::addRiggedFace(LLFace* facep, U32 type)  { +    llassert (facep->isState(LLFace::RIGGED)); +    llassert(getType() == LLDrawPool::POOL_AVATAR); +    if (facep->getPool() && facep->getPool() != this) +    { +        LL_ERRS() << "adding rigged face that's already in another pool" << LL_ENDL; +    }  	if (type >= NUM_RIGGED_PASSES)  	{  		LL_ERRS() << "Invalid rigged face type." << LL_ENDL;  	} -  	if (facep->getRiggedIndex(type) != -1)  	{  		LL_ERRS() << "Tried to add a rigged face that's referenced elsewhere." << LL_ENDL; @@ -2078,6 +2097,12 @@ void LLDrawPoolAvatar::addRiggedFace(LLFace* facep, U32 type)  void LLDrawPoolAvatar::removeRiggedFace(LLFace* facep)  { +    llassert (facep->isState(LLFace::RIGGED)); +    llassert(getType() == LLDrawPool::POOL_AVATAR); +    if (facep->getPool() != this) +    { +        LL_ERRS() << "Tried to remove a rigged face from the wrong pool" << LL_ENDL; +    }  	facep->setPool(NULL);  	for (U32 i = 0; i < NUM_RIGGED_PASSES; ++i) diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h index b9d2204052..45b6d71110 100644 --- a/indra/newview/lldrawpoolavatar.h +++ b/indra/newview/lldrawpoolavatar.h @@ -60,6 +60,8 @@ public:  	virtual S32 getVertexShaderLevel() const;  	LLDrawPoolAvatar(); +    ~LLDrawPoolAvatar(); +    /*virtual*/ BOOL isDead();  	static LLMatrix4& getModelView(); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 06f64b7597..4f18cda92f 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -331,11 +331,7 @@ void LLFace::dirtyTexture()  				{  					vobj->mLODChanged = TRUE; -					LLVOAvatar* avatar = vobj->getAvatar(); -					if (avatar) -					{ //avatar render cost may have changed -						avatar->updateVisualComplexity(); -					} +                    vobj->updateVisualComplexity();  				}  				gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_VOLUME, FALSE);  			} @@ -815,17 +811,11 @@ bool less_than_max_mag(const LLVector4a& vec)  }  BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f, -								const LLMatrix4& mat_vert_in, BOOL global_volume) +                             const LLMatrix4& mat_vert_in, BOOL global_volume)  {  	//get bounding box  	if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED))  	{ -		//VECTORIZE THIS -		LLMatrix4a mat_vert; -		mat_vert.loadu(mat_vert_in); - -		LLVector4a min,max; -	  		if (f >= volume.getNumVolumeFaces())  		{  			LL_WARNS() << "Generating bounding box for invalid face index!" << LL_ENDL; @@ -833,77 +823,52 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,  		}  		const LLVolumeFace &face = volume.getVolumeFace(f); -		min = face.mExtents[0]; -		max = face.mExtents[1]; -		llassert(less_than_max_mag(min)); -		llassert(less_than_max_mag(max)); - -		//min, max are in volume space, convert to drawable render space - -		//get 8 corners of bounding box -		LLVector4Logical mask[6]; - -		for (U32 i = 0; i < 6; ++i) -		{ -			mask[i].clear(); -		} - -		mask[0].setElement<2>(); //001 -		mask[1].setElement<1>(); //010 -		mask[2].setElement<1>(); //011 -		mask[2].setElement<2>(); -		mask[3].setElement<0>(); //100 -		mask[4].setElement<0>(); //101 -		mask[4].setElement<2>(); -		mask[5].setElement<0>(); //110 -		mask[5].setElement<1>(); - -		LLVector4a v[8]; - -		v[6] = min; -		v[7] = max; - -		for (U32 i = 0; i < 6; ++i) -		{ -			v[i].setSelectWithMask(mask[i], min, max); -		} - -		LLVector4a tv[8]; +        LL_DEBUGS("RiggedBox") << "updating extents for face " << f  +                               << " starting extents " << mExtents[0] << ", " << mExtents[1]  +                               << " starting vf extents " << face.mExtents[0] << ", " << face.mExtents[1]  +                               << " num verts " << face.mNumVertices << LL_ENDL; + +        // MAINT-8264 - stray vertices, especially in low LODs, cause bounding box errors. +		if (face.mNumVertices < 3)  +        { +            LL_DEBUGS("RiggedBox") << "skipping face " << f << ", bad num vertices "  +                                   << face.mNumVertices << " " << face.mNumIndices << " " << face.mWeights << LL_ENDL; +            return FALSE; +        } +         +		//VECTORIZE THIS +		LLMatrix4a mat_vert; +		mat_vert.loadu(mat_vert_in); +        LLVector4a new_extents[2]; -		//transform bounding box into drawable space -		for (U32 i = 0; i < 8; ++i) -		{ -			mat_vert.affineTransform(v[i], tv[i]); -		} -	 -		//find bounding box -		LLVector4a& newMin = mExtents[0]; -		LLVector4a& newMax = mExtents[1]; +		llassert(less_than_max_mag(face.mExtents[0])); +		llassert(less_than_max_mag(face.mExtents[1])); -		newMin = newMax = tv[0]; +		matMulBoundBox(mat_vert, face.mExtents, mExtents); -		for (U32 i = 1; i < 8; ++i) -		{ -			newMin.setMin(newMin, tv[i]); -			newMax.setMax(newMax, tv[i]); -		} +        LL_DEBUGS("RiggedBox") << "updated extents for face " << f  +                               << " bbox gave extents " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;  		if (!mDrawablep->isActive())  		{	// Shift position for region  			LLVector4a offset;  			offset.load3(mDrawablep->getRegion()->getOriginAgent().mV); -			newMin.add(offset); -			newMax.add(offset); +			mExtents[0].add(offset); +			mExtents[1].add(offset); +            LL_DEBUGS("RiggedBox") << "updating extents for face " << f  +                                   << " not active, added offset " << offset << LL_ENDL;  		} +        LL_DEBUGS("RiggedBox") << "updated extents for face " << f  +                               << " to " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;  		LLVector4a t; -		t.setAdd(newMin,newMax); +		t.setAdd(mExtents[0],mExtents[1]);  		t.mul(0.5f);  		mCenterLocal.set(t.getF32ptr()); -		t.setSub(newMax,newMin); +		t.setSub(mExtents[1],mExtents[0]);  		mBoundingSphereRadius = t.getLength3().getF32()*0.5f;  		updateCenterAgent(); diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index ae7620f2c7..080d0ed8ea 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -1042,14 +1042,8 @@ LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLViewerDynamicT  	mCameraPitch = 0.f;  	mCameraZoom = 1.f; -	mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion()); -	mDummyAvatar->createDrawable(&gPipeline); -	mDummyAvatar->mIsDummy = TRUE; +	mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR);  	mDummyAvatar->mSpecialRenderMode = 1; -	mDummyAvatar->setPositionAgent(LLVector3::zero); -	mDummyAvatar->slamPosition(); -	mDummyAvatar->updateJointLODs(); -	mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);  	mDummyAvatar->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET);  	mDummyAvatar->hideSkirt(); diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index 0c2bb25e07..3c428a70f3 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -566,15 +566,8 @@ LLImagePreviewAvatar::LLImagePreviewAvatar(S32 width, S32 height) : LLViewerDyna  	mCameraPitch = 0.f;  	mCameraZoom = 1.f; -	mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion()); -	mDummyAvatar->createDrawable(&gPipeline); -	mDummyAvatar->mIsDummy = TRUE; +	mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR);  	mDummyAvatar->mSpecialRenderMode = 2; -	mDummyAvatar->setPositionAgent(LLVector3::zero); -	mDummyAvatar->slamPosition(); -	mDummyAvatar->updateJointLODs(); -	mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable); -	// gPipeline.markVisible(mDummyAvatar->mDrawable, *LLViewerCamera::getInstance());  	mTextureName = 0;  } diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 7a2ab37a0d..268c646719 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -685,6 +685,11 @@ void LLFloaterModelPreview::draw()  			childSetTextArg("status", "[STATUS]", getString("status_parse_error"));  			toggleCalculateButton(false);  		} +        else +        if (mModelPreview->getLoadState() == LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION) +        { +			childSetTextArg("status", "[STATUS]", getString("status_bind_shape_orientation")); +        }  		else  		{  			childSetTextArg("status", "[STATUS]", getString("status_idle")); @@ -1368,7 +1373,11 @@ U32 LLModelPreview::calcResourceCost()  			F32 radius = scale.length()*0.5f*debug_scale; -			streaming_cost += LLMeshRepository::getStreamingCost(ret, radius); +            LLMeshCostData costs; +            if (gMeshRepo.getCostData(ret, costs)) +            { +                streaming_cost += costs.getRadiusBasedStreamingCost(radius); +            }  		}  	} @@ -1618,6 +1627,19 @@ void LLModelPreview::rebuildUploadData()  						mFMP->childDisable( "calculate_btn" );  					}  				} +                LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) mFMP; +                bool upload_skinweights = fmp && fmp->childGetValue("upload_skin").asBoolean(); +                if (upload_skinweights && high_lod_model->mSkinInfo.mJointNames.size() > 0) +                { +                    LLQuaternion bind_rot = LLSkinningUtil::getUnscaledQuaternion(high_lod_model->mSkinInfo.mBindShapeMatrix); +                    LLQuaternion identity; +                    if (!bind_rot.isEqualEps(identity,0.01)) +                    { +                        LL_WARNS() << "non-identity bind shape rot. mat is " << high_lod_model->mSkinInfo.mBindShapeMatrix  +                                   << " bind_rot " << bind_rot << LL_ENDL; +                        setLoadState( LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION ); +                    } +                }  			}  			instance.mTransform = mat;  			mUploadData.push_back(instance); @@ -1763,9 +1785,17 @@ void LLModelPreview::getJointAliases( JointMap& joint_map)      //Joint names and aliases come from avatar_skeleton.xml      joint_map = av->getJointAliases(); -    for (S32 i = 0; i < av->mNumCollisionVolumes; i++) + +    std::vector<std::string> cv_names, attach_names; +    av->getSortedJointNames(1, cv_names); +    av->getSortedJointNames(2, attach_names); +    for (std::vector<std::string>::iterator it = cv_names.begin(); it != cv_names.end(); ++it) +    { +        joint_map[*it] = *it; +    } +    for (std::vector<std::string>::iterator it = attach_names.begin(); it != attach_names.end(); ++it)      { -        joint_map[av->mCollisionVolumes[i].getName()] = av->mCollisionVolumes[i].getName(); +        joint_map[*it] = *it;      }  } @@ -3451,16 +3481,11 @@ void LLModelPreview::update()  //-----------------------------------------------------------------------------  void LLModelPreview::createPreviewAvatar( void )  { -	mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer( LL_PCODE_LEGACY_AVATAR, gAgent.getRegion() ); +	mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer( LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR );  	if ( mPreviewAvatar )  	{  		mPreviewAvatar->createDrawable( &gPipeline ); -		mPreviewAvatar->mIsDummy = TRUE;  		mPreviewAvatar->mSpecialRenderMode = 1; -		mPreviewAvatar->setPositionAgent( LLVector3::zero ); -		mPreviewAvatar->slamPosition(); -		mPreviewAvatar->updateJointLODs(); -		mPreviewAvatar->updateGeometry( mPreviewAvatar->mDrawable );  		mPreviewAvatar->startMotion( ANIM_AGENT_STAND );  		mPreviewAvatar->hideSkirt();  	} diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 53b03c6069..7ec6a58ac7 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -169,7 +169,10 @@ protected:  	void			draw();  	void initDecompControls(); -	 + +    // FIXME - this function and mStatusMessage have no visible effect, and the +    // actual status messages are managed by directly manipulation of +    // the UI element.  	void setStatusMessage(const std::string& msg);  	LLModelPreview*	mModelPreview; diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 2c1980922a..e38bd8846d 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -4214,22 +4214,80 @@ void LLMeshRepository::uploadError(LLSD& args)  	mUploadErrorQ.push(args);  } -F32 LLMeshRepository::getStreamingCost(LLUUID mesh_id, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value) +F32 LLMeshRepository::getEstTrianglesMax(LLUUID mesh_id)  { +    LLMeshCostData costs; +    if (getCostData(mesh_id, costs)) +    { +        return costs.getEstTrisMax(); +    } +    else +    { +        return 0.f; +    } +} + +F32 LLMeshRepository::getEstTrianglesStreamingCost(LLUUID mesh_id) +{ +    LLMeshCostData costs; +    if (getCostData(mesh_id, costs)) +    { +        return costs.getEstTrisForStreamingCost(); +    } +    else +    { +        return 0.f; +    } +} + +// FIXME replace with calc based on LLMeshCostData +F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value) +{ +	F32 result = 0.f;      if (mThread && mesh_id.notNull())      {          LLMutexLock lock(mThread->mHeaderMutex);          LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);          if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0)          { -            return getStreamingCost(iter->second, radius, bytes, bytes_visible, lod, unscaled_value); +            result  = getStreamingCostLegacy(iter->second, radius, bytes, bytes_visible, lod, unscaled_value); +        } +    } +    if (result > 0.f) +    { +        LLMeshCostData data; +        if (getCostData(mesh_id, data)) +        { +            F32 ref_streaming_cost = data.getRadiusBasedStreamingCost(radius); +            F32 ref_weighted_tris = data.getRadiusWeightedTris(radius); +            if (!is_approx_equal(ref_streaming_cost,result)) +            { +                LL_WARNS() << mesh_id << "streaming mismatch " << result << " " << ref_streaming_cost << LL_ENDL; +            } +            if (unscaled_value && !is_approx_equal(ref_weighted_tris,*unscaled_value)) +            { +                LL_WARNS() << mesh_id << "weighted_tris mismatch " << *unscaled_value << " " << ref_weighted_tris << LL_ENDL; +            } +            if (bytes && (*bytes != data.getSizeTotal())) +            { +                LL_WARNS() << mesh_id << "bytes mismatch " << *bytes << " " << data.getSizeTotal() << LL_ENDL; +            } +            if (bytes_visible && (lod >=0) && (lod < 4) && (*bytes_visible != data.getSizeByLOD(lod))) +            { +                LL_WARNS() << mesh_id << "bytes_visible mismatch " << *bytes_visible << " " << data.getSizeByLOD(lod) << LL_ENDL; +            } +        } +        else +        { +            LL_WARNS() << "getCostData failed!!!" << LL_ENDL;          }      } -    return 0.f; +    return result;  } +// FIXME replace with calc based on LLMeshCostData  //static -F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value) +F32 LLMeshRepository::getStreamingCostLegacy(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value)  {  	if (header.has("404")  		|| !header.has("lowest_lod") @@ -4323,7 +4381,7 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32  	F32 weighted_avg = triangles_high*high_area +  					   triangles_mid*mid_area +  					   triangles_low*low_area + -					  triangles_lowest*lowest_area; +					   triangles_lowest*lowest_area;  	if (unscaled_value)  	{ @@ -4333,6 +4391,204 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32  	return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;  } +LLMeshCostData::LLMeshCostData() +{ +    mSizeByLOD.resize(4); +    mEstTrisByLOD.resize(4); + +    std::fill(mSizeByLOD.begin(), mSizeByLOD.end(), 0); +    std::fill(mEstTrisByLOD.begin(), mEstTrisByLOD.end(), 0.f); +} + +bool LLMeshCostData::init(const LLSD& header) +{ +    mSizeByLOD.resize(4); +    mEstTrisByLOD.resize(4); + +    std::fill(mSizeByLOD.begin(), mSizeByLOD.end(), 0); +    std::fill(mEstTrisByLOD.begin(), mEstTrisByLOD.end(), 0.f); +     +    S32 bytes_high = header["high_lod"]["size"].asInteger(); +    S32 bytes_med = header["medium_lod"]["size"].asInteger(); +    if (bytes_med == 0) +    { +        bytes_med = bytes_high; +    } +    S32 bytes_low = header["low_lod"]["size"].asInteger(); +    if (bytes_low == 0) +    { +        bytes_low = bytes_med; +    } +    S32 bytes_lowest = header["lowest_lod"]["size"].asInteger(); +    if (bytes_lowest == 0) +    { +        bytes_lowest = bytes_low; +    } +    mSizeByLOD[0] = bytes_lowest; +    mSizeByLOD[1] = bytes_low; +    mSizeByLOD[2] = bytes_med; +    mSizeByLOD[3] = bytes_high; + +    F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount");  //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead +    F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize"); //make sure nothing is "free" +    F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle"); + +    for (S32 i=0; i<4; i++) +    { +        mEstTrisByLOD[i] = llmax((F32) mSizeByLOD[i]-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;  +    } + +    return true; +} + + +S32 LLMeshCostData::getSizeByLOD(S32 lod) +{ +    if (llclamp(lod,0,3) != lod) +    { +        return 0; +    } +    return mSizeByLOD[lod]; +} + +S32 LLMeshCostData::getSizeTotal() +{ +    return mSizeByLOD[0] + mSizeByLOD[1] + mSizeByLOD[2] + mSizeByLOD[3]; +} + +F32 LLMeshCostData::getEstTrisByLOD(S32 lod) +{ +    if (llclamp(lod,0,3) != lod) +    { +        return 0.f; +    } +    return mEstTrisByLOD[lod]; +} + +F32 LLMeshCostData::getEstTrisMax() +{ +    return llmax(mEstTrisByLOD[0], mEstTrisByLOD[1], mEstTrisByLOD[2], mEstTrisByLOD[3]); +} + +F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) +{ +	F32 max_distance = 512.f; + +	F32 dlowest = llmin(radius/0.03f, max_distance); +	F32 dlow = llmin(radius/0.06f, max_distance); +	F32 dmid = llmin(radius/0.24f, max_distance); +	 +	F32 triangles_lowest = mEstTrisByLOD[0]; +	F32 triangles_low = mEstTrisByLOD[1]; +	F32 triangles_mid = mEstTrisByLOD[2]; +	F32 triangles_high = mEstTrisByLOD[3]; + +	F32 max_area = 102944.f; //area of circle that encompasses region (see MAINT-6559) +	F32 min_area = 1.f; + +	F32 high_area = llmin(F_PI*dmid*dmid, max_area); +	F32 mid_area = llmin(F_PI*dlow*dlow, max_area); +	F32 low_area = llmin(F_PI*dlowest*dlowest, max_area); +	F32 lowest_area = max_area; + +	lowest_area -= low_area; +	low_area -= mid_area; +	mid_area -= high_area; + +	high_area = llclamp(high_area, min_area, max_area); +	mid_area = llclamp(mid_area, min_area, max_area); +	low_area = llclamp(low_area, min_area, max_area); +	lowest_area = llclamp(lowest_area, min_area, max_area); + +	F32 total_area = high_area + mid_area + low_area + lowest_area; +	high_area /= total_area; +	mid_area /= total_area; +	low_area /= total_area; +	lowest_area /= total_area; + +	F32 weighted_avg = triangles_high*high_area + +					   triangles_mid*mid_area + +					   triangles_low*low_area + +					   triangles_lowest*lowest_area; + +    return weighted_avg; +} + +F32 LLMeshCostData::getEstTrisForStreamingCost() +{ +    LL_DEBUGS("StreamingCost") << "tris_by_lod: " +                               << mEstTrisByLOD[0] << ", " +                               << mEstTrisByLOD[1] << ", " +                               << mEstTrisByLOD[2] << ", " +                               << mEstTrisByLOD[3] << LL_ENDL; + +    F32 charged_tris = mEstTrisByLOD[3]; +    F32 allowed_tris = mEstTrisByLOD[3]; +    const F32 ENFORCE_FLOOR = 64.0f; +    for (S32 i=2; i>=0; i--) +    { +        // How many tris can we have in this LOD without affecting land impact? +        // - normally an LOD should be at most half the size of the previous one. +        // - once we reach a floor of ENFORCE_FLOOR, don't require LODs to get any smaller. +        allowed_tris = llclamp(allowed_tris/2.0f,ENFORCE_FLOOR,mEstTrisByLOD[i]); +        F32 excess_tris = mEstTrisByLOD[i]-allowed_tris; +        if (excess_tris>0.f) +        { +            LL_DEBUGS("StreamingCost") << "excess tris in lod[" << i << "] " << excess_tris << " allowed " << allowed_tris <<  LL_ENDL; +            charged_tris += excess_tris; +        } +    } +    return charged_tris; +} + +F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius) +{ +	return getRadiusWeightedTris(radius)/gSavedSettings.getU32("MeshTriangleBudget")*15000.f; +} + +F32 LLMeshCostData::getTriangleBasedStreamingCost() +{ +    F32 result = ANIMATED_OBJECT_COST_PER_KTRI * 0.001 * getEstTrisForStreamingCost(); +    return result; +} + +bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data) +{ +    data = LLMeshCostData(); +     +    if (mThread && mesh_id.notNull()) +    { +        LLMutexLock lock(mThread->mHeaderMutex); +        LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); +        if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0) +        { +            LLSD& header = iter->second; + +            bool header_invalid = (header.has("404") +                                   || !header.has("lowest_lod") +                                   || (header.has("version") && header["version"].asInteger() > MAX_MESH_VERSION)); +            if (!header_invalid) +            { +                return getCostData(header, data); +            } + +            return true; +        } +    } +    return false; +} + +bool LLMeshRepository::getCostData(LLSD& header, LLMeshCostData& data) +{ +    data = LLMeshCostData(); + +    if (!data.init(header)) +    { +        return false; +    } +     +    return true; +}  LLPhysicsDecomp::LLPhysicsDecomp()  : LLThread("Physics Decomp") diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 22215c784a..19d4ff617e 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -490,6 +490,60 @@ private:  	LLCore::HttpRequest::priority_t		mHttpPriority;  }; +// Params related to streaming cost, render cost, and scene complexity tracking. +class LLMeshCostData +{ +public: +    LLMeshCostData(); + +    bool init(const LLSD& header); +     +    // Size for given LOD +    S32 getSizeByLOD(S32 lod); + +    // Sum of all LOD sizes. +    S32 getSizeTotal(); + +    // Estimated triangle counts for the given LOD. +    F32 getEstTrisByLOD(S32 lod); +     +    // Estimated triangle counts for the largest LOD. Typically this +    // is also the "high" LOD, but not necessarily. +    F32 getEstTrisMax(); + +    // Triangle count as computed by original streaming cost +    // formula. Triangles in each LOD are weighted based on how +    // frequently they will be seen. +    // This was called "unscaled_value" in the original getStreamingCost() functions. +    F32 getRadiusWeightedTris(F32 radius); + +    // Triangle count used by triangle-based cost formula. Based on +    // triangles in highest LOD plus potentially partial charges for +    // lower LODs depending on complexity. +    F32 getEstTrisForStreamingCost(); + +    // Streaming cost. This should match the server-side calculation +    // for the corresponding volume. +    F32 getRadiusBasedStreamingCost(F32 radius); + +    // New streaming cost formula, currently only used for animated objects. +    F32 getTriangleBasedStreamingCost(); + +private: +    // From the "size" field of the mesh header. LOD 0=lowest, 3=highest. +    std::vector<S32> mSizeByLOD; + +    // Estimated triangle counts derived from the LOD sizes. LOD 0=lowest, 3=highest. +    std::vector<F32> mEstTrisByLOD; + +    // Estimated triangle counts for the largest LOD. Typically this +    // is also the "high" LOD, but not necessarily. +    F32 mEstTrisMax; + +    // Sum of all LOD sizes. +    S32 mSizeTotal; +}; +  class LLMeshRepository  {  public: @@ -511,8 +565,13 @@ public:  	static LLDeadmanTimer sQuiescentTimer;		// Time-to-complete-mesh-downloads after significant events -	F32 getStreamingCost(LLUUID mesh_id, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); -	static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); +    // Estimated triangle count of the largest LOD +    F32 getEstTrianglesMax(LLUUID mesh_id); +    F32 getEstTrianglesStreamingCost(LLUUID mesh_id); +	F32 getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); +	static F32 getStreamingCostLegacy(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); +    bool getCostData(LLUUID mesh_id, LLMeshCostData& data); +    bool getCostData(LLSD& header, LLMeshCostData& data);  	LLMeshRepository(); @@ -623,5 +682,8 @@ public:  extern LLMeshRepository gMeshRepo; +const F32 ANIMATED_OBJECT_BASE_COST = 15.0f; +const F32 ANIMATED_OBJECT_COST_PER_KTRI = 1.5f; +  #endif diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index 1c4384ff08..96dd309fa4 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -78,6 +78,8 @@  #include "llviewercontrol.h"  #include "llmeshrepository.h" +#include "llvoavatarself.h" +  #include <boost/bind.hpp>  // "Features" Tab @@ -86,6 +88,7 @@ BOOL	LLPanelVolume::postBuild()  {  	// Flexible Objects Parameters  	{ +		childSetCommitCallback("Animated Mesh Checkbox Ctrl", boost::bind(&LLPanelVolume::onCommitAnimatedMeshCheckbox, this, _1, _2), NULL);  		childSetCommitCallback("Flexible1D Checkbox Ctrl", boost::bind(&LLPanelVolume::onCommitIsFlexible, this, _1, _2), NULL);  		childSetCommitCallback("FlexNumSections",onCommitFlexible,this);  		getChild<LLUICtrl>("FlexNumSections")->setValidateBeforeCommit(precommitValidate); @@ -237,6 +240,11 @@ void LLPanelVolume::getState( )  	{  		volobjp = (LLVOVolume *)objectp;  	} +	LLVOVolume *root_volobjp = NULL; +    if (root_objectp && (root_objectp->getPCode() == LL_PCODE_VOLUME)) +    { +        root_volobjp  = (LLVOVolume *)root_objectp; +    }  	if( !objectp )  	{ @@ -260,6 +268,8 @@ void LLPanelVolume::getState( )  	BOOL editable = root_objectp->permModify() && !root_objectp->isPermanentEnforced();  	BOOL single_volume = LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME )  		&& LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1; +    BOOL single_root_volume = LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME ) &&  +        LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() == 1;  	// Select Single Message  	if (single_volume) @@ -345,7 +355,35 @@ void LLPanelVolume::getState( )  		getChildView("Light Focus")->setEnabled(false);  		getChildView("Light Ambiance")->setEnabled(false);  	} -	 + +    // Animated Mesh +	BOOL is_animated_mesh = single_root_volume && root_volobjp && root_volobjp->isAnimatedObject(); +	getChild<LLUICtrl>("Animated Mesh Checkbox Ctrl")->setValue(is_animated_mesh); +    BOOL enabled_animated_object_box = FALSE; +    if (root_volobjp && root_volobjp == volobjp) +    { +        enabled_animated_object_box = single_root_volume && root_volobjp && root_volobjp->canBeAnimatedObject() && editable;  +#if 0 +        if (!enabled_animated_object_box) +        { +            LL_INFOS() << "not enabled: srv " << single_root_volume << " root_volobjp " << (bool) root_volobjp << LL_ENDL; +            if (root_volobjp) +            { +                LL_INFOS() << " cba " << root_volobjp->canBeAnimatedObject() +                           << " editable " << editable << " permModify() " << root_volobjp->permModify() +                           << " ispermenf " << root_volobjp->isPermanentEnforced() << LL_ENDL; +            } +        } +#endif +        if (enabled_animated_object_box && !is_animated_mesh &&  +            root_volobjp->isAttachment() && !gAgentAvatarp->canAttachMoreAnimatedObjects()) +        { +            // Turning this attachment animated would cause us to exceed the limit. +            enabled_animated_object_box = false; +        } +    } +    getChildView("Animated Mesh Checkbox Ctrl")->setEnabled(enabled_animated_object_box); +  	// Flexible properties  	BOOL is_flexible = volobjp && volobjp->isFlexible();  	getChild<LLUICtrl>("Flexible1D Checkbox Ctrl")->setValue(is_flexible); @@ -582,6 +620,7 @@ void LLPanelVolume::clearCtrls()  	getChildView("Light Radius")->setEnabled(false);  	getChildView("Light Falloff")->setEnabled(false); +    getChildView("Animated Mesh Checkbox Ctrl")->setEnabled(false);  	getChildView("Flexible1D Checkbox Ctrl")->setEnabled(false);  	getChildView("FlexNumSections")->setEnabled(false);  	getChildView("FlexGravity")->setEnabled(false); @@ -891,6 +930,31 @@ void LLPanelVolume::onCommitFlexible( LLUICtrl* ctrl, void* userdata )  	self->refresh();  } +void LLPanelVolume::onCommitAnimatedMeshCheckbox(LLUICtrl *, void*) +{ +	LLViewerObject* objectp = mObject; +	if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME)) +	{ +		return; +    } +	LLVOVolume *volobjp = (LLVOVolume *)objectp; +	BOOL animated_mesh = getChild<LLUICtrl>("Animated Mesh Checkbox Ctrl")->getValue(); +    U32 flags = volobjp->getExtendedMeshFlags(); +    U32 new_flags = flags; +    if (animated_mesh) +    { +        new_flags |= LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG; +    } +    else +    { +        new_flags &= ~LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG; +    } +    if (new_flags != flags) +    { +        volobjp->setExtendedMeshFlags(new_flags); +    } +} +  void LLPanelVolume::onCommitIsFlexible(LLUICtrl *, void*)  {  	if (mObject->flagObjectPermanent()) diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h index e3453ae99c..66117316cf 100644 --- a/indra/newview/llpanelvolume.h +++ b/indra/newview/llpanelvolume.h @@ -63,6 +63,7 @@ public:  	static void 	onCommitLight(			LLUICtrl* ctrl, void* userdata);  	void 			onCommitIsFlexible(		LLUICtrl* ctrl, void* userdata);  	static void 	onCommitFlexible(		LLUICtrl* ctrl, void* userdata); +    void            onCommitAnimatedMeshCheckbox(LLUICtrl* ctrl, void* userdata);  	static void     onCommitPhysicsParam(       LLUICtrl* ctrl, void* userdata);  	static void 	onCommitMaterial(		LLUICtrl* ctrl, void* userdata); diff --git a/indra/newview/llsceneview.cpp b/indra/newview/llsceneview.cpp index 112fa5b4e1..f7aa63e34d 100644 --- a/indra/newview/llsceneview.cpp +++ b/indra/newview/llsceneview.cpp @@ -33,6 +33,7 @@  #include "llviewerregion.h"  #include "llagent.h"  #include "llvolumemgr.h" +#include "llmeshrepository.h"  LLSceneView* gSceneView = NULL; @@ -129,17 +130,23 @@ void LLSceneView::draw()  				visible_triangles[idx].push_back(visible);  				triangles[idx].push_back(high_triangles); -				S32 bytes = 0; -				S32 visible_bytes = 0; - -				F32 streaming = object->getStreamingCost(&bytes, &visible_bytes); -				total_streaming[idx] += streaming; -				streaming_cost[idx].push_back(streaming); +                F32 streaming = object->getStreamingCost(); +                total_streaming[idx] += streaming; +                streaming_cost[idx].push_back(streaming);  				F32 physics = object->getPhysicsCost();  				total_physics[idx] += physics;  				physics_cost[idx].push_back(physics); +				S32 bytes = 0; +				S32 visible_bytes = 0; +                LLMeshCostData costs; +                if (object->getCostData(costs)) +                { +                    bytes = costs.getSizeTotal(); +                    visible_bytes = costs.getSizeByLOD(object->getLOD()); +                } +  				total_bytes[idx] += bytes;  				total_visible_bytes[idx] += visible_bytes;  			} diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index fce21fa30a..4a2d545b33 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -46,6 +46,7 @@  #include "llundo.h"  #include "lluuid.h"  #include "llvolume.h" +#include "llcontrolavatar.h"  #include "message.h"  #include "object_flags.h"  #include "llquaternion.h" @@ -645,6 +646,10 @@ void LLSelectMgr::confirmUnlinkObjects(const LLSD& notification, const LLSD& res  // otherwise. this allows the handle_link method to more finely check  // the selection and give an error message when the uer has a  // reasonable expectation for the link to work, but it will fail. +// +// For animated objects, there's additional check that if the +// selection includes at least one animated object, the total mesh +// triangle count cannot exceed the designated limit.  bool LLSelectMgr::enableLinkObjects()  {  	bool new_value = false; @@ -669,6 +674,10 @@ bool LLSelectMgr::enableLinkObjects()  			new_value = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly);  		}  	} +    if (!LLSelectMgr::getInstance()->getSelection()->checkAnimatedObjectLinkable()) +    { +        new_value = false; +    }  	return new_value;  } @@ -6659,7 +6668,10 @@ S32 get_family_count(LLViewerObject *parent)  //-----------------------------------------------------------------------------  // updateSelectionCenter -//----------------------------------------------------------------------------- +// +// FIXME this is a grab bag of functionality only some of which has to do +// with the selection center +// -----------------------------------------------------------------------------  void LLSelectMgr::updateSelectionCenter()  {  	const F32 MOVE_SELECTION_THRESHOLD = 1.f;		//  Movement threshold in meters for updating selection @@ -6677,23 +6689,12 @@ void LLSelectMgr::updateSelectionCenter()  		mSelectionCenterGlobal.clearVec();  		mShowSelection = FALSE;  		mSelectionBBox = LLBBox();  -		mPauseRequest = NULL;  		resetAgentHUDZoom(); -  	}  	else  	{  		mSelectedObjects->mSelectType = getSelectTypeForObject(object); -		if (mSelectedObjects->mSelectType == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid() && object->getParent() != NULL) -		{ -			mPauseRequest = gAgentAvatarp->requestPause(); -		} -		else -		{ -			mPauseRequest = NULL; -		} -  		if (mSelectedObjects->mSelectType != SELECT_TYPE_HUD && isAgentAvatarValid())  		{  			// reset hud ZOOM @@ -6770,6 +6771,59 @@ void LLSelectMgr::updateSelectionCenter()  	{  		gEditMenuHandler = NULL;  	} + +    pauseAssociatedAvatars(); +} + +//----------------------------------------------------------------------------- +// pauseAssociatedAvatars +// +// If the selection includes an attachment or an animated object, the +// associated avatars should pause their animations until they are no +// longer selected. +//----------------------------------------------------------------------------- +void LLSelectMgr::pauseAssociatedAvatars() +{ +    mPauseRequests.clear(); + +    for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); +         iter != mSelectedObjects->end(); iter++) +    { +        LLSelectNode* node = *iter; +        LLViewerObject* object = node->getObject(); +        if (!object) +            continue; +			 +        mSelectedObjects->mSelectType = getSelectTypeForObject(object); + +        if (mSelectedObjects->mSelectType == SELECT_TYPE_ATTACHMENT &&  +            isAgentAvatarValid() && object->getParent() != NULL) +        { +            if (object->isAnimatedObject()) +            { +                // Is an animated object attachment. +                // Pause both the control avatar and the avatar it's attached to. +                if (object->getControlAvatar()) +                { +                    mPauseRequests.push_back(object->getControlAvatar()->requestPause()); +                } +                mPauseRequests.push_back(gAgentAvatarp->requestPause()); +            } +            else +            { +                // Is a regular attachment. Pause the avatar it's attached to. +                mPauseRequests.push_back(gAgentAvatarp->requestPause()); +            } +        } +        else +        { +            if (object && object->isAnimatedObject() && object->getControlAvatar()) +            { +                // Is a non-attached animated object. Pause the control avatar. +                mPauseRequests.push_back(object->getControlAvatar()->requestPause()); +            } +        } +    }  }  void LLSelectMgr::updatePointAt() @@ -7246,10 +7300,16 @@ F32 LLObjectSelection::getSelectedObjectStreamingCost(S32* total_bytes, S32* vis  		if (object)  		{ -			S32 bytes = 0; -			S32 visible = 0; -			cost += object->getStreamingCost(&bytes, &visible); +			cost += object->getStreamingCost(); +            S32 bytes = 0; +            S32 visible = 0; +            LLMeshCostData costs; +            if (object->getCostData(costs)) +            { +                bytes = costs.getSizeTotal(); +                visible = costs.getSizeByLOD(object->getLOD()); +            }  			if (total_bytes)  			{  				*total_bytes += bytes; @@ -7406,6 +7466,31 @@ bool LLObjectSelection::applyToObjects(LLSelectedObjectFunctor* func)  	return result;  } +bool LLObjectSelection::checkAnimatedObjectEstTris() +{ +    F32 est_tris = 0; +    F32 max_tris = 0; +    S32 anim_count = 0; +	for (root_iterator iter = root_begin(); iter != root_end(); ++iter) +	{ +		LLViewerObject* object = (*iter)->getObject(); +		if (!object) +			continue; +        if (object->isAnimatedObject()) +        { +            anim_count++; +        } +        est_tris += object->recursiveGetEstTrianglesMax(); +        max_tris = llmax((F32)max_tris,(F32)object->getAnimatedObjectMaxTris()); +	} +	return anim_count==0 || est_tris <= max_tris; +} + +bool LLObjectSelection::checkAnimatedObjectLinkable() +{ +    return checkAnimatedObjectEstTris(); +} +  bool LLObjectSelection::applyToRootObjects(LLSelectedObjectFunctor* func, bool firstonly)  {  	bool result = firstonly ? false : true; diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 87ac899325..caf104178f 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -340,6 +340,9 @@ public:  	// returns TRUE is any node is currenly worn as an attachment  	BOOL isAttachment(); +    bool checkAnimatedObjectEstTris(); +    bool checkAnimatedObjectLinkable(); +      	// Apply functors to various subsets of the selected objects  	// If firstonly is FALSE, returns the AND of all apply() calls.  	// Else returns TRUE immediately if any apply() call succeeds (i.e. OR with early exit) @@ -748,6 +751,8 @@ public:  	LLVector3d		getSelectionCenterGlobal() const	{ return mSelectionCenterGlobal; }  	void			updateSelectionCenter(); +    void pauseAssociatedAvatars(); +  	void resetAgentHUDZoom();  	void setAgentHUDZoom(F32 target_zoom, F32 current_zoom);  	void getAgentHUDZoom(F32 &target_zoom, F32 ¤t_zoom) const; @@ -849,7 +854,7 @@ private:  	LLFrameTimer			mEffectsTimer;  	BOOL					mForceSelection; -	LLAnimPauseRequest		mPauseRequest; +    std::vector<LLAnimPauseRequest>	mPauseRequests;  };  // *DEPRECATED: For callbacks or observers, use diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp index dba690242a..0fa4c2b114 100644 --- a/indra/newview/llskinningutil.cpp +++ b/indra/newview/llskinningutil.cpp @@ -31,26 +31,73 @@  #include "llvoavatar.h"  #include "llviewercontrol.h"  #include "llmeshrepository.h" +#include "llvolume.h" +#include "llrigginginfo.h" + +void dump_avatar_and_skin_state(const std::string& reason, LLVOAvatar *avatar, const LLMeshSkinInfo *skin) +{ +    static S32 dump_count = 0; +    const S32 max_dump = 10; + +    if (dump_count < max_dump) +    { +        LL_WARNS("Avatar") << avatar->getFullname() << " dumping, reason " << reason +                           << " avatar build state: isBuilt() " << avatar->isBuilt()  +                           << " mInitFlags " << avatar->mInitFlags << LL_ENDL; +        LL_WARNS("Avatar") << "Skin num joints " << skin->mJointNames.size() << " " << skin->mJointNums.size() << LL_ENDL; +        LL_WARNS("Avatar") << "Skin scrubbed " << skin->mInvalidJointsScrubbed  +                           << " nums init " << skin->mJointNumsInitialized << LL_ENDL; +        for (S32 j=0; j<skin->mJointNames.size(); j++) +        { +            LL_WARNS("Avatar") << "skin joint idx " << j << " name [" << skin->mJointNames[j]  +                               << "] num " << skin->mJointNums[j] << LL_ENDL; +            const std::string& name = skin->mJointNames[j]; +            S32 joint_num = skin->mJointNums[j]; + +            LLJoint *name_joint = avatar->getJoint(name); +            LLJoint *num_joint = avatar->getJoint(joint_num); +            if (!name_joint) +            { +                LL_WARNS("Avatar") << "failed to find joint by name" << LL_ENDL;  +            } +            if (!num_joint) +            { +                LL_WARNS("Avatar") << "failed to find joint by num" << LL_ENDL;  +            } +            if (num_joint != name_joint) +            { +                LL_WARNS("Avatar") << "joint pointers don't match" << LL_ENDL; +            } +            if (num_joint && num_joint->getJointNum() != joint_num) +            { +                LL_WARNS("Avatar") << "joint found by num has wrong num " << joint_num << "!=" << num_joint->getJointNum() << LL_ENDL; +            } +            if (name_joint && name_joint->getJointNum() != joint_num) +            { +                LL_WARNS("Avatar") << "joint found by name has wrong num " << joint_num << "!=" << name_joint->getJointNum() << LL_ENDL; +            } +        } +        LL_WARNS("Avatar") << LL_ENDL; + +        dump_count++; +    } +} -// static  void LLSkinningUtil::initClass()  {  } -// static  U32 LLSkinningUtil::getMaxJointCount()  {      U32 result = LL_MAX_JOINTS_PER_MESH_OBJECT;  	return result;  } -// static  U32 LLSkinningUtil::getMeshJointCount(const LLMeshSkinInfo *skin)  {  	return llmin((U32)getMaxJointCount(), (U32)skin->mJointNames.size());  } -// static  void LLSkinningUtil::scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin)  {      if (skin->mInvalidJointsScrubbed) @@ -64,35 +111,25 @@ void LLSkinningUtil::scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin          // needed for handling of any legacy bad data.          if (!avatar->getJoint(skin->mJointNames[j]))          { -            LL_DEBUGS("Avatar") << "Mesh rigged to invalid joint" << skin->mJointNames[j] << LL_ENDL; +            LL_DEBUGS("Avatar") << avatar->getFullname() << " mesh rigged to invalid joint " << skin->mJointNames[j] << LL_ENDL; +            LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " mesh rigged to invalid joint" << skin->mJointNames[j] << LL_ENDL;              skin->mJointNames[j] = "mPelvis"; +            skin->mJointNumsInitialized = false; // force update after names change.          }      }      skin->mInvalidJointsScrubbed = true;  } -// static  void LLSkinningUtil::initSkinningMatrixPalette(      LLMatrix4* mat,      S32 count,       const LLMeshSkinInfo* skin,      LLVOAvatar *avatar)  { +    initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar);      for (U32 j = 0; j < count; ++j)      { -        LLJoint *joint = NULL; -        if (skin->mJointNums[j] == -1) -        { -            joint = avatar->getJoint(skin->mJointNames[j]); -            if (joint) -            { -                skin->mJointNums[j] = joint->getJointNum(); -            } -        } -		else -		{ -			joint = avatar->getJoint(skin->mJointNums[j]); -		} +        LLJoint *joint = avatar->getJoint(skin->mJointNums[j]);          if (joint)          {  #define MAT_USE_SSE @@ -114,12 +151,19 @@ void LLSkinningUtil::initSkinningMatrixPalette(              // rendering  should  be disabled  unless  all joints  are              // valid.  In other  cases of  skinned  rendering, invalid              // joints should already have  been removed during scrubInvalidJoints(). -            LL_WARNS_ONCE("Avatar") << "Rigged to invalid joint name " << skin->mJointNames[j] << LL_ENDL; +            LL_WARNS_ONCE("Avatar") << avatar->getFullname()  +                                    << " rigged to invalid joint name " << skin->mJointNames[j]  +                                    << " num " << skin->mJointNums[j] << LL_ENDL; +            LL_WARNS_ONCE("Avatar") << avatar->getFullname()  +                                    << " avatar build state: isBuilt() " << avatar->isBuilt()  +                                    << " mInitFlags " << avatar->mInitFlags << LL_ENDL; +#if 0 +            dump_avatar_and_skin_state("initSkinningMatrixPalette joint not found", avatar, skin); +#endif          }      }  } -// static  void LLSkinningUtil::checkSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin)  {  #ifdef SHOW_ASSERT                  // same condition that controls llassert() @@ -159,7 +203,6 @@ void LLSkinningUtil::scrubSkinWeights(LLVector4a* weights, U32 num_vertices, con  	checkSkinWeights(weights, num_vertices, skin);  } -// static  void LLSkinningUtil::getPerVertexSkinMatrix(      F32* weights,      LLMatrix4a* mat, @@ -216,3 +259,150 @@ void LLSkinningUtil::getPerVertexSkinMatrix(      llassert(valid_weights);  } +void LLSkinningUtil::initJointNums(LLMeshSkinInfo* skin, LLVOAvatar *avatar) +{ +    if (!skin->mJointNumsInitialized) +    { +        for (U32 j = 0; j < skin->mJointNames.size(); ++j) +        { +            LLJoint *joint = NULL; +            if (skin->mJointNums[j] == -1) +            { +                joint = avatar->getJoint(skin->mJointNames[j]); +                if (joint) +                { +                    skin->mJointNums[j] = joint->getJointNum(); +                    if (skin->mJointNums[j] < 0) +                    { +                        LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " joint has unusual number " << skin->mJointNames[j] << ": " << skin->mJointNums[j] << LL_ENDL; +                        LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " avatar build state: isBuilt() " << avatar->isBuilt() << " mInitFlags " << avatar->mInitFlags << LL_ENDL; +                    } +                } +                else +                { +                    LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " unable to find joint " << skin->mJointNames[j] << LL_ENDL; +                    LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " avatar build state: isBuilt() " << avatar->isBuilt() << " mInitFlags " << avatar->mInitFlags << LL_ENDL; +#if 0 +                    dump_avatar_and_skin_state("initJointNums joint not found", avatar, skin); +#endif +                } +            } +        } +        skin->mJointNumsInitialized = true; +    } +} + +static LLTrace::BlockTimerStatHandle FTM_FACE_RIGGING_INFO("Face Rigging Info"); + +void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *avatar, LLVolumeFace& vol_face) +{ +    LL_RECORD_BLOCK_TIME(FTM_FACE_RIGGING_INFO); + +    if (vol_face.mJointRiggingInfoTab.needsUpdate()) +    { +        S32 num_verts = vol_face.mNumVertices; +        if (num_verts>0 && vol_face.mWeights && (skin->mJointNames.size()>0)) +        { +            initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar); +            if (vol_face.mJointRiggingInfoTab.size()==0) +            { +                //std::set<S32> active_joints; +                //S32 active_verts = 0; +                vol_face.mJointRiggingInfoTab.resize(LL_CHARACTER_MAX_ANIMATED_JOINTS); +                LLJointRiggingInfoTab &rig_info_tab = vol_face.mJointRiggingInfoTab; +                for (S32 i=0; i<vol_face.mNumVertices; i++) +                { +                    LLVector4a& pos = vol_face.mPositions[i]; +                    F32 *weights = vol_face.mWeights[i].getF32ptr(); +                    LLVector4 wght; +                    S32 idx[4]; +                    F32 scale = 0.0f; +                    // FIXME unpacking of weights should be pulled into a common function and optimized if possible. +                    for (U32 k = 0; k < 4; k++) +                    { +                        F32 w = weights[k]; +                        idx[k] = llclamp((S32) floorf(w), (S32)0, (S32)LL_CHARACTER_MAX_ANIMATED_JOINTS-1); +                        wght[k] = w - idx[k]; +                        scale += wght[k]; +                    } +                    if (scale > 0.0f) +                    { +                        for (U32 k=0; k<4; ++k) +                        { +                            wght[k] /= scale; +                        } +                    } +                    for (U32 k=0; k<4; ++k) +                    { +						S32 joint_index = idx[k]; +                        if (wght[k] > 0.0f) +                        { +                            S32 joint_num = skin->mJointNums[joint_index]; +                            if (joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS) +                            { +                                rig_info_tab[joint_num].setIsRiggedTo(true); + +                                // FIXME could precompute these matMuls. +                                LLMatrix4a bind_shape; +                                bind_shape.loadu(skin->mBindShapeMatrix); +                                LLMatrix4a inv_bind; +                                inv_bind.loadu(skin->mInvBindMatrix[joint_index]); +                                LLMatrix4a mat; +                                matMul(bind_shape, inv_bind, mat); +                                LLVector4a pos_joint_space; +                                mat.affineTransform(pos, pos_joint_space); +                                pos_joint_space.mul(wght[k]); +                                LLVector4a *extents = rig_info_tab[joint_num].getRiggedExtents(); +                                update_min_max(extents[0], extents[1], pos_joint_space); +                            } +                        } +                    } +                } +                //LL_DEBUGS("RigSpammish") << "built rigging info for vf " << &vol_face  +                //                         << " num_verts " << vol_face.mNumVertices +                //                         << " active joints " << active_joints.size() +                //                         << " active verts " << active_verts +                //                         << LL_ENDL;  +                vol_face.mJointRiggingInfoTab.setNeedsUpdate(false); +            } +        } +        if (vol_face.mJointRiggingInfoTab.size()!=0) +        { +            LL_DEBUGS("RigSpammish") << "we have rigging info for vf " << &vol_face  +                                     << " num_verts " << vol_face.mNumVertices << LL_ENDL;  +        } +        else +        { +            LL_DEBUGS("RigSpammish") << "no rigging info for vf " << &vol_face  +                                     << " num_verts " << vol_face.mNumVertices << LL_ENDL;  +        } + +    } +} + +// This is used for extracting rotation from a bind shape matrix that +// already has scales baked in +LLQuaternion LLSkinningUtil::getUnscaledQuaternion(const LLMatrix4& mat4) +{ +    LLMatrix3 bind_mat = mat4.getMat3(); +    for (auto i = 0; i < 3; i++) +    { +        F32 len = 0.0f; +        for (auto j = 0; j < 3; j++) +        { +            len += bind_mat.mMatrix[i][j] * bind_mat.mMatrix[i][j]; +        } +        if (len > 0.0f) +        { +            len = sqrt(len); +            for (auto j = 0; j < 3; j++) +            { +                bind_mat.mMatrix[i][j] /= len; +            } +        } +    } +    bind_mat.invert(); +    LLQuaternion bind_rot = bind_mat.quaternion(); +    bind_rot.normalize(); +    return bind_rot; +} diff --git a/indra/newview/llskinningutil.h b/indra/newview/llskinningutil.h index 135b25d4d2..ccc501adc0 100644 --- a/indra/newview/llskinningutil.h +++ b/indra/newview/llskinningutil.h @@ -30,18 +30,21 @@  class LLVOAvatar;  class LLMeshSkinInfo;  class LLMatrix4a; +class LLVolumeFace; -class LLSkinningUtil +namespace LLSkinningUtil  { -public: -    static void initClass(); -    static U32 getMaxJointCount(); -    static U32 getMeshJointCount(const LLMeshSkinInfo *skin); -    static void scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin); -    static void initSkinningMatrixPalette(LLMatrix4* mat, S32 count, const LLMeshSkinInfo* skin, LLVOAvatar *avatar); -    static void checkSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin); -    static void scrubSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin); -    static void getPerVertexSkinMatrix(F32* weights, LLMatrix4a* mat, bool handle_bad_scale, LLMatrix4a& final_mat, U32 max_joints); +    void initClass(); +    U32 getMaxJointCount(); +    U32 getMeshJointCount(const LLMeshSkinInfo *skin); +    void scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin); +    void initSkinningMatrixPalette(LLMatrix4* mat, S32 count, const LLMeshSkinInfo* skin, LLVOAvatar *avatar); +    void checkSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin); +    void scrubSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin); +    void getPerVertexSkinMatrix(F32* weights, LLMatrix4a* mat, bool handle_bad_scale, LLMatrix4a& final_mat, U32 max_joints); +    void initJointNums(LLMeshSkinInfo* skin, LLVOAvatar *avatar); +    void updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *avatar, LLVolumeFace& vol_face); +	LLQuaternion getUnscaledQuaternion(const LLMatrix4& mat4);  };  #endif diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 9791f4a921..ee77556047 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -29,6 +29,7 @@  #include "llspatialpartition.h"  #include "llappviewer.h" +#include "llcallstack.h"  #include "lltexturecache.h"  #include "lltexturefetch.h"  #include "llimageworker.h" @@ -777,6 +778,10 @@ F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)  		dist = eye.getLength3().getF32();  	} +    LL_DEBUGS("RiggedBox") << "calcDistance, group " << group << " camera " << origin << " obj bounds "  +                           << group->mObjectBounds[0] << ", " << group->mObjectBounds[1]  +                           << " dist " << dist << " radius " << group->mRadius << LL_ENDL; +  	if (dist < 16.f)  	{  		dist /= 16.f; @@ -808,7 +813,8 @@ F32 LLSpatialGroup::getUpdateUrgency() const  BOOL LLSpatialGroup::changeLOD()  {  	if (hasState(ALPHA_DIRTY | OBJECT_DIRTY)) -	{ ///a rebuild is going to happen, update distance and LoD +	{ +		//a rebuild is going to happen, update distance and LoD  		return TRUE;  	} @@ -816,8 +822,28 @@ BOOL LLSpatialGroup::changeLOD()  	{  		F32 ratio = (mDistance - mLastUpdateDistance)/(llmax(mLastUpdateDistance, mRadius)); +        // MAINT-8264 - this check is not robust if it needs to work +        // for bounding boxes much larger than the actual enclosed +        // objects, and using distance to box center is also +        // problematic. Consider the case that you have a large box +        // where the enclosed object is in one corner. As you zoom in +        // on the corner, the object gets much closer to the camera, +        // but the distance to the box center changes very little, and +        // an LOD change will not trigger, so object LOD gets "stuck" +        // at a too-low value. In the case of the above JIRA, the box +        // was large only due to another error, so this logic did not +        // need to be changed. +  		if (fabsf(ratio) >= getSpatialPartition()->mSlopRatio)  		{ +            LL_DEBUGS("RiggedBox") << "changeLOD true because of ratio compare " +                                   << fabsf(ratio) << " " << getSpatialPartition()->mSlopRatio << LL_ENDL; +            LL_DEBUGS("RiggedBox") << "sg " << this << "\nmDistance " << mDistance +                                   << " mLastUpdateDistance " << mLastUpdateDistance +                                   << " mRadius " << mRadius +                                   << " fab ratio " << fabsf(ratio)  +                                   << " slop " << getSpatialPartition()->mSlopRatio << LL_ENDL; +         			return TRUE;  		} @@ -869,16 +895,6 @@ void LLSpatialGroup::handleDestruction(const TreeNode* node)  		}  	} -	//clean up avatar attachment stats -	LLSpatialBridge* bridge = getSpatialPartition()->asBridge(); -	if (bridge) -	{ -		if (bridge->mAvatar.notNull()) -		{ -			bridge->mAvatar->subtractAttachmentArea(mSurfaceArea ); -		} -	} -  	clearDrawMap();  	mVertexBuffer = NULL;  	mBufferMap.clear(); @@ -2119,17 +2135,17 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)  	{  		if (drawable->isSpatialBridge())  		{ -			gGL.diffuseColor4f(1,0.5f,0,1); +			gGL.diffuseColor4f(1,0.5f,0,1); // orange  		}  		else if (drawable->getVOVolume()) -		{ -			if (drawable->isRoot()) +		{  +            if (drawable->isRoot())  			{ -				gGL.diffuseColor4f(1,1,0,1); +				gGL.diffuseColor4f(1,1,0,1); // yellow  			}  			else  			{ -				gGL.diffuseColor4f(0,1,0,1); +				gGL.diffuseColor4f(0,1,0,1); // green  			}  		}  		else if (drawable->getVObj()) @@ -2137,24 +2153,24 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)  			switch (drawable->getVObj()->getPCode())  			{  				case LLViewerObject::LL_VO_SURFACE_PATCH: -						gGL.diffuseColor4f(0,1,1,1); +                    	gGL.diffuseColor4f(0,1,1,1); // cyan  						break;  				case LLViewerObject::LL_VO_CLOUDS:  						// no longer used  						break;  				case LLViewerObject::LL_VO_PART_GROUP:  				case LLViewerObject::LL_VO_HUD_PART_GROUP: -						gGL.diffuseColor4f(0,0,1,1); +                    	gGL.diffuseColor4f(0,0,1,1); // blue  						break;  				case LLViewerObject::LL_VO_VOID_WATER:  				case LLViewerObject::LL_VO_WATER: -						gGL.diffuseColor4f(0,0.5f,1,1); +                    	gGL.diffuseColor4f(0,0.5f,1,1); // medium blue  						break;  				case LL_PCODE_LEGACY_TREE: -						gGL.diffuseColor4f(0,0.5f,0,1); +                    	gGL.diffuseColor4f(0,0.5f,0,1); // dark green  						break;  				default: -						gGL.diffuseColor4f(1,0,1,1); +                    	gGL.diffuseColor4f(1,0,1,1); // magenta  						break;  			}  		} diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h index 6104b92d43..7e65da42f7 100644 --- a/indra/newview/llspatialpartition.h +++ b/indra/newview/llspatialpartition.h @@ -468,8 +468,6 @@ public:  	virtual LLCamera transformCamera(LLCamera& camera);  	LLDrawable* mDrawable; -	LLPointer<LLVOAvatar> mAvatar; -  };  class LLCullResult  diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index cc02642203..37aaece5d6 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2397,6 +2397,7 @@ void register_viewer_callbacks(LLMessageSystem* msg)  	msg->setHandlerFuncFast(_PREHASH_NameValuePair,			process_name_value);  	msg->setHandlerFuncFast(_PREHASH_RemoveNameValuePair,	process_remove_name_value);  	msg->setHandlerFuncFast(_PREHASH_AvatarAnimation,		process_avatar_animation); +	msg->setHandlerFuncFast(_PREHASH_ObjectAnimation,		process_object_animation);  	msg->setHandlerFuncFast(_PREHASH_AvatarAppearance,		process_avatar_appearance);  	msg->setHandlerFuncFast(_PREHASH_CameraConstraint,		process_camera_constraint);  	msg->setHandlerFuncFast(_PREHASH_AvatarSitResponse,		process_avatar_sit_response); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 0f38cca56f..f66211ef34 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -785,7 +785,7 @@ void LLToolDragAndDrop::dragOrDrop3D( S32 x, S32 y, MASK mask, BOOL drop, EAccep  	mDrop = drop;  	if (mDrop)  	{ -		// don't allow drag and drop onto transparent objects +		// don't allow drag and drop onto rigged or transparent objects  		pick(gViewerWindow->pickImmediate(x, y, FALSE, FALSE));  	}  	else diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 5082e16685..9e37ca0394 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -111,9 +111,11 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask)      mMouseOutsideSlop = FALSE;  	mMouseDownX = x;  	mMouseDownY = y; - +    LLTimer pick_timer; +    BOOL pick_rigged = false; //gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick");  	//left mouse down always picks transparent (but see handleMouseUp) -	mPick = gViewerWindow->pickImmediate(x, y, TRUE, FALSE); +	mPick = gViewerWindow->pickImmediate(x, y, TRUE, pick_rigged); +    LL_INFOS() << "pick_rigged is " << (S32) pick_rigged << " pick time elapsed " << pick_timer.getElapsedTimeF32() << LL_ENDL;  	mPick.mKeyMask = mask;  	mMouseButtonDown = true; @@ -128,7 +130,10 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask)  BOOL LLToolPie::handleRightMouseDown(S32 x, S32 y, MASK mask)  {  	// don't pick transparent so users can't "pay" transparent objects -	mPick = gViewerWindow->pickImmediate(x, y, /*BOOL pick_transparent*/ FALSE, /*BOOL pick_rigged*/ TRUE, /*BOOL pick_particle*/ TRUE); +	mPick = gViewerWindow->pickImmediate(x, y, +                                         /*BOOL pick_transparent*/ FALSE, +                                         /*BOOL pick_rigged*/ TRUE, +                                         /*BOOL pick_particle*/ TRUE);  	mPick.mKeyMask = mask;  	// claim not handled so UI focus stays same @@ -544,7 +549,8 @@ void LLToolPie::selectionPropertiesReceived()  BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask)  { -	mHoverPick = gViewerWindow->pickImmediate(x, y, FALSE, FALSE); +    BOOL pick_rigged = false; //gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick"); +	mHoverPick = gViewerWindow->pickImmediate(x, y, FALSE, pick_rigged);  	LLViewerObject *parent = NULL;  	LLViewerObject *object = mHoverPick.getObject();  	LLSelectMgr::getInstance()->setHoverObject(object, mHoverPick.mObjectFace); @@ -590,7 +596,7 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask)  	else  	{  		// perform a separate pick that detects transparent objects since they respond to 1-click actions -		LLPickInfo click_action_pick = gViewerWindow->pickImmediate(x, y, TRUE, FALSE); +		LLPickInfo click_action_pick = gViewerWindow->pickImmediate(x, y, TRUE, pick_rigged);  		LLViewerObject* click_action_object = click_action_pick.getObject(); @@ -676,6 +682,7 @@ BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask)          LLPickInfo savedPick = mPick;          mPick = gViewerWindow->pickImmediate(savedPick.mMousePt.mX, savedPick.mMousePt.mY,                                               FALSE /* ignore transparent */, +                                             FALSE /* ignore rigged */,                                               FALSE /* ignore particles */);          if (!mPick.mPosGlobal.isExactlyZero()			// valid coordinates for pick @@ -765,6 +772,7 @@ BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask)          LLPickInfo savedPick = mPick;          mPick = gViewerWindow->pickImmediate(savedPick.mMousePt.mX, savedPick.mMousePt.mY,                                               FALSE /* ignore transparent */, +                                             FALSE /* ignore rigged */,                                               FALSE /* ignore particles */);          if(mPick.mPickType == LLPickInfo::PICK_OBJECT) @@ -1757,8 +1765,7 @@ BOOL LLToolPie::handleRightClickPick()  		gMenuHolder->setObjectSelection(LLSelectMgr::getInstance()->getSelection());  		bool is_other_attachment = (object->isAttachment() && !object->isHUDAttachment() && !object->permYouOwner()); -		if (object->isAvatar()  -			|| is_other_attachment) +		if (object->isAvatar() || is_other_attachment)  		{  			// Find the attachment's avatar  			while( object && object->isAttachment()) diff --git a/indra/newview/lltoolselect.cpp b/indra/newview/lltoolselect.cpp index c22eb48eef..e52bc0b015 100644 --- a/indra/newview/lltoolselect.cpp +++ b/indra/newview/lltoolselect.cpp @@ -64,7 +64,8 @@ LLToolSelect::LLToolSelect( LLToolComposite* composite )  BOOL LLToolSelect::handleMouseDown(S32 x, S32 y, MASK mask)  {  	// do immediate pick query -	mPick = gViewerWindow->pickImmediate(x, y, TRUE, FALSE); +    BOOL pick_rigged = false; //gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick"); +	mPick = gViewerWindow->pickImmediate(x, y, TRUE, pick_rigged);  	// Pass mousedown to agent  	LLTool::handleMouseDown(x, y, mask); diff --git a/indra/newview/lltoolselectrect.cpp b/indra/newview/lltoolselectrect.cpp index 71dc8001d4..bae32f7bc0 100644 --- a/indra/newview/lltoolselectrect.cpp +++ b/indra/newview/lltoolselectrect.cpp @@ -71,7 +71,8 @@ void dialog_refresh_all(void);  BOOL LLToolSelectRect::handleMouseDown(S32 x, S32 y, MASK mask)  { -	handlePick(gViewerWindow->pickImmediate(x, y, TRUE, FALSE)); +    BOOL pick_rigged = false; //gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick"); +	handlePick(gViewerWindow->pickImmediate(x, y, TRUE /* pick_transparent */, pick_rigged));  	LLTool::handleMouseDown(x, y, mask); diff --git a/indra/newview/lluiavatar.cpp b/indra/newview/lluiavatar.cpp new file mode 100644 index 0000000000..e4e266c92a --- /dev/null +++ b/indra/newview/lluiavatar.cpp @@ -0,0 +1,61 @@ +/** + * @file lluiavatar.cpp + * @brief Implementation for special dummy avatar used in some UI views + * + * $LicenseInfo:firstyear=2017&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2017, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "lluiavatar.h" +#include "llagent.h" //  Get state values from here +#include "llviewerobjectlist.h" +#include "pipeline.h" +#include "llanimationstates.h" +#include "llviewercontrol.h" +#include "llmeshrepository.h" +#include "llviewerregion.h" + +LLUIAvatar::LLUIAvatar(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) : +    LLVOAvatar(id, pcode, regionp) +{ +    mIsDummy = TRUE; +    mIsUIAvatar = true; +} + +// virtual +LLUIAvatar::~LLUIAvatar() +{ +} + +// virtual +void LLUIAvatar::initInstance() +{ +    LLVOAvatar::initInstance(); + +    createDrawable( &gPipeline ); +	setPositionAgent(LLVector3::zero); +	slamPosition(); +	updateJointLODs(); +	updateGeometry(mDrawable); + +    mInitFlags |= 1<<3; +} diff --git a/indra/newview/lluiavatar.h b/indra/newview/lluiavatar.h new file mode 100644 index 0000000000..bcdffedef2 --- /dev/null +++ b/indra/newview/lluiavatar.h @@ -0,0 +1,44 @@ +/** + * @file lluiavatar.h + * @brief Special dummy avatar used in some UI views + * + * $LicenseInfo:firstyear=2017&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2017, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_UIAVATAR_H +#define LL_UIAVATAR_H + +#include "llvoavatar.h" +#include "llvovolume.h" + +class LLUIAvatar: +    public LLVOAvatar +{ +    LOG_CLASS(LLUIAvatar); + +public: +    LLUIAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); +	virtual void 			initInstance(); // Called after construction to initialize the class. +	virtual	~LLUIAvatar(); +}; + +#endif //LL_CONTROLAVATAR_H diff --git a/indra/newview/llviewerjointattachment.cpp b/indra/newview/llviewerjointattachment.cpp index 66e392ac42..cf9243a871 100644 --- a/indra/newview/llviewerjointattachment.cpp +++ b/indra/newview/llviewerjointattachment.cpp @@ -357,6 +357,25 @@ void LLViewerJointAttachment::setOriginalPosition(LLVector3& position)  }  //----------------------------------------------------------------------------- +// getNumAnimatedObjects() +//----------------------------------------------------------------------------- +S32 LLViewerJointAttachment::getNumAnimatedObjects() const +{ +    S32 count = 0; +	for (attachedobjs_vec_t::const_iterator iter = mAttachedObjects.begin(); +		 iter != mAttachedObjects.end(); +		 ++iter) +	{ +        const LLViewerObject *attached_object = *iter; +        if (attached_object->isAnimatedObject()) +        { +            count++; +        } +    } +    return count; +} + +//-----------------------------------------------------------------------------  // clampObjectPosition()  //-----------------------------------------------------------------------------  void LLViewerJointAttachment::clampObjectPosition() diff --git a/indra/newview/llviewerjointattachment.h b/indra/newview/llviewerjointattachment.h index 9addafaee1..9641ab4208 100644 --- a/indra/newview/llviewerjointattachment.h +++ b/indra/newview/llviewerjointattachment.h @@ -77,6 +77,7 @@ public:  	S32 getGroup() const { return mGroup; }  	S32 getPieSlice() const { return mPieSlice; }  	S32	getNumObjects() const { return mAttachedObjects.size(); } +	S32	getNumAnimatedObjects() const;  	void clampObjectPosition(); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index ec851ddaf9..d2c26dd6e7 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1078,6 +1078,10 @@ U32 info_display_from_string(std::string info_display)  	{  		return LLPipeline::RENDER_DEBUG_TEXEL_DENSITY;  	} +	else if ("triangle count" == info_display) +	{ +		return LLPipeline::RENDER_DEBUG_TRIANGLE_COUNT; +	}  	else  	{  		LL_WARNS() << "unrecognized feature name '" << info_display << "'" << LL_ENDL; @@ -1617,7 +1621,24 @@ class LLAdvancedEnableAppearanceToXML : public view_listener_t  {  	bool handleEvent(const LLSD& userdata)  	{ -		return gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"); +        LLViewerObject *obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); +        if (obj && obj->isAnimatedObject() && obj->getControlAvatar()) +        { +            return gSavedSettings.getBOOL("DebugAnimatedObjects"); +        } +        else if (obj && obj->isAttachment() && obj->getAvatar()) +        { +            return gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"); +        } +        else if (obj && obj->isAvatar()) +        { +            // This has to be a non-control avatar, because control avs are invisible and unclickable. +            return gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"); +        } +		else +		{ +			return false; +		}  	}  }; @@ -1626,13 +1647,34 @@ class LLAdvancedAppearanceToXML : public view_listener_t  	bool handleEvent(const LLSD& userdata)  	{  		std::string emptyname; -		LLVOAvatar* avatar = -			find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); -		if (!avatar) -		{ +        LLViewerObject *obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); +        LLVOAvatar *avatar = NULL; +        if (obj) +        { +            if (obj->isAvatar()) +            { +                avatar = obj->asAvatar(); +            } +            else +            { +                // If there is a selection, find the associated +                // avatar. Normally there's only one obvious choice. But +                // what should be returned if the object is in an attached +                // animated object? getAvatar() will give the skeleton of +                // the animated object. getAvatarAncestor() will give the +                // actual human-driven avatar. +                avatar = obj->getAvatar(); +            } +        } +        else +        { +            // If no selection, use the self avatar.  			avatar = gAgentAvatarp; -		} -		avatar->dumpArchetypeXML(emptyname); +        } +        if (avatar) +        { +            avatar->dumpArchetypeXML(emptyname); +        }  		return true;  	}  }; @@ -6058,7 +6100,12 @@ class LLAvatarResetSkeleton: public view_listener_t  {      bool handleEvent(const LLSD& userdata)      { -		LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); +		LLVOAvatar* avatar = NULL; +        LLViewerObject *obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); +        if (obj) +        { +            avatar = obj->getAvatar(); +        }  		if(avatar)          {              avatar->resetSkeleton(false); @@ -6067,6 +6114,20 @@ class LLAvatarResetSkeleton: public view_listener_t      }  }; +class LLAvatarEnableResetSkeleton: public view_listener_t +{ +    bool handleEvent(const LLSD& userdata) +    { +        LLViewerObject *obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); +        if (obj && obj->getAvatar()) +        { +            return true; +        } +        return false; +    } +}; + +  class LLAvatarResetSkeletonAndAnimations : public view_listener_t  {  	bool handleEvent(const LLSD& userdata) @@ -6900,7 +6961,7 @@ class LLAttachmentEnableDrop : public view_listener_t  		// Do not enable drop if all faces of object are not enabled  		if (object && LLSelectMgr::getInstance()->getSelection()->contains(object,SELECT_ALL_TES ))  		{ -    		S32 attachmentID  = ATTACHMENT_ID_FROM_STATE(object->getState()); +    		S32 attachmentID  = ATTACHMENT_ID_FROM_STATE(object->getAttachmentState());  			attachment = get_if_there(gAgentAvatarp->mAttachmentPoints, attachmentID, (LLViewerJointAttachment*)NULL);  			if (attachment) @@ -9109,6 +9170,7 @@ void initialize_menus()  	view_listener_t::addMenu(new LLAvatarReportAbuse(), "Avatar.ReportAbuse");  	view_listener_t::addMenu(new LLAvatarToggleMyProfile(), "Avatar.ToggleMyProfile");  	view_listener_t::addMenu(new LLAvatarResetSkeleton(), "Avatar.ResetSkeleton"); +	view_listener_t::addMenu(new LLAvatarEnableResetSkeleton(), "Avatar.EnableResetSkeleton");  	view_listener_t::addMenu(new LLAvatarResetSkeletonAndAnimations(), "Avatar.ResetSkeletonAndAnimations");  	enable.add("Avatar.IsMyProfileOpen", boost::bind(&my_profile_visible)); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 0c5bd9ad1d..981d226824 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -54,6 +54,7 @@  #include "llagentcamera.h"  #include "llcallingcard.h"  #include "llbuycurrencyhtml.h" +#include "llcontrolavatar.h"  #include "llfirstuse.h"  #include "llfloaterbump.h"  #include "llfloaterbuyland.h" @@ -102,6 +103,7 @@  #include "llviewerwindow.h"  #include "llvlmanager.h"  #include "llvoavatarself.h" +#include "llvovolume.h"  #include "llworld.h"  #include "pipeline.h"  #include "llfloaterworldmap.h" @@ -4023,23 +4025,27 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)  	LLUUID	animation_id;  	LLUUID	uuid;  	S32		anim_sequence_id; -	LLVOAvatar *avatarp; +	LLVOAvatar *avatarp = NULL;  	mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); -	//clear animation flags -	avatarp = (LLVOAvatar *)gObjectList.findObject(uuid); +	LLViewerObject *objp = gObjectList.findObject(uuid); +    if (objp) +    { +        avatarp =  objp->asAvatar(); +    }  	if (!avatarp)  	{  		// no agent by this ID...error? -		LL_WARNS("Messaging") << "Received animation state for unknown avatar" << uuid << LL_ENDL; +		LL_WARNS("Messaging") << "Received animation state for unknown avatar " << uuid << LL_ENDL;  		return;  	}  	S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList);  	S32 num_source_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationSourceList); +	//clear animation flags  	avatarp->mSignaledAnimations.clear();  	if (avatarp->isSelf()) @@ -4110,6 +4116,72 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)  	}  } + +void process_object_animation(LLMessageSystem *mesgsys, void **user_data) +{ +	LLUUID	animation_id; +	LLUUID	uuid; +	S32		anim_sequence_id; +	 +	mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); + +    LL_DEBUGS("AnimatedObjectsNotify") << "Received animation state for object " << uuid << LL_ENDL; + +    signaled_animation_map_t signaled_anims; +	S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList); +	LL_DEBUGS("AnimatedObjectsNotify") << "processing object animation requests, num_blocks " << num_blocks << " uuid " << uuid << LL_ENDL; +    for( S32 i = 0; i < num_blocks; i++ ) +    { +        mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i); +        mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i); +        signaled_anims[animation_id] = anim_sequence_id; +        LL_DEBUGS("AnimatedObjectsNotify") << "added signaled_anims animation request for object "  +                                    << uuid << " animation id " << animation_id << LL_ENDL; +    } +    LLObjectSignaledAnimationMap::instance().getMap()[uuid] = signaled_anims; +     +    LLViewerObject *objp = gObjectList.findObject(uuid); +    if (!objp) +    { +		LL_DEBUGS("AnimatedObjectsNotify") << "Received animation state for unknown object " << uuid << LL_ENDL; +        return; +    } +     +	LLVOVolume *volp = dynamic_cast<LLVOVolume*>(objp); +    if (!volp) +    { +		LL_DEBUGS("AnimatedObjectsNotify") << "Received animation state for non-volume object " << uuid << LL_ENDL; +        return; +    } + +    if (!volp->isAnimatedObject()) +    { +		LL_DEBUGS("AnimatedObjectsNotify") << "Received animation state for non-animated object " << uuid << LL_ENDL; +        return; +    } + +    volp->updateControlAvatar(); +    LLControlAvatar *avatarp = volp->getControlAvatar(); +    if (!avatarp) +    { +        LL_DEBUGS("AnimatedObjectsNotify") << "Received animation request for object with no control avatar, ignoring " << uuid << LL_ENDL; +        return; +    } +     +    if (!avatarp->mPlaying) +    { +        avatarp->mPlaying = true; +        //if (!avatarp->mRootVolp->isAnySelected()) +        { +            avatarp->updateVolumeGeom(); +            avatarp->mRootVolp->recursiveMarkForUpdate(TRUE); +        } +    } +         +    avatarp->updateAnimations(); +} + +  void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data)  {  	LLUUID uuid; diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index 913abef2be..2d6636f30d 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -100,6 +100,7 @@ void process_health_message(LLMessageSystem *mesgsys, void **user_data);  void process_sim_stats(LLMessageSystem *mesgsys, void **user_data);  void process_shooter_agent_hit(LLMessageSystem* msg, void** user_data);  void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data); +void process_object_animation(LLMessageSystem *mesgsys, void **user_data);  void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data);  void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data);  void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 955cc79283..dcd09f66c7 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -60,6 +60,7 @@  #include "llbbox.h"  #include "llbox.h"  #include "llcylinder.h" +#include "llcontrolavatar.h"  #include "lldrawable.h"  #include "llface.h"  #include "llfloaterproperties.h" @@ -69,6 +70,7 @@  #include "llselectmgr.h"  #include "llrendersphere.h"  #include "lltooldraganddrop.h" +#include "lluiavatar.h"  #include "llviewercamera.h"  #include "llviewertexturelist.h"  #include "llviewerinventory.h" @@ -103,6 +105,8 @@  #include "llfloaterperms.h"  #include "llvocache.h"  #include "llcleanup.h" +#include "llcallstack.h" +#include "llmeshrepository.h"  //#define DEBUG_UPDATE_TYPE @@ -139,8 +143,11 @@ const U32 MAX_INV_FILE_READ_FAILS = 25;  static LLTrace::BlockTimerStatHandle FTM_CREATE_OBJECT("Create Object");  // static -LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) +LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp, S32 flags)  { +    LL_DEBUGS("ObjectUpdate") << "creating " << id << LL_ENDL; +    dumpStack("ObjectUpdateStack"); +      	LLViewerObject *res = NULL;  	LL_RECORD_BLOCK_TIME(FTM_CREATE_OBJECT); @@ -167,6 +174,18 @@ LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pco  			}  			res = gAgentAvatarp;  		} +		else if (flags & CO_FLAG_CONTROL_AVATAR) +		{ +            LLControlAvatar *control_avatar = new LLControlAvatar(id, pcode, regionp); +			control_avatar->initInstance(); +			res = control_avatar; +		} +        else if (flags & CO_FLAG_UI_AVATAR) +        { +            LLUIAvatar *ui_avatar = new LLUIAvatar(id, pcode, regionp); +            ui_avatar->initInstance(); +            res = ui_avatar; +        }  		else  		{  			LLVOAvatar *avatar = new LLVOAvatar(id, pcode, regionp);  @@ -236,6 +255,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe  	mText(),  	mHudText(""),  	mHudTextColor(LLColor4::white), +    mControlAvatar(NULL),  	mLastInterpUpdateSecs(0.f),  	mLastMessageUpdateSecs(0.f),  	mLatestRecvPacketID(0), @@ -260,7 +280,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe  	mRotTime(0.f),  	mAngularVelocityRot(),  	mPreviousRotation(), -	mState(0), +	mAttachmentState(0),  	mMedia(NULL),  	mClickAction(0),  	mObjectCost(0), @@ -366,17 +386,23 @@ void LLViewerObject::markDead()  		//LL_INFOS() << "Marking self " << mLocalID << " as dead." << LL_ENDL;  		// Root object of this hierarchy unlinks itself. -		LLVOAvatar *av = getAvatarAncestor();  		if (getParent())  		{  			((LLViewerObject *)getParent())->removeChild(this);  		}  		LLUUID mesh_id; -		if (av && LLVOAvatar::getRiggedMeshID(this,mesh_id)) -		{ -			// This case is needed for indirectly attached mesh objects. -			av->resetJointsOnDetach(mesh_id); -		} +        { +            LLVOAvatar *av = getAvatar(); +            if (av && LLVOAvatar::getRiggedMeshID(this,mesh_id)) +            { +                // This case is needed for indirectly attached mesh objects. +                av->updateAttachmentOverrides(); +            } +        } +        if (getControlAvatar()) +        { +            unlinkControlAvatar(); +        }  		// Mark itself as dead  		mDead = TRUE; @@ -677,6 +703,18 @@ void LLViewerObject::setNameValueList(const std::string& name_value_list)  	}  } +BOOL LLViewerObject::isAnySelected() const +{ +    bool any_selected = isSelected(); +    for (child_list_t::const_iterator iter = mChildList.begin(); +         iter != mChildList.end(); iter++) +    { +        const LLViewerObject* child = *iter; +        any_selected = any_selected || child->isSelected(); +    } +    return any_selected; +} +  void LLViewerObject::setSelected(BOOL sel)  {  	mUserSelected = sel; @@ -849,9 +887,18 @@ void LLViewerObject::addChild(LLViewerObject *childp)  	if(childp->setParent(this))  	{  		mChildList.push_back(childp); +        childp->afterReparent();  	}  } +void LLViewerObject::onReparent(LLViewerObject *old_parent, LLViewerObject *new_parent) +{ +} + +void LLViewerObject::afterReparent() +{ +} +  void LLViewerObject::removeChild(LLViewerObject *childp)  {  	for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i) @@ -1068,6 +1115,9 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,  {  	LL_DEBUGS_ONCE("SceneLoadTiming") << "Received viewer object data" << LL_ENDL; +    LL_DEBUGS("ObjectUpdate") << " mesgsys " << mesgsys << " dp " << dp << " id " << getID() << " update_type " << (S32) update_type << LL_ENDL; +    dumpStack("ObjectUpdateStack"); +  	U32 retval = 0x0;  	// If region is removed from the list it is also deleted. @@ -1122,10 +1172,10 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,  	F32 time_dilation = 1.f;  	if(mesgsys != NULL)  	{ -	U16 time_dilation16; -	mesgsys->getU16Fast(_PREHASH_RegionData, _PREHASH_TimeDilation, time_dilation16); -	time_dilation = ((F32) time_dilation16) / 65535.f; -	mRegionp->setTimeDilation(time_dilation); +        U16 time_dilation16; +        mesgsys->getU16Fast(_PREHASH_RegionData, _PREHASH_TimeDilation, time_dilation16); +        time_dilation = ((F32) time_dilation16) / 65535.f; +        mRegionp->setTimeDilation(time_dilation);  	}  	// this will be used to determine if we've really changed position @@ -1381,7 +1431,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,  				U8 state;  				mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); -				mState = state; +				mAttachmentState = state;  				// ...new objects that should come in selected need to be added to the selected list  				mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0); @@ -1651,7 +1701,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,  				U8 state;  				mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); -				mState = state; +				mAttachmentState = state;  				break;  			} @@ -1674,7 +1724,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,  		U8		state;  		dp->unpackU8(state, "State"); -		mState = state; +		mAttachmentState = state;  		switch(update_type)  		{ @@ -2907,6 +2957,131 @@ void LLViewerObject::fetchInventoryFromServer()  	}  } +LLControlAvatar *LLViewerObject::getControlAvatar() +{ +    return getRootEdit()->mControlAvatar.get(); +} + +LLControlAvatar *LLViewerObject::getControlAvatar() const +{ +    return getRootEdit()->mControlAvatar.get(); +} + +// Manage the control avatar state of a given object. +// Any object can be flagged as animated, but for performance reasons +// we don't want to incur the overhead of managing a control avatar +// unless this would have some user-visible consequence. That is, +// there should be at least one rigged mesh in the linkset. Operations +// that change the state of a linkset, such as linking or unlinking +// prims, can also mean that a control avatar needs to be added or +// removed. At the end, if there is a control avatar, we make sure +// that its animation state is current. +void LLViewerObject::updateControlAvatar() +{ +    LLViewerObject *root = getRootEdit(); +    bool is_animated_object = root->isAnimatedObject(); +    bool has_control_avatar = getControlAvatar(); +    if (!is_animated_object && !has_control_avatar) +    { +        return; +    } + +    bool should_have_control_avatar = false; +    if (is_animated_object) +    { +        bool any_rigged_mesh = root->isRiggedMesh(); +        LLViewerObject::const_child_list_t& child_list = root->getChildren(); +        for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin(); +             iter != child_list.end(); ++iter) +        { +            const LLViewerObject* child = *iter; +            any_rigged_mesh = any_rigged_mesh || child->isRiggedMesh(); +        } +        should_have_control_avatar = is_animated_object && any_rigged_mesh; +    } + +    if (should_have_control_avatar && !has_control_avatar) +    { +        std::string vobj_name = llformat("Vol%p", root); +        LL_DEBUGS("AnimatedObjects") << vobj_name << " calling linkControlAvatar()" << LL_ENDL; +        root->linkControlAvatar(); +    } +    if (!should_have_control_avatar && has_control_avatar) +    { +        std::string vobj_name = llformat("Vol%p", root); +        LL_DEBUGS("AnimatedObjects") << vobj_name << " calling unlinkControlAvatar()" << LL_ENDL; +        root->unlinkControlAvatar(); +    } +    if (getControlAvatar()) +    { +        getControlAvatar()->updateAnimations(); +        if (isSelected()) +        { +            LLSelectMgr::getInstance()->pauseAssociatedAvatars(); +        } +    } +} + +void LLViewerObject::linkControlAvatar() +{ +    if (!getControlAvatar() && isRootEdit()) +    { +        LLVOVolume *volp = dynamic_cast<LLVOVolume*>(this); +        if (!volp) +        { +            LL_WARNS() << "called with null or non-volume object" << LL_ENDL; +            return; +        } +        mControlAvatar = LLControlAvatar::createControlAvatar(volp); +        LL_DEBUGS("AnimatedObjects") << volp->getID()  +                                     << " created control av for "  +                                     << (S32) (1+volp->numChildren()) << " prims" << LL_ENDL; +    } +    LLControlAvatar *cav = getControlAvatar(); +    if (cav) +    { +        cav->updateAttachmentOverrides(); +        if (!cav->mPlaying) +        { +            cav->mPlaying = true; +            //if (!cav->mRootVolp->isAnySelected()) +            { +                cav->updateVolumeGeom(); +                cav->mRootVolp->recursiveMarkForUpdate(TRUE); +            } +        } +    } +    else +    { +        LL_WARNS() << "no control avatar found!" << LL_ENDL; +    } +} + +void LLViewerObject::unlinkControlAvatar() +{ +    if (getControlAvatar()) +    { +        getControlAvatar()->updateAttachmentOverrides(); +    } +    if (isRootEdit()) +    { +        // This will remove the entire linkset from the control avatar +        if (mControlAvatar) +        { +            mControlAvatar->markForDeath(); +            mControlAvatar = NULL; +        } +    } +    // For non-root prims, removing from the linkset will +    // automatically remove the control avatar connection. +} + +// virtual +bool LLViewerObject::isAnimatedObject() const +{ +    return false; +} +  struct LLFilenameAndTask  {  	LLUUID mTaskID; @@ -3531,11 +3706,66 @@ F32 LLViewerObject::getLinksetPhysicsCost()  	return mLinksetPhysicsCost;  } -F32 LLViewerObject::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const +F32 LLViewerObject::recursiveGetEstTrianglesMax() const +{ +    F32 est_tris = getEstTrianglesMax(); +    for (child_list_t::const_iterator iter = mChildList.begin(); +         iter != mChildList.end(); iter++) +    { +        const LLViewerObject* child = *iter; +        if (!child->isAvatar()) +        { +            est_tris += child->recursiveGetEstTrianglesMax(); +        } +    } +    return est_tris; +} + +S32 LLViewerObject::getAnimatedObjectMaxTris() const +{ +    S32 max_tris = 0; +    if (gSavedSettings.getBOOL("AnimatedObjectsIgnoreLimits"))  +    { +        max_tris = S32_MAX; +    } +    else +    { +        if (gAgent.getRegion()) +        { +            LLSD features; +            gAgent.getRegion()->getSimulatorFeatures(features); +            if (features.has("AnimatedObjects")) +            { +                max_tris = features["AnimatedObjects"]["AnimatedObjectMaxTris"].asInteger(); +            } +        } +    } +    return max_tris; +} + +F32 LLViewerObject::getEstTrianglesMax() const +{ +    return 0.f; +} + +F32 LLViewerObject::getEstTrianglesStreamingCost() const +{ +    return 0.f; +} + +// virtual +F32 LLViewerObject::getStreamingCost() const  {  	return 0.f;  } +// virtual +bool LLViewerObject::getCostData(LLMeshCostData& costs) const +{ +    costs = LLMeshCostData(); +    return false; +} +  U32 LLViewerObject::getTriangleCount(S32* vcount) const  {  	return 0; @@ -3546,6 +3776,58 @@ U32 LLViewerObject::getHighLODTriangleCount()  	return 0;  } +U32 LLViewerObject::recursiveGetTriangleCount(S32* vcount) const +{ +    S32 total_tris = getTriangleCount(vcount); +    LLViewerObject::const_child_list_t& child_list = getChildren(); +    for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin(); +         iter != child_list.end(); ++iter) +    { +        LLViewerObject* childp = *iter; +        if (childp) +        { +            total_tris += childp->getTriangleCount(vcount); +        } +    } +    return total_tris; +} + +// This is using the stored surface area for each volume (which +// defaults to 1.0 for the case of everything except a sculpt) and +// then scaling it linearly based on the largest dimension in the +// prim's scale. Should revisit at some point. +F32 LLViewerObject::recursiveGetScaledSurfaceArea() const +{ +    F32 area = 0.f; +    const LLDrawable* drawable = mDrawable; +    if (drawable) +    { +        const LLVOVolume* volume = drawable->getVOVolume(); +        if (volume) +        { +            if (volume->getVolume()) +            { +				const LLVector3& scale = volume->getScale(); +                area += volume->getVolume()->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]); +            } +            LLViewerObject::const_child_list_t children = volume->getChildren(); +            for (LLViewerObject::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 && child->getVolume()) +                { +                    const LLVector3& scale = child->getScale(); +                    area += child->getVolume()->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]); +                } +            } +        } +    } +    return area; +} +  void LLViewerObject::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)  {  	LLVector4a center; @@ -3649,7 +3931,6 @@ void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */)  	}  } -  void LLViewerObject::setLineWidthForWindowSize(S32 window_width)  {  	if (window_width < 700) @@ -3876,8 +4157,20 @@ const LLVector3 LLViewerObject::getRenderPosition() const  {  	if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED))  	{ +        LLControlAvatar *cav = getControlAvatar(); +        if (isRoot() && cav) +        { +            F32 fixup; +            if ( cav->hasPelvisFixup( fixup) ) +            { +                //Apply a pelvis fixup (as defined by the avs skin) +                LLVector3 pos = mDrawable->getPositionAgent(); +                pos[VZ] += fixup; +                return pos; +            } +        }  		LLVOAvatar* avatar = getAvatar(); -		if (avatar) +		if ((avatar) && !getControlAvatar())  		{  			return avatar->getPositionAgent();  		} @@ -3901,7 +4194,7 @@ const LLVector3 LLViewerObject::getPivotPositionAgent() const  const LLQuaternion LLViewerObject::getRenderRotation() const  {  	LLQuaternion ret; -	if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED)) +	if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED) && !isAnimatedObject())  	{  		return ret;  	} @@ -5163,7 +5456,13 @@ LLVOAvatar* LLViewerObject::asAvatar()  	return NULL;  } -// If this object is directly or indirectly parented by an avatar, return it. +// If this object is directly or indirectly parented by an avatar, +// return it.  Normally getAvatar() is the correct function to call; +// it will give the avatar used for skinning.  The exception is with +// animated objects that are also attachments; in that case, +// getAvatar() will return the control avatar, used for skinning, and +// getAvatarAncestor will return the avatar to which the object is +// attached.  LLVOAvatar* LLViewerObject::getAvatarAncestor()  {  	LLViewerObject *pobj = (LLViewerObject*) getParent(); @@ -5502,6 +5801,11 @@ LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 para  		  new_block = new LLLightImageParams();  		  break;  	  } +      case LLNetworkData::PARAMS_EXTENDED_MESH: +      { +		  new_block = new LLExtendedMeshParams(); +		  break; +      }  	  default:  	  {  		  LL_INFOS() << "Unknown param type." << LL_ENDL; @@ -5901,6 +6205,17 @@ void LLViewerObject::updateVolume(const LLVolumeParams& volume_params)  	}  } +void LLViewerObject::recursiveMarkForUpdate(BOOL priority) +{ +    for (LLViewerObject::child_list_t::iterator iter = mChildList.begin(); +         iter != mChildList.end(); iter++) +    { +        LLViewerObject* child = *iter; +        child->markForUpdate(priority); +    } +    markForUpdate(priority); +} +  void LLViewerObject::markForUpdate(BOOL priority)  {  	if (mDrawable.notNull()) @@ -5960,6 +6275,11 @@ void LLViewerObject::setRegion(LLViewerRegion *regionp)  		child->setRegion(regionp);  	} +    if (mControlAvatar) +    { +        mControlAvatar->setRegion(regionp); +    } +  	setChanged(MOVED | SILHOUETTE);  	updateDrawable(FALSE);  } @@ -6408,6 +6728,10 @@ const std::string& LLViewerObject::getAttachmentItemName() const  //virtual  LLVOAvatar* LLViewerObject::getAvatar() const  { +    if (getControlAvatar()) +    { +        return getControlAvatar(); +    }  	if (isAttachment())  	{  		LLViewerObject* vobj = (LLViewerObject*) getParent(); diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index d61832c2ad..d6c8b76147 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -42,11 +42,13 @@  #include "v3math.h"  #include "llvertexbuffer.h"  #include "llbbox.h" +#include "llrigginginfo.h"  class LLAgent;			// TODO: Get rid of this.  class LLAudioSource;  class LLAudioSourceVO;  class LLColor4; +class LLControlAvatar;  class LLDataPacker;  class LLDataPackerBinaryBuffer;  class LLDrawable; @@ -67,6 +69,8 @@ class LLViewerRegion;  class LLViewerTexture;  class LLWorld; +class LLMeshCostData; +  typedef enum e_object_update_type  {  	OUT_FULL, @@ -220,6 +224,8 @@ public:  	LLViewerRegion* getRegion() const				{ return mRegionp; }  	BOOL isSelected() const							{ return mUserSelected; } +    // Check whole linkset +    BOOL isAnySelected() const;  	virtual void setSelected(BOOL sel);  	const LLUUID &getID() const						{ return mID; } @@ -231,6 +237,7 @@ public:  	virtual BOOL isFlexible() const					{ return FALSE; }  	virtual BOOL isSculpted() const 				{ return FALSE; }  	virtual BOOL isMesh() const						{ return FALSE; } +	virtual BOOL isRiggedMesh() const				{ return FALSE; }  	virtual BOOL hasLightTexture() const			{ return FALSE; }  	// This method returns true if the object is over land owned by @@ -255,6 +262,8 @@ public:  	*/  	virtual BOOL setParent(LLViewerObject* parent); +    virtual void onReparent(LLViewerObject *old_parent, LLViewerObject *new_parent); +    virtual void afterReparent();  	virtual void addChild(LLViewerObject *childp);  	virtual void removeChild(LLViewerObject *childp);  	const_child_list_t& getChildren() const { 	return mChildList; } @@ -356,9 +365,17 @@ public:  	virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE); -	virtual F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL, F32* unscaled_value = NULL) const; +    S32 getAnimatedObjectMaxTris() const; +    F32 recursiveGetEstTrianglesMax() const; +    virtual F32 getEstTrianglesMax() const; +    virtual F32 getEstTrianglesStreamingCost() const; +	virtual F32 getStreamingCost() const; +    virtual bool getCostData(LLMeshCostData& costs) const;  	virtual U32 getTriangleCount(S32* vcount = NULL) const;  	virtual U32 getHighLODTriangleCount(); +    F32 recursiveGetScaledSurfaceArea() const; + +    U32 recursiveGetTriangleCount(S32* vcount = NULL) const;  	void setObjectCost(F32 cost);  	F32 getObjectCost(); @@ -374,7 +391,7 @@ public:  	void sendShapeUpdate(); -	U8 getState()							{ return mState; } +	U8 getAttachmentState()							{ return mAttachmentState; }  	F32 getAppAngle() const					{ return mAppAngle; }  	F32 getPixelArea() const				{ return mPixelArea; } @@ -411,7 +428,8 @@ public:  	void setIcon(LLViewerTexture* icon_image);  	void clearIcon(); -	void markForUpdate(BOOL priority); +    void recursiveMarkForUpdate(BOOL priority); +	virtual void markForUpdate(BOOL priority);  	void markForUnload(BOOL priority);  	void updateVolume(const LLVolumeParams& volume_params);  	virtual	void updateSpatialExtents(LLVector4a& min, LLVector4a& max); @@ -686,6 +704,27 @@ public:  	static			BOOL		sUseSharedDrawables; +public: +    // Returns mControlAvatar for the edit root prim of this linkset +    LLControlAvatar *getControlAvatar(); +    LLControlAvatar *getControlAvatar() const; + +    // Create or connect to an existing control av as applicable +    void linkControlAvatar(); +    // Remove any reference to control av for this prim +    void unlinkControlAvatar(); +    // Link or unlink as needed +    void updateControlAvatar(); + +    virtual bool isAnimatedObject() const; + +    // Flags for createObject +    static const S32 CO_FLAG_CONTROL_AVATAR = 1 << 0; +    static const S32 CO_FLAG_UI_AVATAR = 1 << 1; + +protected: +    LLPointer<LLControlAvatar> mControlAvatar; +  protected:  	// delete an item in the inventory, but don't tell the  	// server. This is used internally by remove, update, and @@ -696,8 +735,7 @@ protected:  	// updateInventory.  	void doUpdateInventory(LLPointer<LLViewerInventoryItem>& item, U8 key, bool is_new); - -	static LLViewerObject *createObject(const LLUUID &id, LLPCode pcode, LLViewerRegion *regionp); +	static LLViewerObject *createObject(const LLUUID &id, LLPCode pcode, LLViewerRegion *regionp, S32 flags = 0);  	BOOL setData(const U8 *datap, const U32 data_size); @@ -785,7 +823,7 @@ protected:  	LLQuaternion	mAngularVelocityRot;		// accumulated rotation from the angular velocity computations  	LLQuaternion	mPreviousRotation; -	U8				mState;	// legacy +	U8				mAttachmentState;	// this encodes the attachment id in a somewhat complex way. 0 if not an attachment.  	LLViewerObjectMedia* mMedia;	// NULL if no media associated  	U8 mClickAction;  	F32 mObjectCost; //resource cost of this object or -1 if unknown @@ -841,6 +879,10 @@ public:  	BOOL getLastUpdateCached() const;  	void setLastUpdateCached(BOOL last_update_cached); +    virtual void updateRiggingInfo() {} + +    LLJointRiggingInfoTab mJointRiggingInfoTab; +  private:  	LLUUID mAttachmentItemID; // ItemID of the associated object is in user inventory.  	EObjectUpdateType	mLastUpdateType; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 23a51b99f6..fc06455c68 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -68,6 +68,7 @@  #include "u64.h"  #include "llviewertexturelist.h"  #include "lldatapacker.h" +#include "llcallstack.h"  #ifdef LL_USESYSTEMLIBS  #include <zlib.h>  #else @@ -241,6 +242,10 @@ void LLViewerObjectList::processUpdateCore(LLViewerObject* objectp,  	}  	// ignore returned flags +    LL_DEBUGS("ObjectUpdate") << "uuid " << objectp->mID << " calling processUpdateMessage "  +                              << objectp << " just_created " << just_created << " from_cache " << from_cache << " msg " << msg << LL_ENDL; +    dumpStack("ObjectUpdateStack"); +	 	  	objectp->processUpdateMessage(msg, user_data, i, update_type, dpp);  	if (objectp->isDead()) @@ -352,7 +357,10 @@ LLViewerObject* LLViewerObjectList::processObjectUpdateFromCache(LLVOCacheEntry*  	if (!objectp)  	{  		objectp = createObjectFromCache(pcode, regionp, fullid, entry->getLocalID()); -		 + +        LL_DEBUGS("ObjectUpdate") << "uuid " << fullid << " created objectp " << objectp << LL_ENDL; +        dumpStack("ObjectUpdateStack"); +	 	  		if (!objectp)  		{  			LL_INFOS() << "createObject failure for object: " << fullid << LL_ENDL; @@ -471,6 +479,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			compressed_dp.reset();  			uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data); +            LL_DEBUGS("ObjectUpdate") << "got binary data from message to compressed_dpbuffer" << LL_ENDL;  			mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compressed_dpbuffer, 0, i);  			compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length); @@ -530,6 +539,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  				// LL_WARNS() << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << LL_ENDL;  				mNumUnknownUpdates++;  			} +            else +            { +                LL_DEBUGS("ObjectUpdate") << "Non-full, non-compressed update, obj " << local_id << ", global ID " << fullid << " from " << mesgsys->getSender() << LL_ENDL; +            }  		}  		else // OUT_FULL only?  		{ @@ -538,10 +551,19 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);  			msg_size += sizeof(LLUUID);  			msg_size += sizeof(U32); -			// LL_INFOS() << "Full Update, obj " << local_id << ", global ID" << fullid << "from " << mesgsys->getSender() << LL_ENDL; +			LL_DEBUGS("ObjectUpdate") << "Full Update, obj " << local_id << ", global ID " << fullid << " from " << mesgsys->getSender() << LL_ENDL;  		}  		objectp = findObject(fullid); +        if (compressed) +        { +            LL_DEBUGS("ObjectUpdate") << "uuid " << fullid << " received compressed data from message (earlier in function)" << LL_ENDL; +        } +        LL_DEBUGS("ObjectUpdate") << "uuid " << fullid << " objectp " << objectp  +                                     << " update_cache " << (S32) update_cache << " compressed " << compressed +                                     << " update_type "  << update_type << LL_ENDL; +        dumpStack("ObjectUpdateStack"); +          		if(update_cache)  		{  			objectp = regionp->updateCacheEntry(local_id, objectp, update_type); @@ -616,6 +638,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  #endif  			objectp = createObject(pcode, regionp, fullid, local_id, gMessageSystem->getSender()); + +            LL_DEBUGS("ObjectUpdate") << "creating object " << fullid << " result " << objectp << LL_ENDL; +            dumpStack("ObjectUpdateStack"); +  			if (!objectp)  			{  				LL_INFOS() << "createObject failure for object: " << fullid << LL_ENDL; @@ -710,12 +736,17 @@ void LLViewerObjectList::processCachedObjectUpdate(LLMessageSystem *mesgsys,  		mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_CRC, crc, i);  		mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i);  		msg_size += sizeof(U32) * 2; -		 + +        LL_DEBUGS("ObjectUpdate") << "got probe for id " << id << " crc " << crc << LL_ENDL; +        dumpStack("ObjectUpdateStack"); +  		// Lookup data packer and add this id to cache miss lists if necessary.  		U8 cache_miss_type = LLViewerRegion::CACHE_MISS_TYPE_NONE;  		if(!regionp->probeCache(id, crc, flags, cache_miss_type))  		{  			// Cache Miss. +            LL_DEBUGS("ObjectUpdate") << "cache miss for id " << id << " crc " << crc << " miss type " << (S32) cache_miss_type << LL_ENDL; +  			recorder.cacheMissEvent(id, update_type, cache_miss_type, msg_size);  			continue; // no data packer, skip this object @@ -876,8 +907,8 @@ void LLViewerObjectList::update(LLAgent &agent)  			{  				if (idle_count >= idle_list.size())  				{ -				idle_list.push_back( objectp ); -			} +                    idle_list.push_back( objectp ); +                }  			else  				{  					idle_list[idle_count] = objectp; @@ -914,7 +945,7 @@ void LLViewerObjectList::update(LLAgent &agent)  		{  			objectp = *idle_iter;  			llassert(objectp->isActive()); -			objectp->idleUpdate(agent, frame_time); +                objectp->idleUpdate(agent, frame_time);  		}  		//update flexible objects @@ -1274,6 +1305,9 @@ void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp)  	// Cleanup any references we have to this object  	// Remove from object map so noone can look it up. +    LL_DEBUGS("ObjectUpdate") << " dereferencing id " << objectp->mID << LL_ENDL; +    dumpStack("ObjectUpdateStack"); +      	mUUIDObjectMap.erase(objectp->mID);  	//if (objectp->getRegion()) @@ -1960,12 +1994,12 @@ void LLViewerObjectList::resetObjectBeacons()  	mDebugBeacons.clear();  } -LLViewerObject *LLViewerObjectList::createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp) +LLViewerObject *LLViewerObjectList::createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp, S32 flags)  {  	LLUUID fullid;  	fullid.generate(); -	LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp); +	LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp, flags);  	if (!objectp)  	{  // 		LL_WARNS() << "Couldn't create object of type " << LLPrimitive::pCodeToString(pcode) << LL_ENDL; @@ -1985,6 +2019,9 @@ LLViewerObject *LLViewerObjectList::createObjectFromCache(const LLPCode pcode, L  {  	llassert_always(uuid.notNull()); +    LL_DEBUGS("ObjectUpdate") << "creating " << uuid << " local_id " << local_id << LL_ENDL; +    dumpStack("ObjectUpdateStack"); +      	LLViewerObject *objectp = LLViewerObject::createObject(uuid, pcode, regionp);  	if (!objectp)  	{ @@ -2019,6 +2056,9 @@ LLViewerObject *LLViewerObjectList::createObject(const LLPCode pcode, LLViewerRe  		fullid = uuid;  	} +    LL_DEBUGS("ObjectUpdate") << "createObject creating " << fullid << LL_ENDL; +    dumpStack("ObjectUpdateStack"); +  	LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp);  	if (!objectp)  	{ diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index 94c751acc6..72b2b99004 100644 --- a/indra/newview/llviewerobjectlist.h +++ b/indra/newview/llviewerobjectlist.h @@ -67,7 +67,7 @@ public:  	inline LLViewerObject *getObject(const S32 index);  	inline LLViewerObject *findObject(const LLUUID &id); -	LLViewerObject *createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp); // Create a viewer-side object +	LLViewerObject *createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp, S32 flags = 0); // Create a viewer-side object  	LLViewerObject *createObjectFromCache(const LLPCode pcode, LLViewerRegion *regionp, const LLUUID &uuid, const U32 local_id);  	LLViewerObject *createObject(const LLPCode pcode, LLViewerRegion *regionp,  								 const LLUUID &uuid, const U32 local_id, const LLHost &sender); diff --git a/indra/newview/llvieweroctree.cpp b/indra/newview/llvieweroctree.cpp index 5f0e21db71..00a652384c 100644 --- a/indra/newview/llvieweroctree.cpp +++ b/indra/newview/llvieweroctree.cpp @@ -731,7 +731,7 @@ bool LLViewerOctreeGroup::boundObjects(BOOL empty, LLVector4a& minOut, LLVector4  			update_min_max(newMin, newMax, minMax[0]);  			update_min_max(newMin, newMax, minMax[1]);  		} -		 +  		mObjectBounds[0].setAdd(newMin, newMax);  		mObjectBounds[0].mul(0.5f);  		mObjectBounds[1].setSub(newMax, newMin); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 4f0460da29..ba733bb068 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -78,6 +78,7 @@  #include "llcoros.h"  #include "lleventcoro.h"  #include "llcorehttputil.h" +#include "llcallstack.h"  #ifdef LL_WINDOWS  	#pragma warning(disable:4355) @@ -241,6 +242,8 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)              LL_WARNS("AppInit", "Capabilities") << "Attempting to get capabilities for region that no longer exists!" << LL_ENDL;              return; // this error condition is not recoverable.          } +        LL_DEBUGS("AppInit", "Capabilities") << "requesting seed caps for handle " << regionHandle  +                                             << " name " << regionp->getName() << LL_ENDL;          std::string url = regionp->getCapability("Seed");          if (url.empty()) @@ -269,7 +272,8 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)          buildCapabilityNames(capabilityNames);          LL_INFOS("AppInit", "Capabilities") << "Requesting seed from " << url  -            << " (attempt #" << mSeedCapAttempts + 1 << ")" << LL_ENDL; +                                            << " region name " << regionp->getName() +                                            << " (attempt #" << mSeedCapAttempts + 1 << ")" << LL_ENDL;          regionp = NULL;          result = httpAdapter->postAndSuspend(httpRequest, url, capabilityNames); @@ -323,6 +327,8 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)  #endif          regionp->setCapabilitiesReceived(true); +        LL_DEBUGS("AppInit", "Capabilities") << "received caps for handle " << regionHandle  +                                             << " region name " << regionp->getName() << LL_ENDL;          if (STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState())          { @@ -1259,7 +1265,7 @@ void LLViewerRegion::updateVisibleEntries(F32 max_time)  		LLPointer<LLViewerOctreeGroup> group = *group_iter;  		if(group->getNumRefs() < 3 || //group to be deleted  			!group->getOctreeNode() || group->isEmpty()) //group empty -{ +        {  			continue;  		} @@ -2143,6 +2149,26 @@ void LLViewerRegion::getInfo(LLSD& info)  	info["Region"]["Handle"]["y"] = (LLSD::Integer)y;  } +void LLViewerRegion::requestSimulatorFeatures() +{ +    LL_DEBUGS("SimulatorFeatures") << "region " << getName() << " ptr " << this +                                   << " trying to request SimulatorFeatures" << LL_ENDL; +    // kick off a request for simulator features +    std::string url = getCapability("SimulatorFeatures"); +    if (!url.empty()) +    { +        std::string coroname = +            LLCoros::instance().launch("LLViewerRegionImpl::requestSimulatorFeatureCoro", +                                       boost::bind(&LLViewerRegionImpl::requestSimulatorFeatureCoro, mImpl, url, getHandle())); +         +        LL_INFOS("AppInit", "SimulatorFeatures") << "Launching " << coroname << " requesting simulator features from " << url << LL_ENDL; +    } +    else +    { +        LL_WARNS("AppInit", "SimulatorFeatures") << "SimulatorFeatures cap not set" << LL_ENDL; +    } +} +  boost::signals2::connection LLViewerRegion::setSimulatorFeaturesReceivedCallback(const caps_received_signal_t::slot_type& cb)  {  	return mSimulatorFeaturesReceivedSignal.connect(cb); @@ -2174,7 +2200,7 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)  	std::stringstream str;  	LLSDSerialize::toPrettyXML(sim_features, str); -	LL_INFOS() << str.str() << LL_ENDL; +	LL_INFOS() << "region " << getName() << " "  << str.str() << LL_ENDL;  	mSimulatorFeatures = sim_features;  	setSimulatorFeaturesReceived(true); @@ -2220,7 +2246,7 @@ void LLViewerRegion::decodeBoundingInfo(LLVOCacheEntry* entry)  	{  		LLViewerRegion* old_regionp = ((LLDrawable*)entry->getEntry()->getDrawable())->getRegion();  		if(old_regionp != this && old_regionp) -{ +        {  			LLViewerObject* obj = ((LLDrawable*)entry->getEntry()->getDrawable())->getVObj();  			if(obj)  			{ @@ -2383,12 +2409,18 @@ LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLDataPackerB  		// we've seen this object before  		if (entry->getCRC() == crc)  		{ +            LL_DEBUGS("AnimatedObjects") << " got dupe for local_id " << local_id << LL_ENDL; +            dumpStack("AnimatedObjectsStack"); +  			// Record a hit  			entry->recordDupe();  			result = CACHE_UPDATE_DUPE;  		}  		else //CRC changed  		{ +            LL_DEBUGS("AnimatedObjects") << " got update for local_id " << local_id << LL_ENDL; +            dumpStack("AnimatedObjectsStack"); +  			// Update the cache entry  			entry->updateEntry(crc, dp); @@ -2399,6 +2431,9 @@ LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLDataPackerB  	}  	else  	{ +        LL_DEBUGS("AnimatedObjects") << " got first notification for local_id " << local_id << LL_ENDL; +        dumpStack("AnimatedObjectsStack"); +  		// we haven't seen this object before  		// Create new entry and add to map  		result = CACHE_UPDATE_ADDED; @@ -2503,7 +2538,7 @@ bool LLViewerRegion::probeCache(U32 local_id, U32 crc, U32 flags, U8 &cache_miss  			// Record a hit  			mRegionCacheHitCount++;  			entry->recordHit(); -		cache_miss_type = CACHE_MISS_TYPE_NONE; +            cache_miss_type = CACHE_MISS_TYPE_NONE;  			entry->setUpdateFlags(flags);  			if(entry->isState(LLVOCacheEntry::ACTIVE)) @@ -2526,12 +2561,14 @@ bool LLViewerRegion::probeCache(U32 local_id, U32 crc, U32 flags, U8 &cache_miss  			// LL_INFOS() << "CRC miss for " << local_id << LL_ENDL;  			addCacheMiss(local_id, CACHE_MISS_TYPE_CRC); +            cache_miss_type = CACHE_MISS_TYPE_CRC;  		}  	}  	else  	{  		// LL_INFOS() << "Cache miss for " << local_id << LL_ENDL;  		addCacheMiss(local_id, CACHE_MISS_TYPE_FULL); +        cache_miss_type = CACHE_MISS_TYPE_FULL;  	}  	return false; @@ -2568,6 +2605,9 @@ void LLViewerRegion::requestCacheMisses()  		msg->nextBlockFast(_PREHASH_ObjectData);  		msg->addU8Fast(_PREHASH_CacheMissType, (*iter).mType);  		msg->addU32Fast(_PREHASH_ID, (*iter).mID); + +        LL_DEBUGS("AnimatedObjects") << "Requesting cache missed object " << (*iter).mID << LL_ENDL; +          		blocks++;  		if (blocks >= 255) @@ -2873,6 +2913,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)  	capabilityNames.append("MeshUploadFlag");	  	capabilityNames.append("NavMeshGenerationStatus");  	capabilityNames.append("NewFileAgentInventory"); +	capabilityNames.append("ObjectAnimation");  	capabilityNames.append("ObjectMedia");  	capabilityNames.append("ObjectMediaNavigate");  	capabilityNames.append("ObjectNavMeshProperties"); @@ -2967,12 +3008,8 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u  	}  	else if (name == "SimulatorFeatures")  	{ -		// kick off a request for simulator features -        std::string coroname = -            LLCoros::instance().launch("LLViewerRegionImpl::requestSimulatorFeatureCoro", -            boost::bind(&LLViewerRegionImpl::requestSimulatorFeatureCoro, mImpl, url, getHandle())); - -        LL_INFOS("AppInit", "SimulatorFeatures") << "Launching " << coroname << " requesting simulator features from " << url << LL_ENDL; +        mImpl->mCapabilities["SimulatorFeatures"] = url; +        requestSimulatorFeatures();  	}  	else  	{ diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 302647215f..d5266ec873 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -305,6 +305,7 @@ public:  	bool meshUploadEnabled() const;  	// has region received its simulator features list? Requires an additional query after caps received. +    void requestSimulatorFeatures();  	void setSimulatorFeaturesReceived(bool);  	bool simulatorFeaturesReceived() const;  	boost::signals2::connection setSimulatorFeaturesReceivedCallback(const caps_received_signal_t::slot_type& cb); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 01ec703fe6..e53ccd7b8d 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -543,7 +543,14 @@ public:  								object_count++;  								S32 bytes = 0;	  								S32 visible = 0; -								cost += object->getStreamingCost(&bytes, &visible); +								cost += object->getStreamingCost(); +                                LLMeshCostData costs; +                                if (object->getCostData(costs)) +                                { +                                    bytes = costs.getSizeTotal(); +                                    visible = costs.getSizeByLOD(object->getLOD()); +                                } +  								S32 vt = 0;  								count += object->getTriangleCount(&vt);  								vcount += vt; @@ -1146,7 +1153,9 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi  				if (prim_media_dnd_enabled)  				{ -					LLPickInfo pick_info = pickImmediate( pos.mX, pos.mY,  TRUE /*BOOL pick_transparent*/, FALSE ); +					LLPickInfo pick_info = pickImmediate( pos.mX, pos.mY, +                                                          TRUE /* pick_transparent */,  +                                                          FALSE /* pick_rigged */);  					LLUUID object_id = pick_info.getObjectID();  					S32 object_face = pick_info.mObjectFace; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index b221dc7c35..1f72aed5b4 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,10 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  	mLastUpdateRequestCOFVersion(-1),  	mLastUpdateReceivedCOFVersion(-1),  	mCachedMuteListUpdateTime(0), -	mCachedInMuteList(false) +	mCachedInMuteList(false), +    mIsControlAvatar(false), +    mIsUIAvatar(false), +    mEnableDefaultMotions(true)  {  	LL_DEBUGS("AvatarRender") << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << LL_ENDL; @@ -689,6 +697,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  	mNeedsImpostorUpdate = TRUE;  	mNeedsAnimUpdate = TRUE; +	mNeedsExtentUpdate = true; +  	mImpostorDistance = 0;  	mImpostorPixelArea = 0; @@ -718,6 +728,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  	mCurrentGesticulationLevel = 0; +      	mRuthTimer.reset();  	mRuthDebugTimer.reset();  	mDebugExistenceTimer.reset(); @@ -733,8 +744,15 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  std::string LLVOAvatar::avString() const  { -	std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus()); -	return " Avatar '" + getFullname() + "' " + viz_string + " "; +    if (isControlAvatar()) +    { +        return getFullname(); +    } +    else +    { +        std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus()); +        return " Avatar '" + getFullname() + "' " + viz_string + " "; +    }  }  void LLVOAvatar::debugAvatarRezTime(std::string notification_name, std::string comment) @@ -1099,7 +1117,7 @@ void LLVOAvatar::cleanupClass()  }  // virtual -void LLVOAvatar::initInstance(void) +void LLVOAvatar::initInstance()  {  	//-------------------------------------------------------------------------  	// register motions @@ -1170,6 +1188,8 @@ void LLVOAvatar::initInstance(void)  	//VTPause();  // VTune  	mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) ); + +    mInitFlags |= 1<<1;  }  // virtual @@ -1222,8 +1242,6 @@ const LLVector3 LLVOAvatar::getRenderPosition() const  	{  		return getPosition() * mDrawable->getParent()->getRenderMatrix();  	} -	 -	  }  void LLVOAvatar::updateDrawable(BOOL force_damped) @@ -1240,6 +1258,19 @@ void LLVOAvatar::onShift(const LLVector4a& shift_vector)  void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)  { +    if (mDrawable.isNull()) +    { +        return; +    } + +    if (mNeedsExtentUpdate) +    { +        calculateSpatialExtents(newMin,newMax); +        mLastAnimExtents[0].set(newMin.getF32ptr()); +        mLastAnimExtents[1].set(newMax.getF32ptr()); +        mNeedsExtentUpdate = false; +    } +            	if (isImpostor() && !needsImpostorUpdate())  	{  		LLVector3 delta = getRenderPosition() - @@ -1250,98 +1281,192 @@ void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)  	}  	else  	{ -		getSpatialExtents(newMin,newMax); -		mLastAnimExtents[0].set(newMin.getF32ptr()); -		mLastAnimExtents[1].set(newMax.getF32ptr()); +        newMin.load3(mLastAnimExtents[0].mV); +        newMax.load3(mLastAnimExtents[1].mV);  		LLVector4a pos_group;  		pos_group.setAdd(newMin,newMax);  		pos_group.mul(0.5f);  		mImpostorOffset = LLVector3(pos_group.getF32ptr())-getRenderPosition();  		mDrawable->setPositionGroup(pos_group);  	} -	 -	  } -void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) + +static LLTrace::BlockTimerStatHandle FTM_AVATAR_EXTENT_UPDATE("Avatar Update Extent"); + +void LLVOAvatar::calculateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)  { -	LLVector4a buffer(0.25f); +    LL_RECORD_BLOCK_TIME(FTM_AVATAR_EXTENT_UPDATE); + +    S32 box_detail = gSavedSettings.getS32("AvatarBoundingBoxComplexity"); + +    // FIXME the update_min_max function used below assumes there is a +    // known starting point, but in general there isn't. Ideally the +    // box update logic should be modified to handle the no-point-yet +    // case. For most models, starting with the pelvis is safe though. +    LLVector3 zero_pos;  	LLVector4a pos; -	pos.load3(getRenderPosition().mV); -	newMin.setSub(pos, buffer); -	newMax.setAdd(pos, buffer); +    if (dist_vec(zero_pos, mPelvisp->getWorldPosition())<0.001) +    { +        // Don't use pelvis until av initialized +        pos.load3(getRenderPosition().mV); +    } +    else +    { +        pos.load3(mPelvisp->getWorldPosition().mV); +    } +	newMin = pos; +	newMax = pos; -	float max_attachment_span = get_default_max_prim_scale() * 5.0f; -	 -	//stretch bounding box by joint positions -	for (polymesh_map_t::iterator i = mPolyMeshes.begin(); i != mPolyMeshes.end(); ++i) -	{ -		LLPolyMesh* mesh = i->second; -		for (S32 joint_num = 0; joint_num < mesh->mJointRenderData.size(); joint_num++) -		{ -			LLVector4a trans; -			trans.load3( mesh->mJointRenderData[joint_num]->mWorldMatrix->getTranslation().mV); -			update_min_max(newMin, newMax, trans); -		} -	} +	//stretch bounding box by joint positions. Doing this for +	//control avs, where the polymeshes aren't maintained or +	//displayed, can give inaccurate boxes due to joints stuck at (0,0,0). +    if ((box_detail>=1) && !isControlAvatar()) +    { +        for (polymesh_map_t::iterator i = mPolyMeshes.begin(); i != mPolyMeshes.end(); ++i) +        { +            LLPolyMesh* mesh = i->second; +            for (S32 joint_num = 0; joint_num < mesh->mJointRenderData.size(); joint_num++) +            { +                LLVector4a trans; +                trans.load3( mesh->mJointRenderData[joint_num]->mWorldMatrix->getTranslation().mV); +                update_min_max(newMin, newMax, trans); +            } +        } -	LLVector4a center, size; -	center.setAdd(newMin, newMax); -	center.mul(0.5f); +    } -	size.setSub(newMax,newMin); -	size.mul(0.5f); +	// Pad bounding box for starting joint, plus polymesh if +	// applicable. Subsequent calcs should be accurate enough to not +	// need padding. +	LLVector4a padding(0.25); +	newMin.sub(padding); +	newMax.add(padding); -	mPixelArea = LLPipeline::calcPixelArea(center, size, *LLViewerCamera::getInstance()); -	//stretch bounding box by attachments -	for (attachment_map_t::iterator iter = mAttachmentPoints.begin();  -		 iter != mAttachmentPoints.end(); -		 ++iter) -	{ -		LLViewerJointAttachment* attachment = iter->second; +	//stretch bounding box by static attachments +    if (box_detail >= 2) +    { +        float max_attachment_span = get_default_max_prim_scale() * 5.0f; +	 +        for (attachment_map_t::iterator iter = mAttachmentPoints.begin();  +             iter != mAttachmentPoints.end(); +             ++iter) +        { +            LLViewerJointAttachment* attachment = iter->second; -		if (attachment->getValid()) -		{ -			for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); -				 attachment_iter != attachment->mAttachedObjects.end(); -				 ++attachment_iter) -			{ -				const LLViewerObject* attached_object = (*attachment_iter); -				if (attached_object && !attached_object->isHUDAttachment()) -				{ -					LLDrawable* drawable = attached_object->mDrawable; -					if (drawable && !drawable->isState(LLDrawable::RIGGED)) -					{ -						LLSpatialBridge* bridge = drawable->getSpatialBridge(); -						if (bridge) -						{ -							const LLVector4a* ext = bridge->getSpatialExtents(); -							LLVector4a distance; -							distance.setSub(ext[1], ext[0]); -							LLVector4a max_span(max_attachment_span); +            if (attachment->getValid()) +            { +                for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); +                     attachment_iter != attachment->mAttachedObjects.end(); +                     ++attachment_iter) +                { +                    // Don't we need to look at children of attached_object as well? +                    const LLViewerObject* attached_object = (*attachment_iter); +                    if (attached_object && !attached_object->isHUDAttachment()) +                    { +                        const LLVOVolume *vol = dynamic_cast<const LLVOVolume*>(attached_object); +                        if (vol && vol->isAnimatedObject()) +                        { +                            // Animated objects already have a bounding box in their control av, use that.  +                            // Could lag by a frame if there's no guarantee on order of processing for avatars. +                            LLControlAvatar *cav = vol->getControlAvatar(); +                            if (cav) +                            { +                                LLVector4a cav_min; +                                cav_min.load3(cav->mLastAnimExtents[0].mV); +                                LLVector4a cav_max; +                                cav_max.load3(cav->mLastAnimExtents[1].mV); +                                update_min_max(newMin,newMax,cav_min); +                                update_min_max(newMin,newMax,cav_max); +                                continue; +                            } +                        } +                        if (vol && vol->isRiggedMesh()) +                        { +                            continue; +                        } +                        LLDrawable* drawable = attached_object->mDrawable; +                        if (drawable && !drawable->isState(LLDrawable::RIGGED)) +                        { +                            LLSpatialBridge* bridge = drawable->getSpatialBridge(); +                            if (bridge) +                            { +                                const LLVector4a* ext = bridge->getSpatialExtents(); +                                LLVector4a distance; +                                distance.setSub(ext[1], ext[0]); +                                LLVector4a max_span(max_attachment_span); -							S32 lt = distance.lessThan(max_span).getGatheredBits() & 0x7; +                                S32 lt = distance.lessThan(max_span).getGatheredBits() & 0x7; -							// Only add the prim to spatial extents calculations if it isn't a megaprim. -							// max_attachment_span calculated at the start of the function  -							// (currently 5 times our max prim size)  -							if (lt == 0x7) -							{ -								update_min_max(newMin,newMax,ext[0]); -								update_min_max(newMin,newMax,ext[1]); -							} -						} -					} -				} -			} -		} -	} +                                // Only add the prim to spatial extents calculations if it isn't a megaprim. +                                // max_attachment_span calculated at the start of the function  +                                // (currently 5 times our max prim size)  +                                if (lt == 0x7) +                                { +                                    update_min_max(newMin,newMax,ext[0]); +                                    update_min_max(newMin,newMax,ext[1]); +                                } +                            } +                        } +                    } +                } +            } +        } +    } + +    // Stretch bounding box by rigged mesh joint boxes +    if (box_detail>=3) +    { +        // FIXME could try to cache unless something has changed about attached rigged meshes,  +        // but needs more logic based on volume states. -	//pad bounding box	 +        //if (mRiggingInfoTab.needsUpdate()) +        { +            updateRiggingInfo(); +            //mJointRiggingInfoTab.setNeedsUpdate(false); +        } +        for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++) +        { +            LLJoint *joint = getJoint(joint_num); +            LLJointRiggingInfo *rig_info = NULL; +            if (joint_num < mJointRiggingInfoTab.size()) +            { +                rig_info = &mJointRiggingInfoTab[joint_num]; +            } -	newMin.sub(buffer); -	newMax.add(buffer); +            if (joint && rig_info && rig_info->isRiggedTo()) +            { +                LLViewerJointAttachment *as_joint_attach = dynamic_cast<LLViewerJointAttachment*>(joint); +                if (as_joint_attach && as_joint_attach->getIsHUDAttachment()) +                { +                    // Ignore bounding box of HUD joints +                    continue; +                } +                LLMatrix4a mat; +                LLVector4a new_extents[2]; +                mat.loadu(joint->getWorldMatrix()); +                matMulBoundBox(mat, rig_info->getRiggedExtents(), new_extents); +                update_min_max(newMin, newMax, new_extents[0]); +                update_min_max(newMin, newMax, new_extents[1]); +                //if (isSelf()) +                //{ +                //    LL_INFOS() << joint->getName() << " extents " << new_extents[0] << "," << new_extents[1] << LL_ENDL; +                //    LL_INFOS() << joint->getName() << " av box is " << newMin << "," << newMax << LL_ENDL; +                //} +            } +        } +    } + +    // Update pixel area +    LLVector4a center, size; +    center.setAdd(newMin, newMax); +    center.mul(0.5f); +     +    size.setSub(newMax,newMin); +    size.mul(0.5f); +     +    mPixelArea = LLPipeline::calcPixelArea(center, size, *LLViewerCamera::getInstance());  }  void render_sphere_and_line(const LLVector3& begin_pos, const LLVector3& end_pos, F32 sphere_scale, const LLVector3& occ_color, const LLVector3& visible_color) @@ -1404,13 +1529,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 +1563,6 @@ void LLVOAvatar::renderCollisionVolumes()  		mNameText->lineSegmentIntersect(unused, unused, unused, TRUE);  	} - -	mDebugText.clear(); -	addDebugText(ostr.str());  }  void LLVOAvatar::renderBones() @@ -1467,7 +1605,7 @@ void LLVOAvatar::renderBones()          }          else          { -            if (jointIsRiggedTo(jointp->getName())) +            if (jointIsRiggedTo(jointp))              {                  occ_color = RIGGED_COLOR_OCCLUDED;                  visible_color = RIGGED_COLOR_VISIBLE; @@ -1595,6 +1733,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 +1823,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 +1940,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 @@ -1856,7 +2004,7 @@ void LLVOAvatar::resetVisualParams()  void LLVOAvatar::resetSkeleton(bool reset_animations)  {      LL_DEBUGS("Avatar") << avString() << " reset starts" << LL_ENDL; -    if (!mLastProcessedAppearance) +    if (!isControlAvatar() && !mLastProcessedAppearance)      {          LL_WARNS() << "Can't reset avatar; no appearance message has been received yet." << LL_ENDL;          return; @@ -1910,12 +2058,15 @@ void LLVOAvatar::resetSkeleton(bool reset_animations)      }      // Reset tweakable params to preserved state -    bool slam_params = true; -    applyParsedAppearanceMessage(*mLastProcessedAppearance, slam_params); +    if (mLastProcessedAppearance) +    { +        bool slam_params = true; +        applyParsedAppearanceMessage(*mLastProcessedAppearance, slam_params); +    }      updateVisualParams();      // Restore attachment pos overrides -    rebuildAttachmentOverrides(); +    updateAttachmentOverrides();      // Animations      if (reset_animations) @@ -1942,7 +2093,7 @@ void LLVOAvatar::resetSkeleton(bool reset_animations)  //-----------------------------------------------------------------------------  void LLVOAvatar::releaseMeshData()  { -	if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || mIsDummy) +	if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || isUIAvatar())  	{  		return;  	} @@ -1962,15 +2113,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 +2145,10 @@ void LLVOAvatar::releaseMeshData()  void LLVOAvatar::restoreMeshData()  {  	llassert(!isSelf()); +    if (mDrawable.isNull()) +    { +        return; +    }  	//LL_INFOS() << "Restoring" << LL_ENDL;  	mMeshValid = TRUE; @@ -2291,6 +2446,9 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time)  		return;  	} +    // Update should be happening max once per frame. +    mNeedsExtentUpdate = true; +          LLScopedContextString str("avatar_idle_update " + getFullname());  	checkTextureLoading() ; @@ -2303,7 +2461,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 +2627,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 ); @@ -2573,8 +2731,10 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update)  			}  			else  			{ -				//VECTORIZE THIS -				getSpatialExtents(ext[0], ext[1]); +				ext[0].load3(mLastAnimExtents[0].mV); +                ext[1].load3(mLastAnimExtents[1].mV); +                // Expensive. Just call this once per frame, in updateSpatialExtents(); +                //calculateSpatialExtents(ext[0], ext[1]);  				LLVector4a diff;  				diff.setSub(ext[1], mImpostorExtents[1]);  				if (diff.getLength3().getF32() > 0.05f) @@ -2593,13 +2753,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 +2923,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());  			} @@ -3358,154 +3522,218 @@ bool LLVOAvatar::isInMuteList()  	return muted;  } -void LLVOAvatar::updateDebugText() +void LLVOAvatar::updateAppearanceMessageDebugText()  { -	// clear debug text -	mDebugText.clear(); +    S32 central_bake_version = -1; +    if (getRegion()) +    { +        central_bake_version = getRegion()->getCentralBakeVersion(); +    } +    bool all_baked_downloaded = allBakedTexturesCompletelyDownloaded(); +    bool all_local_downloaded = allLocalTexturesCompletelyDownloaded(); +    std::string debug_line = llformat("%s%s - mLocal: %d, mEdit: %d, mUSB: %d, CBV: %d", +                                      isSelf() ? (all_local_downloaded ? "L" : "l") : "-", +                                      all_baked_downloaded ? "B" : "b", +                                      mUseLocalAppearance, mIsEditingAppearance, +                                      1, central_bake_version); +    std::string origin_string = bakedTextureOriginInfo(); +    debug_line += " [" + origin_string + "]"; +    S32 curr_cof_version = LLAppearanceMgr::instance().getCOFVersion(); +    S32 last_request_cof_version = mLastUpdateRequestCOFVersion; +    S32 last_received_cof_version = mLastUpdateReceivedCOFVersion; +    if (isSelf()) +    { +        debug_line += llformat(" - cof: %d req: %d rcv:%d", +                               curr_cof_version, last_request_cof_version, last_received_cof_version); +        if (gSavedSettings.getBOOL("DebugForceAppearanceRequestFailure")) +        { +            debug_line += " FORCING ERRS"; +        } +    } +    else +    { +        debug_line += llformat(" - cof rcv:%d", last_received_cof_version); +    } +    debug_line += llformat(" bsz-z: %.3f", mBodySize[2]); +    if (mAvatarOffset[2] != 0.0f) +    { +        debug_line += llformat("avofs-z: %.3f", mAvatarOffset[2]); +    } +    bool hover_enabled = getRegion() && getRegion()->avatarHoverHeightEnabled(); +    debug_line += hover_enabled ? " H" : " h"; +    const LLVector3& hover_offset = getHoverOffset(); +    if (hover_offset[2] != 0.0) +    { +        debug_line += llformat(" hov_z: %.3f", hover_offset[2]); +        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(); +    LLVector3 normal; +    LLVector3 ankle_right_ground_agent = ankle_right_pos_agent; +    resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal); +    F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]); +    debug_line += llformat(" relev %.3f", rightElev); -	if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) -	{ -		S32 central_bake_version = -1; -		if (getRegion()) -		{ -			central_bake_version = getRegion()->getCentralBakeVersion(); -		} -		bool all_baked_downloaded = allBakedTexturesCompletelyDownloaded(); -		bool all_local_downloaded = allLocalTexturesCompletelyDownloaded(); -		std::string debug_line = llformat("%s%s - mLocal: %d, mEdit: %d, mUSB: %d, CBV: %d", -										  isSelf() ? (all_local_downloaded ? "L" : "l") : "-", -										  all_baked_downloaded ? "B" : "b", -										  mUseLocalAppearance, mIsEditingAppearance, -										  1, central_bake_version); -		std::string origin_string = bakedTextureOriginInfo(); -		debug_line += " [" + origin_string + "]"; -		S32 curr_cof_version = LLAppearanceMgr::instance().getCOFVersion(); -		S32 last_request_cof_version = mLastUpdateRequestCOFVersion; -		S32 last_received_cof_version = mLastUpdateReceivedCOFVersion; -		if (isSelf()) -		{ -			debug_line += llformat(" - cof: %d req: %d rcv:%d", -								   curr_cof_version, last_request_cof_version, last_received_cof_version); -			if (gSavedSettings.getBOOL("DebugForceAppearanceRequestFailure")) -			{ -				debug_line += " FORCING ERRS"; -			} -		} -		else -		{ -			debug_line += llformat(" - cof rcv:%d", last_received_cof_version); -		} -		debug_line += llformat(" bsz-z: %.3f", mBodySize[2]); -        if (mAvatarOffset[2] != 0.0f) +    LLVector3 root_pos = mRoot->getPosition(); +    LLVector3 pelvis_pos = mPelvisp->getPosition(); +    debug_line += llformat(" rp %.3f pp %.3f", root_pos[2], pelvis_pos[2]); + +    S32 is_visible = (S32) isVisible(); +    S32 is_m_visible = (S32) mVisible; +    debug_line += llformat(" v %d/%d", is_visible, is_m_visible); + +    addDebugText(debug_line); +} + +LLViewerInventoryItem* getObjectInventoryItem(LLViewerObject *vobj, LLUUID asset_id) +{ +    LLViewerInventoryItem *item = NULL; + +    if (vobj) +    { +        if (vobj->getInventorySerial()<=0)          { -            debug_line += llformat("avofs-z: %.3f", mAvatarOffset[2]); +            vobj->requestInventory();           } -		bool hover_enabled = getRegion() && getRegion()->avatarHoverHeightEnabled(); -		debug_line += hover_enabled ? " H" : " h"; -		const LLVector3& hover_offset = getHoverOffset(); -		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", (isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED) ? "G" : "-")); -		} -        LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition(); -		LLVector3 normal; -        LLVector3 ankle_right_ground_agent = ankle_right_pos_agent; -        resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal); -        F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]); -        debug_line += llformat(" relev %.3f", rightElev); +        item = vobj->getInventoryItemByAsset(asset_id); +    } +    return item; +} -        LLVector3 root_pos = mRoot->getPosition(); -        LLVector3 pelvis_pos = mPelvisp->getPosition(); -        debug_line += llformat(" rp %.3f pp %.3f", root_pos[2], pelvis_pos[2]); +LLViewerInventoryItem* recursiveGetObjectInventoryItem(LLViewerObject *vobj, LLUUID asset_id) +{ +    LLViewerInventoryItem *item = getObjectInventoryItem(vobj, asset_id); +    if (!item) +    { +        LLViewerObject::const_child_list_t& children = vobj->getChildren(); +        for (LLViewerObject::const_child_list_t::const_iterator it = children.begin(); +             it != children.end(); ++it) +        { +            LLViewerObject *childp = *it; +            item = getObjectInventoryItem(childp, asset_id); +            if (item) +            { +                break; +            } +        } +    } +    return item; +} -		addDebugText(debug_line); +void LLVOAvatar::updateAnimationDebugText() +{ +    for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin(); +         iter != mMotionController.getActiveMotions().end(); ++iter) +    { +        LLMotion* motionp = *iter; +        if (motionp->getMinPixelArea() < getPixelArea()) +        { +            std::string output; +            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; +                    LLViewerInventoryItem *item = recursiveGetObjectInventoryItem(volp,motionp->getID()); +                    if (item) +                    { +                        motion_name = item->getName(); +                    } +                } +            } +            if (motion_name.empty()) +            { +                std::string name; +                if (gAgent.isGodlikeWithoutAdminMenuFakery() || isSelf()) +                { +                    name = motionp->getID().asString(); +                    LLVOAvatar::AnimSourceIterator anim_it = mAnimationSources.begin(); +                    for (; anim_it != mAnimationSources.end(); ++anim_it) +                    { +                        if (anim_it->second == motionp->getID()) +                        { +                            LLViewerObject* object = gObjectList.findObject(anim_it->first); +                            if (!object) +                            { +                                break; +                            } +                            if (object->isAvatar()) +                            { +                                if (mMotionController.mIsSelf) +                                { +                                    // Searching inventory by asset id is really long +                                    // so just mark as inventory +                                    // Also item is likely to be named by LLPreviewAnim +                                    name += "(inventory)"; +                                } +                            } +                            else +                            { +                                LLViewerInventoryItem* item = NULL; +                                if (!object->isInventoryDirty()) +                                { +                                    item = object->getInventoryItemByAsset(motionp->getID()); +                                } +                                if (item) +                                { +                                    name = item->getName(); +                                } +                                else if (object->isAttachment()) +                                { +                                    name += "(" + getAttachmentItemName() + ")"; +                                } +                                else +                                { +                                    // in-world object, name or content unknown +                                    name += "(in-world)"; +                                } +                            } +                            break; +                        } +                    } +                } +                else +                { +                    name = LLUUID::null.asString(); +                } +                output = llformat("%s - %d", +                                  name.c_str(), +                                  (U32)motionp->getPriority()); +            } +            else +            { +                output = llformat("%s - %d", +                                  motion_name.c_str(), +                                  (U32)motionp->getPriority()); +            } +            addDebugText(output); +        } +    } +} + +void LLVOAvatar::updateDebugText() +{ +    // Leave mDebugText uncleared here, in case a derived class has added some state first + +	if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) +	{ +        updateAppearanceMessageDebugText();  	} +  	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(); -			 iter != mMotionController.getActiveMotions().end(); ++iter) -		{ -			LLMotion* motionp = *iter; -			if (motionp->getMinPixelArea() < getPixelArea()) -			{ -				std::string output; -				if (motionp->getName().empty()) -				{ -					std::string name; -					if (gAgent.isGodlikeWithoutAdminMenuFakery() || isSelf()) -					{ -						name = motionp->getID().asString(); -						LLVOAvatar::AnimSourceIterator anim_it = mAnimationSources.begin(); -						for (; anim_it != mAnimationSources.end(); ++anim_it) -						{ -							if (anim_it->second == motionp->getID()) -							{ -								LLViewerObject* object = gObjectList.findObject(anim_it->first); -								if (!object) -								{ -									break; -								} -								if (object->isAvatar()) -								{ -									if (mMotionController.mIsSelf) -									{ -										// Searching inventory by asset id is really long -										// so just mark as inventory -										// Also item is likely to be named by LLPreviewAnim -										name += "(inventory)"; -									} -								} -								else -								{ -									LLViewerInventoryItem* item = NULL; -									if (!object->isInventoryDirty()) -									{ -										item = object->getInventoryItemByAsset(motionp->getID()); -									} -									if (item) -									{ -										name = item->getName(); -									} -									else if (object->isAttachment()) -									{ -										name += "(" + getAttachmentItemName() + ")"; -									} -									else -									{ -										// in-world object, name or content unknown -										name += "(in-world)"; -									} -								} -								break; -							} -						} -					} -					else -					{ -						name = LLUUID::null.asString(); -					} - -					output = llformat("%s - %d", -							  name.c_str(), -							  (U32)motionp->getPriority()); -				} -				else -				{ -					output = llformat("%s - %d", -							  motionp->getName().c_str(), -							  (U32)motionp->getPriority()); -				} -				addDebugText(output); -			} -		} +        updateAnimationDebugText();  	}  	if (!mDebugText.size() && mText.notNull()) @@ -3517,51 +3745,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) +        && !isUIAvatar() +        && 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 +3887,214 @@ 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() +{ +	if (!isSelf() && !isUIAvatar()) // ie, non-self avatars, and animated objects will be affected.  	{ +        // 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 +4108,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 +4117,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 +4138,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  		root_pos = gAgent.getPosGlobalFromAgent(getRenderPosition());  		root_pos.mdV[VZ] += getVisualParamWeight(AVATAR_HOVER); - +        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 +4160,155 @@ 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) +        { +            // SL-1350: Moved to LLDrawable::updateXform() +            //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) ); -			 +            // Rotation fixups for avatars in motion. +            // Skip for animated objects. +            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 UI avatar used in some areas of the UI, such as when +// previewing uploaded animations. Class is LLUIAvatar, 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 +	//-------------------------------------------------------------------- +    // 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 +4327,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 +4344,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 +4370,7 @@ void LLVOAvatar::updateHeadOffset()  	{  		midEyePt = midEyePt * ~mDrawable->getWorldRotation();  	} -	if (mIsSitting) +	if (isSitting())  	{  		mHeadOffset = midEyePt;	  	} @@ -4115,7 +4466,7 @@ void LLVOAvatar::updateVisibility()  	if (mIsDummy)  	{ -		visible = TRUE; +		visible = FALSE;  	}  	else if (mDrawable.isNull())  	{ @@ -4229,7 +4580,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 +4616,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); @@ -4430,7 +4787,7 @@ U32 LLVOAvatar::renderSkinned()  		{  			if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender)  			{ -				if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy) +				if (isTextureVisible(TEX_HEAD_BAKED) || isUIAvatar())  				{  					LLViewerJoint* head_mesh = getViewerJoint(MESH_ID_HEAD);  					if (head_mesh) @@ -4440,7 +4797,7 @@ U32 LLVOAvatar::renderSkinned()  					first_pass = FALSE;  				}  			} -			if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy) +			if (isTextureVisible(TEX_UPPER_BAKED) || isUIAvatar())  			{  				LLViewerJoint* upper_mesh = getViewerJoint(MESH_ID_UPPER_BODY);  				if (upper_mesh) @@ -4450,7 +4807,7 @@ U32 LLVOAvatar::renderSkinned()  				first_pass = FALSE;  			} -			if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy) +			if (isTextureVisible(TEX_LOWER_BAKED) || isUIAvatar())  			{  				LLViewerJoint* lower_mesh = getViewerJoint(MESH_ID_LOWER_BODY);  				if (lower_mesh) @@ -4479,7 +4836,7 @@ U32 LLVOAvatar::renderSkinned()  U32 LLVOAvatar::renderTransparent(BOOL first_pass)  {  	U32 num_indices = 0; -	if( isWearingWearableType( LLWearableType::WT_SKIRT ) && (mIsDummy || isTextureVisible(TEX_SKIRT_BAKED)) ) +	if( isWearingWearableType( LLWearableType::WT_SKIRT ) && (isUIAvatar() || isTextureVisible(TEX_SKIRT_BAKED)) )  	{  		gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.25f);  		LLViewerJoint* skirt_mesh = getViewerJoint(MESH_ID_SKIRT); @@ -4507,18 +4864,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 +4912,7 @@ U32 LLVOAvatar::renderRigid()  		gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f);  	} -	if (isTextureVisible(TEX_EYES_BAKED)  || mIsDummy) +	if (isTextureVisible(TEX_EYES_BAKED)  || isUIAvatar())  	{  		LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT);  		LLViewerJoint* eyeball_right = getViewerJoint(MESH_ID_EYEBALL_RIGHT); @@ -5105,10 +5459,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 +5475,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 @@ -5488,72 +5851,30 @@ 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; -					} -		} -	} -	return false; -} - -bool LLVOAvatar::jointIsRiggedTo(const std::string& joint_name) -{ -	for (attachment_map_t::iterator iter = mAttachmentPoints.begin();  -		 iter != mAttachmentPoints.end(); -		 ++iter) -	{ -		LLViewerJointAttachment* attachment = iter->second; -        for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); -             attachment_iter != attachment->mAttachedObjects.end(); -             ++attachment_iter) -        { -            const LLViewerObject* attached_object = (*attachment_iter); -            if (attached_object && jointIsRiggedTo(joint_name, attached_object)) -            { +            {				 +                mesh_id = pSkinData->mMeshID;                  return true;              } -        } +		}  	} -    return false; +	return false;  } -bool LLVOAvatar::jointIsRiggedTo(const std::string& joint_name, const LLViewerObject *vo) +bool LLVOAvatar::jointIsRiggedTo(const LLJoint *joint) const  { -	// Process all children -	LLViewerObject::const_child_list_t& children = vo->getChildren(); -	for (LLViewerObject::const_child_list_t::const_iterator it = children.begin(); -		 it != children.end(); ++it) -	{ -		LLViewerObject *childp = *it; -        if (jointIsRiggedTo(joint_name,childp)) -        { -            return true; -        } -	} - -	const LLVOVolume *vobj = dynamic_cast<const LLVOVolume*>(vo); -	if (!vobj) -	{ -		return false; -	} - -	LLUUID currentId = vobj->getVolume()->getParams().getSculptID();						 -	const LLMeshSkinInfo*  pSkinData = gMeshRepo.getSkinInfo( currentId, vobj ); - -	if ( vobj && vobj->isAttachment() && vobj->isMesh() && pSkinData ) -	{ -        if (std::find(pSkinData->mJointNames.begin(), pSkinData->mJointNames.end(), joint_name) != -            pSkinData->mJointNames.end()) +    if (joint) +    { +        const LLJointRiggingInfoTab& tab = mJointRiggingInfoTab; +        S32 joint_num = joint->getJointNum(); +        if (joint_num < tab.size() && tab[joint_num].isRiggedTo())          {              return true;          }      } -      return false;  } @@ -5570,6 +5891,20 @@ void LLVOAvatar::clearAttachmentOverrides()  			pJoint->clearAttachmentScaleOverrides();          }      } + +    if (mPelvisFixups.count()>0) +    { +        mPelvisFixups.clear(); +        LLJoint* pJointPelvis = getJoint("mPelvis"); +        if (pJointPelvis) +        { +			pJointPelvis->setPosition( LLVector3( 0.0f, 0.0f, 0.0f) ); +        } +        postPelvisSetRecalc();	 +    } + +    mActiveOverrideMeshes.clear(); +    onActiveOverrideMeshesChanged();  }  //----------------------------------------------------------------------------- @@ -5579,7 +5914,25 @@ void LLVOAvatar::rebuildAttachmentOverrides()  {      LLScopedContextString str("rebuildAttachmentOverrides " + getFullname()); -    // Attachment points +    LL_DEBUGS("AnimatedObjects") << "rebuilding" << LL_ENDL; +    dumpStack("AnimatedObjectsStack"); +     +    clearAttachmentOverrides(); + +    // 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) +        { +            LL_DEBUGS("Avatar") << volp->getID() << " adding attachment overrides for root vol, prim count "  +                                << (S32) (1+volp->numChildren()) << LL_ENDL; +            addAttachmentOverridesForObject(volp); +        } +    } + +    // Attached objects  	for (attachment_map_t::iterator iter = mAttachmentPoints.begin();  		 iter != mAttachmentPoints.end();  		 ++iter) @@ -5590,33 +5943,164 @@ 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) +// updateAttachmentOverrides +// +// This is intended to give the same results as +// rebuildAttachmentOverrides(), while avoiding redundant work. +// ----------------------------------------------------------------------------- +void LLVOAvatar::updateAttachmentOverrides()  { -	LLVOAvatar *av = vo->getAvatarAncestor(); -	if (!av || (av != this)) +    LLScopedContextString str("updateAttachmentOverrides " + getFullname()); + +    LL_DEBUGS("AnimatedObjects") << "updating" << LL_ENDL; +    dumpStack("AnimatedObjectsStack"); + +    std::set<LLUUID> meshes_seen; +     +    // Handle the case that we're updating the skeleton of an animated object. +    LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this); +    if (control_av) +    { +        LLVOVolume *volp = control_av->mRootVolp; +        if (volp) +        { +            LL_DEBUGS("Avatar") << volp->getID() << " adding attachment overrides for root vol, prim count "  +                                << (S32) (1+volp->numChildren()) << LL_ENDL; +            addAttachmentOverridesForObject(volp, &meshes_seen); +        } +    } + +    // Attached objects +	for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); +		 iter != mAttachmentPoints.end(); +		 ++iter)  	{ +		LLViewerJointAttachment *attachment_pt = (*iter).second; +        if (attachment_pt) +        { +            for (LLViewerJointAttachment::attachedobjs_vec_t::iterator at_it = attachment_pt->mAttachedObjects.begin(); +				 at_it != attachment_pt->mAttachedObjects.end(); ++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, &meshes_seen); +                } +            } +        } +    } +    // Remove meshes that are no longer present on the skeleton + +	// have to work with a copy because removeAttachmentOverrides() will change mActiveOverrideMeshes. +    std::set<LLUUID> active_override_meshes = mActiveOverrideMeshes;  +    for (std::set<LLUUID>::iterator it = active_override_meshes.begin(); it != active_override_meshes.end(); ++it) +    { +        if (meshes_seen.find(*it) == meshes_seen.end()) +        { +            removeAttachmentOverridesForObject(*it); +        } +    } + + +#ifdef ATTACHMENT_OVERRIDE_VALIDATION +    { +        std::vector<LLVector3OverrideMap> pos_overrides_by_joint; +        std::vector<LLVector3OverrideMap> scale_overrides_by_joint; +        LLVector3OverrideMap pelvis_fixups; + +        // Capture snapshot of override state after update +        for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++) +        { +            LLVector3OverrideMap pos_overrides; +            LLJoint *joint = getJoint(joint_num); +            if (joint) +            { +                pos_overrides_by_joint.push_back(joint->m_attachmentPosOverrides); +                scale_overrides_by_joint.push_back(joint->m_attachmentScaleOverrides); +            } +            else +            { +                // No joint, use default constructed empty maps +                pos_overrides_by_joint.push_back(LLVector3OverrideMap()); +                scale_overrides_by_joint.push_back(LLVector3OverrideMap()); +            } +        } +        pelvis_fixups = mPelvisFixups; +        //dumpArchetypeXML(getFullname() + "_paranoid_updated"); + +        // Rebuild and compare +        rebuildAttachmentOverrides(); +        //dumpArchetypeXML(getFullname() + "_paranoid_rebuilt"); +        bool mismatched = false; +        for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++) +        { +            LLJoint *joint = getJoint(joint_num); +            if (joint) +            { +                if (pos_overrides_by_joint[joint_num] != joint->m_attachmentPosOverrides) +                { +                    mismatched = true; +                } +                if (scale_overrides_by_joint[joint_num] != joint->m_attachmentScaleOverrides) +                { +                    mismatched = true; +                } +            } +        } +        if (pelvis_fixups != mPelvisFixups) +        { +            mismatched = true; +        } +        if (mismatched) +        { +            LL_WARNS() << "MISMATCHED ATTACHMENT OVERRIDES" << LL_ENDL; +        } +    } +#endif +} + +//----------------------------------------------------------------------------- +// addAttachmentOverridesForObject +//----------------------------------------------------------------------------- +void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo, std::set<LLUUID>* meshes_seen, bool recursive) +{ +    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 " + getFullname()); +     +    LL_DEBUGS("AnimatedObjects") << "adding" << LL_ENDL; +    dumpStack("AnimatedObjectsStack");  	// Process all children -	LLViewerObject::const_child_list_t& children = vo->getChildren(); -	for (LLViewerObject::const_child_list_t::const_iterator it = children.begin(); -		 it != children.end(); ++it) -	{ -		LLViewerObject *childp = *it; -		addAttachmentOverridesForObject(childp); -	} +    if (recursive) +    { +        LLViewerObject::const_child_list_t& children = vo->getChildren(); +        for (LLViewerObject::const_child_list_t::const_iterator it = children.begin(); +             it != children.end(); ++it) +        { +            LLViewerObject *childp = *it; +            addAttachmentOverridesForObject(childp, meshes_seen, true); +        } +    }  	LLVOVolume *vobj = dynamic_cast<LLVOVolume*>(vo);  	bool pelvisGotSet = false; @@ -5625,15 +6109,18 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)  	{  		return;  	} + +	LLViewerObject *root_object = (LLViewerObject*)vobj->getRoot(); +    LL_DEBUGS("AnimatedObjects") << "trying to add attachment overrides for root object " << root_object->getID() << " prim is " << vobj << LL_ENDL;  	if (vobj->isMesh() &&  		((vobj->getVolume() && !vobj->getVolume()->isMeshAssetLoaded()) || !gMeshRepo.meshRezEnabled()))  	{ +        LL_DEBUGS("AnimatedObjects") << "failed to add attachment overrides for root object " << root_object->getID() << " mesh asset not loaded" << LL_ENDL;  		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(); @@ -5645,8 +6132,26 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)  		{					  			const F32 pelvisZOffset = pSkinData->mPelvisOffset;  			const LLUUID& mesh_id = pSkinData->mMeshID; + +            if (meshes_seen) +            { +                meshes_seen->insert(mesh_id); +            } +            bool mesh_overrides_loaded = (mActiveOverrideMeshes.find(mesh_id) != mActiveOverrideMeshes.end()); +            if (mesh_overrides_loaded) +            { +                LL_DEBUGS("AnimatedObjects") << "skipping add attachment overrides for " << mesh_id  +                                             << " to root object " << root_object->getID() +                                             << ", already loaded" +                                             << LL_ENDL; +            } +            else +            { +                LL_DEBUGS("AnimatedObjects") << "adding attachment overrides for " << mesh_id  +                                             << " to root object " << root_object->getID() << LL_ENDL; +            }  			bool fullRig = (jointCnt>=JOINT_COUNT_REQUIRED_FOR_FULLRIG) ? true : false;								 -			if ( fullRig ) +			if ( fullRig && !mesh_overrides_loaded )  			{								  				for ( int i=0; i<jointCnt; ++i )  				{ @@ -5690,9 +6195,15 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)                      }  				} +                mActiveOverrideMeshes.insert(mesh_id); +                onActiveOverrideMeshesChanged();  			}							  		}  	} +    else +    { +        LL_DEBUGS("AnimatedObjects") << "failed to add attachment overrides for root object " << root_object->getID() << " not mesh or no pSkinData" << LL_ENDL; +    }  	//Rebuild body data if we altered joints/pelvis  	if ( pelvisGotSet )  @@ -5815,14 +6326,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 @@ -5831,37 +6342,32 @@ 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(); -	avatar_joint_list_t::iterator end  = mSkeleton.end(); -  	LLJoint* pJointPelvis = getJoint("mPelvis"); -	 -	for (; iter != end; ++iter) -	{ -		LLJoint* pJoint = (*iter); -		//Reset joints except for pelvis +    const std::string av_string = avString(); +    for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++) +    { +        LLJoint *pJoint = getJoint(joint_num);  		if ( pJoint )  		{			              bool dummy; // unused -			pJoint->removeAttachmentPosOverride(mesh_id, avString(),dummy); -			pJoint->removeAttachmentScaleOverride(mesh_id, avString()); +			pJoint->removeAttachmentPosOverride(mesh_id, av_string, dummy); +			pJoint->removeAttachmentScaleOverride(mesh_id, av_string);  		}		  		if ( pJoint && pJoint == pJointPelvis)  		{ @@ -5872,6 +6378,9 @@ void LLVOAvatar::resetJointsOnDetach(const LLUUID& mesh_id)  	}	  	postPelvisSetRecalc();	 + +    mActiveOverrideMeshes.erase(mesh_id); +    onActiveOverrideMeshesChanged();  }  //-----------------------------------------------------------------------------  // getCharacterPosition() @@ -5923,7 +6432,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; -	if (mIsDummy) +	if (isUIAvatar())  	{  		outNorm.setVec(z_vec);  		out_pos_agent = in_pos_agent; @@ -5952,7 +6461,7 @@ F32 LLVOAvatar::getTimeDilation()  //-----------------------------------------------------------------------------  F32 LLVOAvatar::getPixelArea() const  { -	if (mIsDummy) +	if (isUIAvatar())  	{  		return 100000.f;  	} @@ -6386,7 +6895,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. @@ -6449,6 +6958,11 @@ const LLViewerJointAttachment *LLVOAvatar::attachObject(LLViewerObject *viewer_o  		return 0;  	} +    if (!viewer_object->isAnimatedObject()) +    { +        updateAttachmentOverrides(); +    } +  	updateVisualComplexity();  	if (viewer_object->isSelected()) @@ -6478,19 +6992,62 @@ 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; +    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();  }  //----------------------------------------------------------------------------- @@ -6581,7 +7138,7 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )  	LLUUID mesh_id;  	if (getRiggedMeshID(pVO, mesh_id))  	{ -		resetJointsOnDetach(mesh_id); +        // FIXME this seems like an odd place for this code.  		if ( gAgentCamera.cameraCustomizeAvatar() )  		{  			gAgent.unpauseAnimation(); @@ -6606,9 +7163,13 @@ BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object)  		if (attachment->isObjectAttached(viewer_object))  		{              updateVisualComplexity(); +            bool is_animated_object = viewer_object->isAnimatedObject();  			cleanupAttachedMesh( viewer_object ); -		  			attachment->removeObject(viewer_object); +            if (!is_animated_object) +            { +                updateAttachmentOverrides(); +            }  			LL_DEBUGS() << "Detaching object " << viewer_object->mID << " from " << attachment->getName() << LL_ENDL;  			return TRUE;  		} @@ -6742,7 +7303,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())  	{ @@ -6900,6 +7464,12 @@ void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color)  	updateMeshTextures();  } +// FIXME: We have an mVisible member, set in updateVisibility(), but this +// function doesn't return it! isVisible() and mVisible are used +// different places for different purposes. mVisible seems to be more +// related to whether the actual avatar mesh is shown, and isVisible() +// to whether anything about the avatar is displayed in the scene. +// Maybe better naming could make this clearer?  BOOL LLVOAvatar::isVisible() const  {  	return mDrawable.notNull() @@ -6910,6 +7480,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) @@ -7189,9 +7764,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; @@ -8696,8 +9271,43 @@ void LLVOAvatar::dumpArchetypeXML(const std::string& prefix, bool group_by_weara  							 pelvis_fixup, mesh_id.asString().c_str());  		} -		apr_file_printf( file, "\t</archetype>\n" ); -		apr_file_printf( file, "\n</linden_genepool>\n" ); +        LLVector3 rp = getRootJoint()->getWorldPosition(); +        LLVector4a rpv; +        rpv.load3(rp.mV); +         +        for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++) +        { +            LLJoint *joint = getJoint(joint_num); +            if (joint_num < mJointRiggingInfoTab.size()) +            { +                LLJointRiggingInfo& rig_info = mJointRiggingInfoTab[joint_num]; +                if (rig_info.isRiggedTo()) +                { +                    LLMatrix4a mat; +                    LLVector4a new_extents[2]; +                    mat.loadu(joint->getWorldMatrix()); +                    matMulBoundBox(mat, rig_info.getRiggedExtents(), new_extents); +                    LLVector4a rrp[2]; +                    rrp[0].setSub(new_extents[0],rpv); +                    rrp[1].setSub(new_extents[1],rpv); +                    apr_file_printf( file, "\t\t<joint_rig_info num=\"%d\" name=\"%s\" min=\"%f %f %f\" max=\"%f %f %f\" tmin=\"%f %f %f\" tmax=\"%f %f %f\"/>\n",  +                                     joint_num, +                                     joint->getName().c_str(), +                                     rig_info.getRiggedExtents()[0][0], +                                     rig_info.getRiggedExtents()[0][1], +                                     rig_info.getRiggedExtents()[0][2], +                                     rig_info.getRiggedExtents()[1][0], +                                     rig_info.getRiggedExtents()[1][1], +                                     rig_info.getRiggedExtents()[1][2], +                                     rrp[0][0], +                                     rrp[0][1], +                                     rrp[0][2], +                                     rrp[1][0], +                                     rrp[1][1], +                                     rrp[1][2] ); +                } +            } +        }  		bool ultra_verbose = false;  		if (isSelf() && ultra_verbose) @@ -8705,6 +9315,10 @@ void LLVOAvatar::dumpArchetypeXML(const std::string& prefix, bool group_by_weara  			// show the cloned params inside the wearables as well.  			gAgentAvatarp->dumpWearableInfo(outfile);  		} + +		apr_file_printf( file, "\t</archetype>\n" ); +		apr_file_printf( file, "\n</linden_genepool>\n" ); +  		LLSD args;  		args["PATH"] = fullpath;  		LLNotificationsUtil::add("AppearanceToXMLSaved", args); @@ -8836,6 +9450,7 @@ void LLVOAvatar::updateRegion(LLViewerRegion *regionp)  	LLViewerObject::updateRegion(regionp);  } +// virtual  std::string LLVOAvatar::getFullname() const  {  	std::string name; @@ -8882,6 +9497,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; @@ -8907,11 +9527,120 @@ BOOL LLVOAvatar::updateLOD()  	return res;  } -void LLVOAvatar::updateLODRiggedAttachments( void ) +void LLVOAvatar::updateLODRiggedAttachments()  {  	updateLOD();  	rebuildRiggedAttachments();  } + +void showRigInfoTabExtents(LLVOAvatar *avatar, LLJointRiggingInfoTab& tab, S32& count_rigged, S32& count_box) +{ +    count_rigged = count_box = 0; +    LLVector4a zero_vec; +    zero_vec.clear(); +    for (S32 i=0; i<tab.size(); i++) +    { +        if (tab[i].isRiggedTo()) +        { +            count_rigged++; +            LLJoint *joint = avatar->getJoint(i); +            LL_DEBUGS("RigSpam") << "joint " << i << " name " << joint->getName() << " box "  +                                 << tab[i].getRiggedExtents()[0] << ", " << tab[i].getRiggedExtents()[1] << LL_ENDL; +            if ((!tab[i].getRiggedExtents()[0].equals3(zero_vec)) || +                (!tab[i].getRiggedExtents()[1].equals3(zero_vec))) +            { +                count_box++; +            } +       } +    } +} + +void LLVOAvatar::getAssociatedVolumes(std::vector<LLVOVolume*>& volumes) +{ +	for ( LLVOAvatar::attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter ) +	{ +		LLViewerJointAttachment* attachment = iter->second; +		LLViewerJointAttachment::attachedobjs_vec_t::iterator attach_end = attachment->mAttachedObjects.end(); +		 +		for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attach_iter = attachment->mAttachedObjects.begin(); +			 attach_iter != attach_end; ++attach_iter) +		{ +			LLViewerObject* attached_object =  *attach_iter; +            LLVOVolume *volume = dynamic_cast<LLVOVolume*>(attached_object); +            if (volume) +            { +                volumes.push_back(volume); +                if (volume->isAnimatedObject()) +                { +                    // For animated object attachment, don't need +                    // the children. Will just get bounding box +                    // from the control avatar. +                    continue; +                } +            } +            LLViewerObject::const_child_list_t& children = attached_object->getChildren(); +            for (LLViewerObject::const_child_list_t::const_iterator it = children.begin(); +                 it != children.end(); ++it) +            { +                LLViewerObject *childp = *it; +                LLVOVolume *volume = dynamic_cast<LLVOVolume*>(childp); +                if (volume) +                { +                    volumes.push_back(volume); +                } +            } +        } +    } + +    LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this); +    if (control_av) +    { +        LLVOVolume *volp = control_av->mRootVolp; +        if (volp) +        { +            volumes.push_back(volp); +            LLViewerObject::const_child_list_t& children = volp->getChildren(); +            for (LLViewerObject::const_child_list_t::const_iterator it = children.begin(); +                 it != children.end(); ++it) +            { +                LLViewerObject *childp = *it; +                LLVOVolume *volume = dynamic_cast<LLVOVolume*>(childp); +                if (volume) +                { +                    volumes.push_back(volume); +                } +            } +        } +    } +} + +// virtual +void LLVOAvatar::updateRiggingInfo() +{ +    LL_DEBUGS("RigSpammish") << getFullname() << " updating rig tab" << LL_ENDL; +    mJointRiggingInfoTab.clear(); +    std::vector<LLVOVolume*> volumes; +    getAssociatedVolumes(volumes); +    for (std::vector<LLVOVolume*>::iterator it = volumes.begin(); it != volumes.end(); ++it) +    { +        LLVOVolume *vol = *it; +        vol->updateRiggingInfo(); +        mJointRiggingInfoTab.merge(vol->mJointRiggingInfoTab); +    } + +    //LL_INFOS() << "done update rig count is " << countRigInfoTab(mJointRiggingInfoTab) << LL_ENDL; +    LL_DEBUGS("RigSpammish") << getFullname() << " after update rig tab:" << LL_ENDL; +    S32 joint_count, box_count; +    showRigInfoTabExtents(this, mJointRiggingInfoTab, joint_count, box_count); +    LL_DEBUGS("RigSpammish") << "uses " << joint_count << " joints " << " nonzero boxes: " << box_count << LL_ENDL; +} + +// virtual +void LLVOAvatar::onActiveOverrideMeshesChanged() +{ +    mJointRiggingInfoTab.setNeedsUpdate(true); +} +  U32 LLVOAvatar::getPartitionType() const  {   	// Avatars merely exist as drawables in the bridge partition @@ -8922,10 +9651,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() @@ -9022,6 +9751,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	 @@ -9069,10 +9809,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); @@ -9091,22 +9837,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; @@ -9114,6 +9851,135 @@ 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; +                const F32 animated_object_attachment_surcharge = 1000; + +                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()  { @@ -9152,7 +10018,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) @@ -9163,112 +10047,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);  			}  		} diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index bd89d4ef23..25c89f96d7 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -49,7 +49,10 @@  #include "lldriverparam.h"  #include "llviewertexlayer.h"  #include "material_codes.h"		// LL_MCODE_END +#include "llrigginginfo.h"  #include "llviewerstats.h" +#include "llvovolume.h" +#include "llavatarrendernotifier.h"  extern const LLUUID ANIM_AGENT_BODY_NOISE;  extern const LLUUID ANIM_AGENT_BREATHE_ROT; @@ -159,7 +162,7 @@ public:  	/*virtual*/ void   	 	 	setPixelAreaAndAngle(LLAgent &agent);  	/*virtual*/ void   	 	 	updateRegion(LLViewerRegion *regionp);  	/*virtual*/ void   	 	 	updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax); -	/*virtual*/ void   	 	 	getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax); +	void			   	 	 	calculateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);  	/*virtual*/ BOOL   	 	 	lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,  												 S32 face = -1,                    // which face to check, -1 = ALL_SIDES  												 BOOL pick_transparent = FALSE, @@ -169,7 +172,8 @@ public:  												 LLVector2* tex_coord = NULL,      // return the texture coordinates of the intersection point  												 LLVector4a* normal = NULL,         // return the surface normal at the intersection point  												 LLVector4a* tangent = NULL);     // return the surface tangent at the intersection point -	LLViewerObject*	lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end, +	virtual LLViewerObject*	lineSegmentIntersectRiggedAttachments( +                                                 const LLVector4a& start, const LLVector4a& end,  												 S32 face = -1,                    // which face to check, -1 = ALL_SIDES  												 BOOL pick_transparent = FALSE,  												 BOOL pick_rigged = FALSE, @@ -200,18 +204,26 @@ public:  	virtual LLJoint*		getJoint(const std::string &name);  	LLJoint*		        getJoint(S32 num); -	 -	void 					addAttachmentOverridesForObject(LLViewerObject *vo); -	void					resetJointsOnDetach(const LLUUID& mesh_id); -	void					resetJointsOnDetach(LLViewerObject *vo); -    bool					jointIsRiggedTo(const std::string& joint_name); -    bool					jointIsRiggedTo(const std::string& joint_name, const LLViewerObject *vo); + +	void 					addAttachmentOverridesForObject(LLViewerObject *vo, std::set<LLUUID>* meshes_seen = NULL, bool recursive = true); +	void					removeAttachmentOverridesForObject(const LLUUID& mesh_id); +	void					removeAttachmentOverridesForObject(LLViewerObject *vo); +    bool					jointIsRiggedTo(const LLJoint *joint) const;  	void					clearAttachmentOverrides();  	void					rebuildAttachmentOverrides(); +    void					updateAttachmentOverrides();      void                    showAttachmentOverrides(bool verbose = false) const;      void                    getAttachmentOverrideNames(std::set<std::string>& pos_names,                                                          std::set<std::string>& scale_names) const; -	 + +    void 					getAssociatedVolumes(std::vector<LLVOVolume*>& volumes); + +    // virtual +    void 					updateRiggingInfo(); + +    std::set<LLUUID>		mActiveOverrideMeshes; +    virtual void			onActiveOverrideMeshesChanged(); +      	/*virtual*/ const LLUUID&	getID() const;  	/*virtual*/ void			addDebugText(const std::string& text);  	/*virtual*/ F32				getTimeDilation(); @@ -233,6 +245,9 @@ public:  public:  	virtual bool 	isSelf() const { return false; } // True if this avatar is for this viewer's agent +	virtual bool 	isControlAvatar() const { return mIsControlAvatar; } // True if this avatar is a control av (no associated user) +	virtual bool 	isUIAvatar() const { return mIsUIAvatar; } // True if this avatar is a supplemental av used in some UI views (no associated user) +  private: //aligned members  	LL_ALIGN_16(LLVector4a	mImpostorExtents[2]); @@ -240,8 +255,16 @@ private: //aligned members  	// Updates  	//--------------------------------------------------------------------  public: -	void			updateDebugText(); +    void			updateAppearanceMessageDebugText(); +	void 			updateAnimationDebugText(); +	virtual void	updateDebugText();  	virtual BOOL 	updateCharacter(LLAgent &agent); +    void			updateFootstepSounds(); +    void			computeUpdatePeriod(); +    void			updateOrientation(LLAgent &agent, F32 speed, F32 delta_time); +    void			updateTimeStep(); +    void			updateRootPositionAndRotation(LLAgent &agent, F32 speed, bool was_sit_ground_constrained); +      	void 			idleUpdateVoiceVisualizer(bool voice_enabled);  	void 			idleUpdateMisc(bool detailed_update);  	virtual void	idleUpdateAppearanceAnimation(); @@ -259,14 +282,17 @@ public:  	static void		invalidateNameTags();  	void			addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font);  	void 			idleUpdateRenderComplexity(); +    void 			accountRenderComplexityForObject(const LLViewerObject *attached_object, +                                                     const F32 max_attachment_complexity, +                                                     LLVOVolume::texture_cost_t& textures, +                                                     U32& cost, +                                                     hud_complexity_list_t& hud_complexity_list);  	void			calculateUpdateRenderComplexity();  	static const U32 VISUAL_COMPLEXITY_UNKNOWN;  	void			updateVisualComplexity();  	U32				getVisualComplexity()			{ return mVisualComplexity;				};		// Numbers calculated here by rendering AV  	F32				getAttachmentSurfaceArea()		{ return mAttachmentSurfaceArea;		};		// estimated surface area of attachments -    void            addAttachmentArea(F32 delta_area); -    void            subtractAttachmentArea(F32 delta_area);  	U32				getReportedVisualComplexity()					{ return mReportedVisualComplexity;				};	// Numbers as reported by the SL server  	void			setReportedVisualComplexity(U32 value)			{ mReportedVisualComplexity = value;			}; @@ -422,6 +448,8 @@ public:    private:  	F32			mAttachmentSurfaceArea; //estimated surface area of attachments +    U32			mAttachmentVisibleTriangleCount; +    F32			mAttachmentEstTriangleCount;  	bool		shouldAlphaMask();  	BOOL 		mNeedsSkin; // avatar has been animated and verts have not been updated @@ -441,6 +469,14 @@ public:  	VisualMuteSettings		mVisuallyMuteSetting;			// Always or never visually mute this AV  	//-------------------------------------------------------------------- +	// animated object status +	//-------------------------------------------------------------------- +public: +    bool mIsControlAvatar; +    bool mIsUIAvatar; +    bool mEnableDefaultMotions; + +	//--------------------------------------------------------------------  	// Morph masks  	//--------------------------------------------------------------------  public: @@ -490,10 +526,14 @@ public:  	static void updateImpostors();  	LLRenderTarget mImpostor;  	BOOL		mNeedsImpostorUpdate; +    const LLVector3*  getLastAnimExtents() const { return mLastAnimExtents; }  private:  	LLVector3	mImpostorOffset;  	LLVector2	mImpostorDim; +    // This becomes true in the constructor and false after the first +    // idleUpdateMisc(). Not clear it serves any purpose.  	BOOL		mNeedsAnimUpdate; +    bool		mNeedsExtentUpdate;  	LLVector3	mImpostorAngle;  	F32			mImpostorDistance;  	F32			mImpostorPixelArea; @@ -717,6 +757,7 @@ private:  public:  	BOOL			isVisible() const;  	void			setVisibilityRank(U32 rank); +    U32				getVisibilityRank() const { return mVisibilityRank; }  	static S32 		sNumVisibleAvatars; // Number of instances of this class  /**                    Appearance   **                                                                            ** @@ -739,9 +780,9 @@ public:  	static LLVOAvatar*  findAvatarFromAttachment(LLViewerObject* obj);  	/*virtual*/ BOOL	isWearingWearableType(LLWearableType::EType type ) const;  	LLViewerObject *	findAttachmentByID( const LLUUID & target_id ) const; +	LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object);  protected: -	LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object);  	void 				lazyAttach();  	void				rebuildRiggedAttachments( void ); @@ -761,10 +802,12 @@ public:  	BOOL 				hasHUDAttachment() const;  	LLBBox 				getHUDBBox() const;  	void 				resetHUDAttachments(); -	BOOL				canAttachMoreObjects() const; -	BOOL				canAttachMoreObjects(U32 n) const; +	BOOL				canAttachMoreObjects(U32 n=1) const; +    S32					getMaxAnimatedObjectAttachments() const; +    BOOL				canAttachMoreAnimatedObjects(U32 n=1) const;  protected:  	U32					getNumAttachments() const; // O(N), not O(1) +	U32					getNumAnimatedObjectAttachments() const; // O(N), not O(1)  /**                    Wearables   **                                                                            ** @@ -897,7 +940,7 @@ private:   **/  public: -	std::string		getFullname() const; // Returns "FirstName LastName" +	virtual std::string	getFullname() const; // Returns "FirstName LastName"  	std::string		avString() const; // Frequently used string in log messages "Avatar '<full name'"  protected:  	static void		getAnimLabels(std::vector<std::string>* labels); diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index b2954f4de2..b3a2d7951c 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -235,6 +235,8 @@ void LLVOAvatarSelf::initInstance()  	//doPeriodically(output_self_av_texture_diagnostics, 30.0);  	doPeriodically(update_avatar_rez_metrics, 5.0);  	doPeriodically(boost::bind(&LLVOAvatarSelf::checkStuckAppearance, this), 30.0); + +    mInitFlags |= 1<<2;  }  void LLVOAvatarSelf::setHoverIfRegionEnabled() @@ -2793,7 +2795,7 @@ BOOL LLVOAvatarSelf::needsRenderBeam()  		// don't render selection beam on hud objects  		is_touching_or_grabbing = FALSE;  	} -	return is_touching_or_grabbing || (mState & AGENT_STATE_EDITING && LLSelectMgr::getInstance()->shouldShowSelection()); +	return is_touching_or_grabbing || (getAttachmentState() & AGENT_STATE_EDITING && LLSelectMgr::getInstance()->shouldShowSelection());  }  // static diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp index b5c90a8f60..d651d540b9 100644 --- a/indra/newview/llvograss.cpp +++ b/indra/newview/llvograss.cpp @@ -92,7 +92,7 @@ LLVOGrass::~LLVOGrass()  void LLVOGrass::updateSpecies()  { -	mSpecies = mState; +	mSpecies = getAttachmentState();  	if (!sSpeciesTable.count(mSpecies))  	{ diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 7d6881f8a8..913460b3d1 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -76,8 +76,14 @@  #include "lldatapacker.h"  #include "llviewershadermgr.h"  #include "llvoavatar.h" +#include "llcontrolavatar.h" +#include "llvoavatarself.h"  #include "llvocache.h"  #include "llmaterialmgr.h" +#include "llanimationstates.h" +#include "llinventorytype.h" +#include "llviewerinventory.h" +#include "llcallstack.h"  #include "llsculptidsize.h"  const F32 FORCE_SIMPLE_RENDER_AREA = 512.f; @@ -213,6 +219,9 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re  	mFaceMappingChanged = FALSE;  	mLOD = MIN_LOD; +    mLODDistance = 0.0f; +    mLODAdjustedDistance = 0.0f; +    mLODRadius = 0.0f;  	mTextureAnimp = NULL;  	mVolumeChanged = FALSE;  	mVObjRadius = LLVector3(1,1,0.5f).length(); @@ -225,6 +234,7 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re  	mLastFetchedMediaVersion = -1;  	memset(&mIndexInTex, 0, sizeof(S32) * LLRender::NUM_VOLUME_TEXTURE_CHANNELS);  	mMDCImplCount = 0; +    mLastRiggingInfoLOD = -1;  }  LLVOVolume::~LLVOVolume() @@ -310,6 +320,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,  										  U32 block_num, EObjectUpdateType update_type,  										  LLDataPacker *dp)  { +	 	  	LLColor4U color;  	const S32 teDirtyBits = (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR|TEM_CHANGE_MEDIA); @@ -323,6 +334,9 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,  		LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);  		sculpt_id = sculpt_params->getSculptTexture();  		sculpt_type = sculpt_params->getSculptType(); + +        LL_DEBUGS("ObjectUpdate") << "uuid " << mID << " set sculpt_id " << sculpt_id << LL_ENDL; +        dumpStack("ObjectUpdateStack");  	}  	if (!dp) @@ -1106,16 +1120,34 @@ void LLVOVolume::updateSculptTexture()  } +void LLVOVolume::updateVisualComplexity() +{ +    LLVOAvatar* avatar = getAvatarAncestor(); +    if (avatar) +    { +        avatar->updateVisualComplexity(); +    } +    LLVOAvatar* rigged_avatar = getAvatar(); +    if(rigged_avatar && (rigged_avatar != avatar)) +    { +        rigged_avatar->updateVisualComplexity(); +    } +} +  void LLVOVolume::notifyMeshLoaded()  {   	mSculptChanged = TRUE;  	gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE); -	LLVOAvatar* avatar = getAvatar(); -	if (avatar) -	{ -		avatar->updateVisualComplexity(); -	} +    if (getAvatar() && !isAnimatedObject()) +    { +        getAvatar()->addAttachmentOverridesForObject(this); +    } +    if (getControlAvatar() && isAnimatedObject()) +    { +        getControlAvatar()->addAttachmentOverridesForObject(this); +    } +    updateVisualComplexity();  }  // sculpt replaces generate() for sculpted surfaces @@ -1240,6 +1272,46 @@ S32	LLVOVolume::computeLODDetail(F32 distance, F32 radius, F32 lod_factor)  	return cur_detail;  } +std::string get_debug_object_lod_text(LLVOVolume *rootp) +{ +    std::string cam_dist_string = ""; +    cam_dist_string += LLStringOps::getReadableNumber(rootp->mLODDistance) +  " "; +    std::string lod_string = llformat("%d",rootp->getLOD()); +    F32 lod_radius = rootp->mLODRadius; +    S32 cam_dist_count = 0; +    LLViewerObject::const_child_list_t& child_list = rootp->getChildren(); +    for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin(); +         iter != child_list.end(); ++iter) +    { +        LLViewerObject *childp = *iter; +        LLVOVolume *volp = dynamic_cast<LLVOVolume*>(childp); +        if (volp) +        { +            lod_string += llformat("%d",volp->getLOD()); +            if (volp->isRiggedMesh()) +            { +                // Rigged/animatable mesh. This is computed from the +                // avatar dynamic box, so value from any vol will be +                // the same. +                lod_radius = volp->mLODRadius; +            } +            if (volp->mDrawable) +            { +                if (cam_dist_count < 4) +                { +                    cam_dist_string += LLStringOps::getReadableNumber(volp->mLODDistance) +  " "; +                    cam_dist_count++; +                } +            } +        } +    } +    std::string result = llformat("lod_radius %s dists %s lods %s", +                                  LLStringOps::getReadableNumber(lod_radius).c_str(), +                                  cam_dist_string.c_str(), +                                  lod_string.c_str()); +    return result; +} +  BOOL LLVOVolume::calcLOD()  {  	if (mDrawable.isNull()) @@ -1264,18 +1336,60 @@ BOOL LLVOVolume::calcLOD()  		}  		distance = avatar->mDrawable->mDistanceWRTCamera; -		radius = avatar->getBinRadius(); + + +        if (avatar->isControlAvatar()) +        { +            // MAINT-7926 Handle volumes in an animated object as a special case +            const LLVector3* box = avatar->getLastAnimExtents(); +            LLVector3 diag = box[1] - box[0]; +            radius = diag.magVec() * 0.5f; +            LL_DEBUGS("DynamicBox") << avatar->getFullname() << " diag " << diag << " radius " << radius << LL_ENDL; +        } +        else +        { +            // Volume in a rigged mesh attached to a regular avatar. +            // Note this isn't really a radius, so distance calcs are off by factor of 2 +            //radius = avatar->getBinRadius(); +            // SL-937: add dynamic box handling for rigged mesh on regular avatars. +            const LLVector3* box = avatar->getLastAnimExtents(); +            LLVector3 diag = box[1] - box[0]; +            radius = diag.magVec(); // preserve old BinRadius behavior - 2x off +            LL_DEBUGS("DynamicBox") << avatar->getFullname() << " diag " << diag << " radius " << radius << LL_ENDL; +        } +        if (distance <= 0.f || radius <= 0.f) +        { +            LL_DEBUGS("DynamicBox","CalcLOD") << "avatar distance/radius uninitialized, skipping" << LL_ENDL; +            return FALSE; +        }  	}  	else  	{  		distance = mDrawable->mDistanceWRTCamera;  		radius = getVolume() ? getVolume()->mLODScaleBias.scaledVec(getScale()).length() : getScale().length(); +        if (distance <= 0.f || radius <= 0.f) +        { +            LL_DEBUGS("DynamicBox","CalcLOD") << "non-avatar distance/radius uninitialized, skipping" << LL_ENDL; +            return FALSE; +        }  	}  	//hold onto unmodified distance for debugging  	//F32 debug_distance = distance; -	 -	distance *= sDistanceFactor; + +    mLODDistance = distance; +    mLODRadius = radius; + +    if (gSavedSettings.getBOOL("DebugObjectLODs")) +    { +        if (getAvatar() && isRootEdit()) +        { +            std::string debug_object_text = get_debug_object_lod_text(this); +            setDebugText(debug_object_text); +        } +    } + +    distance *= sDistanceFactor;  	F32 rampDist = LLVOVolume::sLODFactor * 2; @@ -1296,24 +1410,47 @@ BOOL LLVOVolume::calcLOD()  		lod_factor *= DEFAULT_FIELD_OF_VIEW / LLViewerCamera::getInstance()->getDefaultFOV();  	} -	cur_detail = computeLODDetail(ll_round(distance, 0.01f),  -									ll_round(radius, 0.01f), -									lod_factor); +    mLODAdjustedDistance = distance; +    if (isHUDAttachment()) +    { +        // HUDs always show at highest detail +        cur_detail = 3; +    } +    else +    { +        cur_detail = computeLODDetail(ll_round(distance, 0.01f), ll_round(radius, 0.01f), lod_factor); +    } + +    if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TRIANGLE_COUNT) && mDrawable->getFace(0)) +    { +        if (isRootEdit()) +        { +            S32 total_tris = recursiveGetTriangleCount(); +            S32 est_max_tris = recursiveGetEstTrianglesMax(); +            setDebugText(llformat("TRIS SHOWN %d EST %d", total_tris, est_max_tris)); +        } +    }  	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LOD_INFO) &&  		mDrawable->getFace(0))  	{ -		//setDebugText(llformat("%.2f:%.2f, %d", mDrawable->mDistanceWRTCamera, radius, cur_detail)); - -		setDebugText(llformat("%d", mDrawable->getFace(0)->getTextureIndex())); +        // This is a debug display for LODs. Please don't put the texture index here. +        setDebugText(llformat("%d", cur_detail));  	}  	if (cur_detail != mLOD)  	{ +        LL_DEBUGS("DynamicBox","CalcLOD") << "new LOD " << cur_detail << " change from " << mLOD  +                             << " distance " << distance << " radius " << radius << " rampDist " << rampDist +                             << " drawable rigged? " << (mDrawable ? (S32) mDrawable->isState(LLDrawable::RIGGED) : (S32) -1) +							 << " mRiggedVolume " << (void*)getRiggedVolume() +                             << " distanceWRTCamera " << (mDrawable ? mDrawable->mDistanceWRTCamera : -1.f) +                             << LL_ENDL; +          		mAppAngle = ll_round((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f); -		mLOD = cur_detail; +		mLOD = cur_detail;		 -		return TRUE; +        return TRUE;  	}  	return FALSE; @@ -1339,6 +1476,16 @@ BOOL LLVOVolume::updateLOD()  	if (lod_changed)  	{ +        if (debugLoggingEnabled("AnimatedObjectsLinkset")) +        { +            if (isAnimatedObject() && isRiggedMesh()) +            { +                std::string vobj_name = llformat("Vol%p", this); +                F32 est_tris = getEstTrianglesMax(); +                LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " updateLOD to " << getLOD() << ", tris " << est_tris << LL_ENDL;  +            } +        } +  		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);  		mLODChanged = TRUE;  	} @@ -1413,7 +1560,8 @@ void LLVOVolume::updateFaceFlags()  BOOL LLVOVolume::setParent(LLViewerObject* parent)  {  	BOOL ret = FALSE ; -	if (parent != getParent()) +    LLViewerObject *old_parent = (LLViewerObject*) getParent(); +	if (parent != old_parent)  	{  		ret = LLViewerObject::setParent(parent);  		if (ret && mDrawable) @@ -1421,6 +1569,7 @@ BOOL LLVOVolume::setParent(LLViewerObject* parent)  			gPipeline.markMoved(mDrawable);  			gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);  		} +        onReparent(old_parent, parent);  	}  	return ret ; @@ -1485,14 +1634,29 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)  	BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED); -	//	bool rigged = false; +    if (getRiggedVolume()) +    { +        // MAINT-8264 - better to use the existing call in calling +        // func LLVOVolume::updateGeometry() if we can detect when +        // updates needed, set REBUILD_RIGGED accordingly. + +        // Without the flag, this will remove unused rigged volumes, which we are not currently very aggressive about. +        updateRiggedVolume(); +    } +      	LLVolume* volume = mRiggedVolume;  	if (!volume)  	{  		volume = getVolume();  	} -	// There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces() +    bool any_valid_boxes = false; +     +    if (getRiggedVolume()) +    { +        LL_DEBUGS("RiggedBox") << "rebuilding box, volume face count " << getVolume()->getNumVolumeFaces() << " drawable face count " << mDrawable->getNumFaces() << LL_ENDL; +    } +    // There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces()  	for (S32 i = 0;  		 i < getVolume()->getNumVolumeFaces() && i < mDrawable->getNumFaces() && i < getNumTEs();  		 i++) @@ -1502,15 +1666,28 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)  		{  			continue;  		} -		res &= face->genVolumeBBoxes(*volume, i, -										mRelativeXform,  -										(mVolumeImpl && mVolumeImpl->isVolumeGlobal()) || force_global); + +        BOOL face_res = face->genVolumeBBoxes(*volume, i, +                                              mRelativeXform,  +                                              (mVolumeImpl && mVolumeImpl->isVolumeGlobal()) || force_global); +        res &= face_res; // note that this result is never used +		 +        // MAINT-8264 - ignore bboxes of ill-formed faces. +        if (!face_res) +        { +            continue; +        }  		if (rebuild)  		{ -			if (i == 0) +            if (getRiggedVolume()) +            { +                LL_DEBUGS("RiggedBox") << "rebuilding box, face " << i << " extents " << face->mExtents[0] << ", " << face->mExtents[1] << LL_ENDL; +            } +			if (!any_valid_boxes)  			{  				min = face->mExtents[0];  				max = face->mExtents[1]; +                any_valid_boxes = true;  			}  			else  			{ @@ -1519,17 +1696,28 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)  			}  		}  	} -	 -	if (rebuild) -	{ -		mDrawable->setSpatialExtents(min,max); -		min.add(max); -		min.mul(0.5f); -		mDrawable->setPositionGroup(min);	 -	} -	updateRadius(); -	mDrawable->movePartition(); +    if (any_valid_boxes) +    { +        if (rebuild) +        { +            if (getRiggedVolume()) +            { +                LL_DEBUGS("RiggedBox") << "rebuilding got extents " << min << ", " << max << LL_ENDL; +            } +            mDrawable->setSpatialExtents(min,max); +            min.add(max); +            min.mul(0.5f); +            mDrawable->setPositionGroup(min);	 +        } + +        updateRadius(); +        mDrawable->movePartition(); +    } +    else +    { +        LL_DEBUGS("RiggedBox") << "genBBoxes failed to find any valid face boxes" << LL_ENDL; +    }  	return res;  } @@ -1677,6 +1865,11 @@ bool LLVOVolume::lodOrSculptChanged(LLDrawable *drawable, BOOL &compiled)  	if ((new_lod != old_lod) || mSculptChanged)  	{ +        if (mDrawable->isState(LLDrawable::RIGGED)) +        { +            updateVisualComplexity(); +        } +  		compiled = TRUE;  		sNumLODChanges += new_num_faces; @@ -3317,6 +3510,208 @@ BOOL LLVOVolume::setIsFlexible(BOOL is_flexible)  	return res;  } +const LLMeshSkinInfo* LLVOVolume::getSkinInfo() const +{ +    if (getVolume()) +    { +        return gMeshRepo.getSkinInfo(getVolume()->getParams().getSculptID(), this); +    } +    else +    { +        return NULL; +    } +} + +// virtual +BOOL LLVOVolume::isRiggedMesh() const +{ +    return isMesh() && getSkinInfo(); +} + +//---------------------------------------------------------------------------- +U32 LLVOVolume::getExtendedMeshFlags() const +{ +	const LLExtendedMeshParams *param_block =  +        (const LLExtendedMeshParams *)getParameterEntry(LLNetworkData::PARAMS_EXTENDED_MESH); +	if (param_block) +	{ +		return param_block->getFlags(); +	} +	else +	{ +		return 0; +	} +} + +void LLVOVolume::onSetExtendedMeshFlags(U32 flags) +{ + +    // The isAnySelected() check was needed at one point to prevent +    // graphics problems. These are now believed to be fixed so the +    // check has been disabled. +	if (/*!getRootEdit()->isAnySelected() &&*/ mDrawable.notNull()) +    { +        // Need to trigger rebuildGeom(), which is where control avatars get created/removed +        getRootEdit()->recursiveMarkForUpdate(TRUE); +    } +    if (isAttachment() && getAvatarAncestor()) +    { +        updateVisualComplexity(); +        if (flags & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG) +        { +            // Making a rigged mesh into an animated object +            getAvatarAncestor()->updateAttachmentOverrides(); +        } +        else +        { +            // Making an animated object into a rigged mesh +            getAvatarAncestor()->updateAttachmentOverrides(); +        } +    } +} + +void LLVOVolume::setExtendedMeshFlags(U32 flags) +{ +    U32 curr_flags = getExtendedMeshFlags(); +    if (curr_flags != flags) +    { +        bool in_use = true; +        setParameterEntryInUse(LLNetworkData::PARAMS_EXTENDED_MESH, in_use, true); +        LLExtendedMeshParams *param_block =  +            (LLExtendedMeshParams *)getParameterEntry(LLNetworkData::PARAMS_EXTENDED_MESH); +        if (param_block) +        { +            param_block->setFlags(flags); +        } +        parameterChanged(LLNetworkData::PARAMS_EXTENDED_MESH, true); +        LL_DEBUGS("AnimatedObjects") << this +                                     << " new flags " << flags << " curr_flags " << curr_flags +                                     << ", calling onSetExtendedMeshFlags()" +                                     << LL_ENDL; +        onSetExtendedMeshFlags(flags); +    } +} + +bool LLVOVolume::canBeAnimatedObject() const +{ +    F32 est_tris = recursiveGetEstTrianglesMax(); +    if (est_tris < 0 || est_tris > getAnimatedObjectMaxTris()) +    { +        return false; +    } +    return true; +} + +bool LLVOVolume::isAnimatedObject() const +{ +    LLVOVolume *root_vol = (LLVOVolume*)getRootEdit(); +    bool root_is_animated_flag = root_vol->getExtendedMeshFlags() & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG; +    return root_is_animated_flag; +} + +// Called any time parenting changes for a volume. Update flags and +// control av accordingly.  This is called after parent has been +// changed to new_parent, but before new_parent's mChildList has changed. + +// virtual +void LLVOVolume::onReparent(LLViewerObject *old_parent, LLViewerObject *new_parent) +{ +    LLVOVolume *old_volp = dynamic_cast<LLVOVolume*>(old_parent); + +    if (new_parent && !new_parent->isAvatar()) +    { +        if (mControlAvatar.notNull()) +        { +            // Here an animated object is being made the child of some +            // other prim. Should remove the control av from the child. +            LLControlAvatar *av = mControlAvatar; +            mControlAvatar = NULL; +            av->markForDeath(); +        } +    } +    if (old_volp && old_volp->isAnimatedObject()) +    { +        if (old_volp->getControlAvatar()) +        { +            // We have been removed from an animated object, need to do cleanup. +            old_volp->getControlAvatar()->updateAttachmentOverrides(); +            old_volp->getControlAvatar()->updateAnimations(); +        } +    } +} + +// This needs to be called after onReparent(), because mChildList is +// not updated until the end of LLViewerObject::addChild() + +// virtual +void LLVOVolume::afterReparent() +{ +    { +        LL_DEBUGS("AnimatedObjects") << "new child added for parent "  +            << ((LLViewerObject*)getParent())->getID() << LL_ENDL; +    } +                                                                                              +    if (isAnimatedObject() && getControlAvatar()) +    { +        LL_DEBUGS("AnimatedObjects") << "adding attachment overrides, parent is animated object "  +            << ((LLViewerObject*)getParent())->getID() << LL_ENDL; + +        // MAINT-8239 - doing a full rebuild whenever parent is set +        // makes the joint overrides load more robustly. In theory, +        // addAttachmentOverrides should be sufficient, but in +        // practice doing a full rebuild helps compensate for +        // notifyMeshLoaded() not being called reliably enough. +         +        // was: getControlAvatar()->addAttachmentOverridesForObject(this); +        //getControlAvatar()->rebuildAttachmentOverrides(); +        getControlAvatar()->updateAnimations(); +    } +    else +    { +        LL_DEBUGS("AnimatedObjects") << "not adding overrides, parent: "  +                                     << ((LLViewerObject*)getParent())->getID()  +                                     << " isAnimated: "  << isAnimatedObject() << " cav " +                                     << getControlAvatar() << LL_ENDL; +    } +} + +//---------------------------------------------------------------------------- +static LLTrace::BlockTimerStatHandle FTM_VOVOL_RIGGING_INFO("VOVol Rigging Info"); + +void LLVOVolume::updateRiggingInfo() +{ +    LL_RECORD_BLOCK_TIME(FTM_VOVOL_RIGGING_INFO); +    if (isRiggedMesh()) +    { +        const LLMeshSkinInfo* skin = getSkinInfo(); +        LLVOAvatar *avatar = getAvatar(); +        LLVolume *volume = getVolume(); +        if (skin && avatar && volume) +        { +            LL_DEBUGS("RigSpammish") << "starting, vovol " << this << " lod " << getLOD() << " last " << mLastRiggingInfoLOD << LL_ENDL; +            if (getLOD()>mLastRiggingInfoLOD || getLOD()==3) +            { +                // Rigging info may need update +                mJointRiggingInfoTab.clear(); +                for (S32 f = 0; f < volume->getNumVolumeFaces(); ++f) +                { +                    LLVolumeFace& vol_face = volume->getVolumeFace(f); +                    LLSkinningUtil::updateRiggingInfo(skin, avatar, vol_face); +                    if (vol_face.mJointRiggingInfoTab.size()>0) +                    { +                        mJointRiggingInfoTab.merge(vol_face.mJointRiggingInfoTab); +                    } +                } +                // Keep the highest LOD info available. +                mLastRiggingInfoLOD = getLOD(); +                LL_DEBUGS("RigSpammish") << "updated rigging info for LLVOVolume "  +                                         << this << " lod " << mLastRiggingInfoLOD  +                                         << LL_ENDL; +            } +        } +    } +} +  //----------------------------------------------------------------------------  void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point) @@ -3378,7 +3773,7 @@ void LLVOVolume::updateRadius()  BOOL LLVOVolume::isAttachment() const  { -	return mState != 0 ; +	return mAttachmentState != 0 ;  }  BOOL LLVOVolume::isHUDAttachment() const @@ -3386,7 +3781,7 @@ BOOL LLVOVolume::isHUDAttachment() const  	// *NOTE: we assume hud attachment points are in defined range  	// since this range is constant for backwards compatibility  	// reasons this is probably a reasonable assumption to make -	S32 attachment_id = ATTACHMENT_ID_FROM_STATE(mState); +	S32 attachment_id = ATTACHMENT_ID_FROM_STATE(mAttachmentState);  	return ( attachment_id >= 31 && attachment_id <= 38 );  } @@ -3466,16 +3861,25 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const  		path_params = volume_params.getPathParams();  		profile_params = volume_params.getProfileParams(); -		F32 weighted_triangles = -1.0; -		getStreamingCost(NULL, NULL, &weighted_triangles); - -		if (weighted_triangles > 0.0) +        LLMeshCostData costs; +		if (getCostData(costs))  		{ -			num_triangles = (U32)(weighted_triangles);  +            if (isAnimatedObject() && isRiggedMesh()) +            { +                // Scaling here is to make animated object vs +                // non-animated object ARC proportional to the +                // corresponding calculations for streaming cost. +                num_triangles = (ANIMATED_OBJECT_COST_PER_KTRI * 0.001 * costs.getEstTrisForStreamingCost())/0.06; +            } +            else +            { +                F32 radius = getScale().length()*0.5f; +                num_triangles = costs.getRadiusWeightedTris(radius); +            }  		}  	} -	if (num_triangles == 0) +	if (num_triangles <= 0)  	{  		num_triangles = 4;  	} @@ -3489,12 +3893,11 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const  			S32 size = gMeshRepo.getMeshSize(volume_params.getSculptID(), getLOD());  			if ( size > 0)  			{ -				if (gMeshRepo.getSkinInfo(volume_params.getSculptID(), this)) +				if (isRiggedMesh())  				{  					// weighted attachment - 1 point for every 3 bytes  					weighted_mesh = 1;  				} -  			}  			else  			{ @@ -3663,6 +4066,14 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const  		shame += media_faces * ARC_MEDIA_FACE_COST;  	} +    // Streaming cost for animated objects includes a fixed cost +    // per linkset. Add a corresponding charge here translated into +    // triangles, but not weighted by any graphics properties. +    if (isAnimatedObject() && isRootEdit()) +    { +        shame += (ANIMATED_OBJECT_BASE_COST/0.06) * 5.0f; +    } +  	if (shame > mRenderComplexity_current)  	{  		mRenderComplexity_current = (S32)shame; @@ -3671,16 +4082,68 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const  	return (U32)shame;  } -F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const +F32 LLVOVolume::getEstTrianglesMax() const  { -	F32 radius = getScale().length()*0.5f; - -	if (isMesh()) +	if (isMesh() && getVolume())  	{ -		return gMeshRepo.getStreamingCost(getVolume()->getParams().getSculptID(), radius, bytes, visible_bytes, mLOD, unscaled_value); +		return gMeshRepo.getEstTrianglesMax(getVolume()->getParams().getSculptID());  	} -	else +    return 0.f; +} + +F32 LLVOVolume::getEstTrianglesStreamingCost() const +{ +	if (isMesh() && getVolume())  	{ +		return gMeshRepo.getEstTrianglesStreamingCost(getVolume()->getParams().getSculptID()); +	} +    return 0.f; +} + +F32 LLVOVolume::getStreamingCost() const +{ +	F32 radius = getScale().length()*0.5f; +    F32 linkset_base_cost = 0.f; + +    LLMeshCostData costs; +    if (getCostData(costs)) +    { +        if (isAnimatedObject() && isRootEdit()) +        { +            // Root object of an animated object has this to account for skeleton overhead. +            linkset_base_cost = ANIMATED_OBJECT_BASE_COST; +        } +        if (isMesh()) +        { +            if (isAnimatedObject() && isRiggedMesh()) +            { +                return linkset_base_cost + costs.getTriangleBasedStreamingCost(); +            } +            else +            { +                return linkset_base_cost + costs.getRadiusBasedStreamingCost(radius); +            } +        } +        else +        { +            return linkset_base_cost + costs.getRadiusBasedStreamingCost(radius); +        } +    } +    else +    { +        return 0.f; +    } +} + +// virtual +bool LLVOVolume::getCostData(LLMeshCostData& costs) const +{ +    if (isMesh()) +    { +        return gMeshRepo.getCostData(getVolume()->getParams().getSculptID(), costs); +    } +    else +    {  		LLVolume* volume = getVolume();  		S32 counts[4];  		LLVolume::getLoDTriangleCounts(volume->getParams(), counts); @@ -3691,8 +4154,8 @@ F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_v  		header["medium_lod"]["size"] = counts[2] * 10;  		header["high_lod"]["size"] = counts[3] * 10; -		return LLMeshRepository::getStreamingCost(header, radius, NULL, NULL, -1, unscaled_value); -	}	 +		return gMeshRepo.getCostData(header, costs); +    }  }  //static  @@ -3762,6 +4225,21 @@ void LLVOVolume::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_u  	{  		mVolumeImpl->onParameterChanged(param_type, data, in_use, local_origin);  	} +    if (!local_origin && param_type == LLNetworkData::PARAMS_EXTENDED_MESH) +    { +        U32 extended_mesh_flags = getExtendedMeshFlags(); +        bool enabled =  (extended_mesh_flags & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG); +        bool was_enabled = (getControlAvatar() != NULL); +        if (enabled != was_enabled) +        { +            LL_DEBUGS("AnimatedObjects") << this +                                         << " calling onSetExtendedMeshFlags, enabled " << (U32) enabled +                                         << " was_enabled " << (U32) was_enabled +                                         << " local_origin " << (U32) local_origin +                                         << LL_ENDL; +            onSetExtendedMeshFlags(extended_mesh_flags); +        } +    }  	if (mDrawable.notNull())  	{  		BOOL is_light = getIsLight(); @@ -3775,10 +4253,17 @@ void LLVOVolume::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_u  void LLVOVolume::setSelected(BOOL sel)  {  	LLViewerObject::setSelected(sel); -	if (mDrawable.notNull()) -	{ -		markForUpdate(TRUE); -	} +    if (isAnimatedObject()) +    { +        getRootEdit()->recursiveMarkForUpdate(TRUE); +    } +    else +    { +        if (mDrawable.notNull()) +        { +            markForUpdate(TRUE); +        } +    }  }  void LLVOVolume::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) @@ -3888,6 +4373,22 @@ const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const  	return xform->getWorldMatrix();  } +void LLVOVolume::markForUpdate(BOOL priority) +{  +    if (debugLoggingEnabled("AnimatedObjectsLinkset")) +    { +        if (isAnimatedObject() && isRiggedMesh()) +        { +            std::string vobj_name = llformat("Vol%p", this); +            F32 est_tris = getEstTrianglesMax(); +            LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " markForUpdate, tris " << est_tris << LL_ENDL;  +        } +    } + +    LLViewerObject::markForUpdate(priority);  +    mVolumeChanged = TRUE;  +} +  LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const  {  	LLVector3 ret = pos - getRenderPosition(); @@ -4140,9 +4641,9 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&  bool LLVOVolume::treatAsRigged()  {  	return isSelected() && -			isAttachment() && -			mDrawable.notNull() && -			mDrawable->isState(LLDrawable::RIGGED); +        (isAttachment() || isAnimatedObject()) && +        mDrawable.notNull() && +        mDrawable->isState(LLDrawable::RIGGED);  }  LLRiggedVolume* LLVOVolume::getRiggedVolume() @@ -4172,9 +4673,7 @@ void LLVOVolume::updateRiggedVolume(bool force_update)  	}  	LLVolume* volume = getVolume(); - -	const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(volume->getParams().getSculptID(), this); - +	const LLMeshSkinInfo* skin = getSkinInfo();  	if (!skin)  	{  		clearRiggedVolume(); @@ -4182,7 +4681,6 @@ void LLVOVolume::updateRiggedVolume(bool force_update)  	}  	LLVOAvatar* avatar = getAvatar(); -  	if (!avatar)  	{  		clearRiggedVolume(); @@ -4197,7 +4695,6 @@ void LLVOVolume::updateRiggedVolume(bool force_update)  	}  	mRiggedVolume->update(skin, avatar, volume); -  }  static LLTrace::BlockTimerStatHandle FTM_SKIN_RIGGED("Skin"); @@ -4227,6 +4724,21 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons  	{  		copyVolumeFaces(volume);	  	} +    else +    { +#if 1 +        bool is_paused = avatar && avatar->areAnimationsPaused(); +		if (is_paused) +		{ +            S32 frames_paused = LLFrameTimer::getFrameCount() - avatar->getMotionController().getPausedFrame(); +            if (frames_paused > 2) +            { +                return; +            } +		} +#endif +    } +  	//build matrix palette  	static const size_t kMaxJoints = LL_MAX_JOINTS_PER_MESH_OBJECT; @@ -4235,6 +4747,9 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons  	U32 maxJoints = LLSkinningUtil::getMeshJointCount(skin);      LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, maxJoints, skin, avatar); +    S32 rigged_vert_count = 0; +    S32 rigged_face_count = 0; +    LLVector4a box_min, box_max;  	for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)  	{  		const LLVolumeFace& vol_face = volume->getVolumeFace(i); @@ -4256,6 +4771,8 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons  				LL_RECORD_BLOCK_TIME(FTM_SKIN_RIGGED);                  U32 max_joints = LLSkinningUtil::getMaxJointCount(); +                rigged_vert_count += dst_face.mNumVertices; +                rigged_face_count++;  				for (U32 j = 0; j < dst_face.mNumVertices; ++j)  				{  					LLMatrix4a final_mat; @@ -4270,11 +4787,17 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons  				}  				//update bounding box +				// VFExtents change  				LLVector4a& min = dst_face.mExtents[0];  				LLVector4a& max = dst_face.mExtents[1];  				min = pos[0];  				max = pos[1]; +                if (i==0) +                { +                    box_min = min; +                    box_max = max; +                }  				for (U32 j = 1; j < dst_face.mNumVertices; ++j)  				{ @@ -4282,6 +4805,9 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons  					max.setMax(max, pos[j]);  				} +                box_min.setMin(min,box_min); +                box_max.setMax(max,box_max); +  				dst_face.mCenter->setAdd(dst_face.mExtents[0], dst_face.mExtents[1]);  				dst_face.mCenter->mul(0.5f); @@ -4300,6 +4826,10 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons  			}  		}  	} +    mExtraDebugText = llformat("rigged %d/%d - box (%f %f %f) (%f %f %f)", +                               rigged_face_count, rigged_vert_count, +                               box_min[0], box_min[1], box_min[2], +                               box_max[0], box_max[1], box_max[2]);  }  U32 LLVOVolume::getPartitionType() const @@ -4788,27 +5318,19 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  	group->mBuilt = 1.f; -	LLVOAvatar* pAvatarVO = NULL; -  	LLSpatialBridge* bridge = group->getSpatialPartition()->asBridge(); -	if (bridge) -	{ -		if (bridge->mAvatar.isNull()) -		{ -			LLViewerObject* vobj = bridge->mDrawable->getVObj(); -			if (vobj) -			{ -				bridge->mAvatar = vobj->getAvatar(); -			} -		} - -		pAvatarVO = bridge->mAvatar; -	} +    LLViewerObject *vobj = NULL; +    LLVOVolume *vol_obj = NULL; -	if (pAvatarVO) +	if (bridge)  	{ -		pAvatarVO->subtractAttachmentArea( group->mSurfaceArea ); +        vobj = bridge->mDrawable->getVObj(); +        vol_obj = dynamic_cast<LLVOVolume*>(vobj);  	} +    if (vol_obj) +    { +        vol_obj->updateVisualComplexity(); +    }  	group->mGeometryBytes = 0;  	group->mSurfaceArea = 0; @@ -4851,7 +5373,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  		LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_FACE_LIST);  		//get all the faces into a list -		for (LLSpatialGroup::element_iter drawable_iter = group->getDataBegin(); drawable_iter != group->getDataEnd(); ++drawable_iter) +		for (LLSpatialGroup::element_iter drawable_iter = group->getDataBegin();  +             drawable_iter != group->getDataEnd(); ++drawable_iter)  		{  			LLDrawable* drawablep = (LLDrawable*)(*drawable_iter)->getDrawable(); @@ -4866,12 +5389,14 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  			}  			LLVOVolume* vobj = drawablep->getVOVolume(); - +              			if (!vobj)  			{  				continue;  			} +            std::string vobj_name = llformat("Vol%p", vobj); +  			if (vobj->isMesh() &&  				((vobj->getVolume() && !vobj->getVolume()->isMeshAssetLoaded()) || !gMeshRepo.meshRezEnabled()))  			{ @@ -4885,29 +5410,46 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  				group->mSurfaceArea += volume->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]);  			} +            bool is_mesh = vobj->isMesh(); +            F32 est_tris = vobj->getEstTrianglesMax(); + +            vobj->updateControlAvatar(); +             +            LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " rebuilding, isAttachment: " << (U32) vobj->isAttachment() +                                                << " is_mesh " << is_mesh +                                                << " est_tris " << est_tris +                                                << " is_animated " << vobj->isAnimatedObject() +                                                << " can_animate " << vobj->canBeAnimatedObject()  +                                                << " cav " << vobj->getControlAvatar()  +                                                << " lod " << vobj->getLOD() +                                                << " drawable rigged " << (drawablep->isState(LLDrawable::RIGGED)) +                                                << " drawable state " << drawablep->getState() +                                                << " playing " << (U32) (vobj->getControlAvatar() ? vobj->getControlAvatar()->mPlaying : false) +                                                << " frame " << LLFrameTimer::getFrameCount() +                                                << LL_ENDL; +  			llassert_always(vobj);  			vobj->updateTextureVirtualSize(true);  			vobj->preRebuild();  			drawablep->clearState(LLDrawable::HAS_ALPHA); -			bool rigged = vobj->isAttachment() &&  -                          vobj->isMesh() &&  -						  gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID(), vobj); - -			bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic(); - -			bool is_rigged = false; - -            if (rigged && pAvatarVO) +            if (vobj->isRiggedMesh() && +                ((vobj->isAnimatedObject() && vobj->getControlAvatar()) || +                 (!vobj->isAnimatedObject() && vobj->getAvatar())))              { -                pAvatarVO->addAttachmentOverridesForObject(vobj); -				if (!LLApp::isExiting() && pAvatarVO->isSelf() && debugLoggingEnabled("AvatarAttachments")) -                { -                    bool verbose = true; -					pAvatarVO->showAttachmentOverrides(verbose); -				} +                vobj->getAvatar()->addAttachmentOverridesForObject(vobj, NULL, false);              } +             +            // Standard rigged mesh attachments:  +			bool rigged = !vobj->isAnimatedObject() && vobj->isRiggedMesh() && vobj->isAttachment(); +            // Animated objects. Have to check for isRiggedMesh() to +            // exclude static objects in animated object linksets. +			rigged = rigged || (vobj->isAnimatedObject() && vobj->isRiggedMesh() && +                vobj->getControlAvatar() && vobj->getControlAvatar()->mPlaying); + +			bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic(); +			bool any_rigged_face = false;  			//for each face  			for (S32 i = 0; i < drawablep->getNumFaces(); i++) @@ -4925,7 +5467,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  				//sum up face verts and indices  				drawablep->updateFaceSize(i); -				if (rigged)  +				if (rigged)  				{  					if (!facep->isState(LLFace::RIGGED))  					{ //completely reset vertex buffer @@ -4933,7 +5475,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  					}  					facep->setState(LLFace::RIGGED); -					is_rigged = true; +					any_rigged_face = true;  					//get drawpool of avatar with rigged face  					LLDrawPoolAvatar* pool = get_avatar_drawpool(vobj);				 @@ -5277,7 +5819,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  				}		  			} -			if (is_rigged) +			if (any_rigged_face)  			{  				if (!drawablep->isState(LLDrawable::RIGGED))  				{ @@ -5291,6 +5833,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  			else  			{  				drawablep->clearState(LLDrawable::RIGGED); +                vobj->updateRiggedVolume();  			}  		}  	} @@ -5350,9 +5893,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  			LLDrawable* drawablep = (LLDrawable*)(*drawable_iter)->getDrawable();  			if(drawablep)  			{ -			drawablep->clearState(LLDrawable::REBUILD_ALL); -		} -	} +                drawablep->clearState(LLDrawable::REBUILD_ALL); +            } +        }  	}  	group->mLastUpdateTime = gFrameTimeSeconds; @@ -5365,11 +5908,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  	}  	mFaceList.clear(); - -	if (pAvatarVO) -	{ -        pAvatarVO->addAttachmentArea( group->mSurfaceArea ); -	}  }  static LLTrace::BlockTimerStatHandle FTM_REBUILD_MESH_FLUSH("Flush Mesh"); @@ -5398,6 +5936,15 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)  			if (drawablep && !drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL) && !drawablep->isState(LLDrawable::RIGGED) )  			{  				LLVOVolume* vobj = drawablep->getVOVolume(); +                if (debugLoggingEnabled("AnimatedObjectsLinkset")) +                { +                    if (vobj->isAnimatedObject() && vobj->isRiggedMesh()) +                    { +                        std::string vobj_name = llformat("Vol%p", vobj); +                        F32 est_tris = vobj->getEstTrianglesMax(); +                        LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " rebuildMesh, tris " << est_tris << LL_ENDL;  +                    } +                }  				if (vobj->isNoLOD()) continue;  				vobj->preRebuild(); diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index a0342d31a2..0882fc095d 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -64,6 +64,8 @@ public:  	}  	void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume); + +    std::string mExtraDebugText;  };  // Base class for implementations of the volume - Primitive, Flexible Object, etc. @@ -136,8 +138,10 @@ public:  	/*virtual*/	const LLMatrix4	getRenderMatrix() const;  				typedef std::map<LLUUID, S32> texture_cost_t;  				U32 	getRenderCost(texture_cost_t &textures) const; -				F32		getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const; -	/*virtual*/	F32		getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL) { return getStreamingCost(bytes, visible_bytes, NULL); } +    /*virtual*/	F32		getEstTrianglesMax() const; +    /*virtual*/	F32		getEstTrianglesStreamingCost() const; +    /* virtual*/ F32	getStreamingCost() const; +    /*virtual*/ bool getCostData(LLMeshCostData& costs) const;  	/*virtual*/ U32		getTriangleCount(S32* vcount = NULL) const;  	/*virtual*/ U32		getHighLODTriangleCount(); @@ -163,7 +167,7 @@ public:  	/*virtual*/ F32  	getRadius() const						{ return mVObjRadius; };  				const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const; -				void	markForUpdate(BOOL priority)			{ LLViewerObject::markForUpdate(priority); mVolumeChanged = TRUE; } +				void	markForUpdate(BOOL priority);  				void	markForUnload()							{ LLViewerObject::markForUnload(TRUE); mVolumeChanged = TRUE; }  				void    faceMappingChanged()                    { mFaceMappingChanged=TRUE; }; @@ -264,12 +268,29 @@ public:  	virtual BOOL isFlexible() const;  	virtual BOOL isSculpted() const;  	virtual BOOL isMesh() const; +	virtual BOOL isRiggedMesh() const;  	virtual BOOL hasLightTexture() const; +      	BOOL isVolumeGlobal() const;  	BOOL canBeFlexible() const;  	BOOL setIsFlexible(BOOL is_flexible); +    const LLMeshSkinInfo* getSkinInfo() const; +     +    // Extended Mesh Properties +    U32 getExtendedMeshFlags() const; +    void onSetExtendedMeshFlags(U32 flags); +    void setExtendedMeshFlags(U32 flags); +    bool canBeAnimatedObject() const; +    bool isAnimatedObject() const; +    virtual void onReparent(LLViewerObject *old_parent, LLViewerObject *new_parent); +    virtual void afterReparent(); + +    //virtual +    void updateRiggingInfo(); +    S32 mLastRiggingInfoLOD; +          // Functions that deal with media, or media navigation      // Update this object's media data with the given media data array @@ -303,7 +324,10 @@ public:  	bool hasMedia() const;  	LLVector3 getApproximateFaceNormal(U8 face_id); -	 + +    // Flag any corresponding avatars as needing update. +    void updateVisualComplexity(); +      	void notifyMeshLoaded();  	// Returns 'true' iff the media data for this object is in flight @@ -332,7 +356,7 @@ public:  	void clearRiggedVolume();  protected: -	S32	computeLODDetail(F32	distance, F32 radius, F32 lod_factor); +	S32	computeLODDetail(F32 distance, F32 radius, F32 lod_factor);  	BOOL calcLOD();  	LLFace* addFace(S32 face_index);  	void updateTEData(); @@ -356,6 +380,9 @@ public:  	LLViewerTextureAnim *mTextureAnimp;  	U8 mTexAnimMode; +    F32 mLODDistance; +    F32 mLODAdjustedDistance; +    F32 mLODRadius;  private:  	friend class LLDrawable;  	friend class LLFace; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index e541c1054e..4bfa749d9f 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -3356,6 +3356,18 @@ void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags f  {  	if (drawablep && !drawablep->isDead() && assertInitialized())  	{ +        if (debugLoggingEnabled("AnimatedObjectsLinkset")) +        { +            LLVOVolume *vol_obj = drawablep->getVOVolume(); +            if (vol_obj && vol_obj->isAnimatedObject() && vol_obj->isRiggedMesh()) +            { +                std::string vobj_name = llformat("Vol%p", vol_obj); +                F32 est_tris = vol_obj->getEstTrianglesMax(); +                LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " markRebuild, tris " << est_tris  +                                                    << " priority " << (S32) priority << " flag " << std::hex << flag << LL_ENDL;  +            } +        } +      		if (!drawablep->isState(LLDrawable::BUILT))  		{  			priority = true; diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index f43607ccb0..85c13487ee 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -513,11 +513,11 @@ public:  		RENDER_DEBUG_TEXTURE_AREA		= 0x00000100,  		RENDER_DEBUG_FACE_AREA			= 0x00000200,  		RENDER_DEBUG_PARTICLES			= 0x00000400, -		RENDER_DEBUG_GLOW				= 0x00000800, +		RENDER_DEBUG_GLOW				= 0x00000800, // not used  		RENDER_DEBUG_TEXTURE_ANIM		= 0x00001000,  		RENDER_DEBUG_LIGHTS				= 0x00002000,  		RENDER_DEBUG_BATCH_SIZE			= 0x00004000, -		RENDER_DEBUG_ALPHA_BINS			= 0x00008000, +		RENDER_DEBUG_ALPHA_BINS			= 0x00008000, // not used  		RENDER_DEBUG_RAYCAST            = 0x00010000,  		RENDER_DEBUG_AVATAR_DRAW_INFO	= 0x00020000,  		RENDER_DEBUG_SHADOW_FRUSTA		= 0x00040000, @@ -531,8 +531,9 @@ public:  		RENDER_DEBUG_NORMALS	        = 0x04000000,  		RENDER_DEBUG_LOD_INFO	        = 0x08000000,  		RENDER_DEBUG_RENDER_COMPLEXITY  = 0x10000000, -		RENDER_DEBUG_ATTACHMENT_BYTES	= 0x20000000, -		RENDER_DEBUG_TEXEL_DENSITY		= 0x40000000 +		RENDER_DEBUG_ATTACHMENT_BYTES	= 0x20000000, // not used +		RENDER_DEBUG_TEXEL_DENSITY		= 0x40000000, +		RENDER_DEBUG_TRIANGLE_COUNT		= 0x80000000   	};  public: diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml index 2750316f2e..3db431de1b 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -14,6 +14,7 @@    <string name="status_idle"></string>    <string name="status_parse_error">Error: Dae parsing issue - see log for details.</string> +  <string name="status_bind_shape_orientation">Warning: bind shape matrix is not in standard X-forward orientation.</string>    <string name="status_material_mismatch">Error: Material of model is not a subset of reference model.</string>    <string name="status_reading_file">Loading...</string>    <string name="status_generating_meshes">Generating Meshes...</string> diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index bdcf3648fa..d9a15fed9e 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -2,7 +2,7 @@  <floater   positioning="cascading"   legacy_header_height="18" - height="590" + height="600"   layout="topleft"   bg_opaque_image="Window_NoTitle_Foreground"   bg_alpha_image="Window_NoTitle_Background" @@ -2135,7 +2135,7 @@ even though the user gets a free copy.          <panel           border="false"           follows="all" -         height="367" +         height="387"           label="Features"           layout="topleft"           left_delta="0" @@ -2173,13 +2173,23 @@ even though the user gets a free copy.                  Edit object features:              </text>              <check_box -             height="19" +             height="15" +             label="Animated Mesh" +             layout="topleft" +             left="10" +             name="Animated Mesh Checkbox Ctrl" +             tool_tip="Allows rigged mesh objects to be animated independently" +             top_pad="10" +             width="121" /> +            <check_box +             height="10"               label="Flexible Path" +             follows="left|top"               layout="topleft"               left="10"               name="Flexible1D Checkbox Ctrl"               tool_tip="Allows object to flex about the Z axis (Client-side only)" -             top_pad="20" +             top_pad="15"               width="121" />              <spinner               follows="left|top" diff --git a/indra/newview/skins/default/xui/en/menu_object.xml b/indra/newview/skins/default/xui/en/menu_object.xml index dc9622a27d..ce34508303 100644 --- a/indra/newview/skins/default/xui/en/menu_object.xml +++ b/indra/newview/skins/default/xui/en/menu_object.xml @@ -214,4 +214,20 @@      <menu_item_call.on_enable       function="EnableMuteParticle" />    </menu_item_call> +    <menu_item_call +		 label="Dump XML" +         name="Dump XML"> +            <menu_item_call.on_click +             function="Advanced.AppearanceToXML" /> +            <menu_item_call.on_visible +             function="Advanced.EnableAppearanceToXML"/> +    </menu_item_call> +    <menu_item_call +		 label="Reset Skeleton" +         name="Reset Skeleton"> +            <menu_item_call.on_click +                function="Avatar.ResetSkeleton" /> +            <menu_item_call.on_visible +             function="Avatar.EnableResetSkeleton"/> +    </menu_item_call>  </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 7c6b1bc357..8490d792fa 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2652,6 +2652,16 @@             parameter="lod info" />          </menu_item_check>          <menu_item_check +         label="Triangle Count" +         name="Triangle Count"> +          <menu_item_check.on_check +           function="Advanced.CheckInfoDisplay" +           parameter="triangle count" /> +          <menu_item_check.on_click +           function="Advanced.ToggleInfoDisplay" +           parameter="triangle count" /> +        </menu_item_check> +        <menu_item_check           label="Build Queue"           name="Build Queue">            <menu_item_check.on_check diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 9eaa5330c3..7dc3d6b08f 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -10215,6 +10215,14 @@ You have been teleported by the object '[OBJECT_NAME]' owned by an unknown user.  Unable to create requested object. The region is full.    </notification> +   <notification +    icon="alertmodal.tga" +   name="CantCreateAnimatedObjectTooLarge" +   type="notify"> +   <tag>fail</tag> +Unable to create requested animated object because it exceeds the rigged triangle limit. +  </notification> +    <notification     icon="alertmodal.tga"     name="CantAttackMultipleObjOneSpot" @@ -10345,6 +10353,46 @@ You are not allowed to change this shape.    <notification     icon="alertmodal.tga" +   name="NoPermsTooManyAttachedAnimatedObjects" +   type="notify"> +   <tag>fail</tag> +Operation would cause the number of attached animated objects to exceed the limit. +  </notification> + +  <notification +   icon="alertmodal.tga" +   name="NoPermsLinkAnimatedObjectTooLarge" +   type="notify"> +   <tag>fail</tag> +Can't link these objects because the resulting animated object would exceed the rigged triangle limit. +  </notification> + +  <notification +   icon="alertmodal.tga" +   name="NoPermsSetFlagAnimatedObjectTooLarge" +   type="notify"> +   <tag>fail</tag> +Can't make this object into an animated object because it would exceed the rigged triangle limit. +  </notification> + +  <notification +   icon="alertmodal.tga" +   name="CantChangeAnimatedObjectStateInsufficientLand" +   type="notify"> +   <tag>fail</tag> +Can't change animated object state for this object because it would cause parcel limit to be exceeded. +  </notification> + +  <notification +   icon="alertmodal.tga" +   name="ErrorNoMeshData" +   type="notify"> +   <tag>fail</tag> +Server error: cannot complete this operation because mesh data is not loaded. +  </notification> + +  <notification +   icon="alertmodal.tga"     name="NoAccessToClaimObjects"     type="notify">     <tag>fail</tag> diff --git a/indra/test/test.cpp b/indra/test/test.cpp index 80c84d9bea..bdeaeec970 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -34,7 +34,6 @@   *   */ -  #include "linden_common.h"  #include "llerrorcontrol.h"  #include "lltut.h" @@ -685,5 +684,4 @@ int main(int argc, char **argv)  	return retval;  	//delete mycallback; -  } diff --git a/scripts/content_tools/anim_tool.py b/scripts/content_tools/anim_tool.py index 3496617b21..3aef8cd5ab 100644 --- a/scripts/content_tools/anim_tool.py +++ b/scripts/content_tools/anim_tool.py @@ -610,12 +610,16 @@ def main(*argv):      parser = argparse.ArgumentParser(description="process SL animations")      parser.add_argument("--verbose", help="verbose flag", action="store_true") -    parser.add_argument("--dump", metavar="FILEPATH", help="dump to specified file") +    parser.add_argument("--dump", help="dump to stdout", action="store_true")      parser.add_argument("--rot", help="specify sequence of rotations", type=float_triple, nargs="+")      parser.add_argument("--rand_pos", help="request NUM random positions (default %(default)s)",                          metavar="NUM", type=int, default=2)      parser.add_argument("--reset_pos", help="request original positions", action="store_true")      parser.add_argument("--pos", help="specify sequence of positions", type=float_triple, nargs="+") +    parser.add_argument("--duration", help="specify duration", type=float) +    parser.add_argument("--loop_in", help="specify loop in time", type=float) +    parser.add_argument("--loop_out", help="specify loop out time", type=float) +    parser.add_argument("--num_pos", help="number of positions to create", type=int, default=2)      parser.add_argument("--delete_joints", help="specify joints to be deleted", nargs="+",                          metavar="JOINT")      parser.add_argument("--joints", help="specify joints to be added or modified", nargs="+", @@ -696,8 +700,17 @@ def main(*argv):          print "set joint priority",args.joint_priority          for joint in anim.joints:              joint.joint_priority = args.joint_priority +    if args.duration is not None: +        print "set duration",args.duration +        anim.duration = args.duration +    if args.loop_in is not None: +        print "set loop_in",args.loop_in +        anim.loop_in_point = args.loop_in +    if args.loop_out is not None: +        print "set loop_out",args.loop_out +        anim.loop_out_point = args.loop_out      if args.dump: -        anim.dump(args.dump) +        anim.dump("-")      if args.summary:          anim.summary()      if args.outfilename: diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg index c56eaae6fe..18264ee59c 100755 --- a/scripts/messages/message_template.msg +++ b/scripts/messages/message_template.msg @@ -8,7 +8,7 @@ version 2.0  // for each type is listed below:  // Low: 423  // Medium: 18 -// High: 29 +// High: 30  // PLEASE UPDATE THIS WHEN YOU ADD A NEW MESSAGE! @@ -3571,7 +3571,6 @@ version 2.0  	}  } -  // AvatarAppearance - Update visual params  {  	AvatarAppearance Low 158 Trusted Zerocoded @@ -7286,6 +7285,29 @@ version 2.0  // ************************************************************************* +// Object animation messages +// ************************************************************************* + +// Note this is basically identical to AvatarAnimation. +// Needs to be a different message because existing viewers +// have insufficiently smart handler functions. + +// ObjectAnimation - Update animation state +// simulator --> viewer +{ +        ObjectAnimation High 30 Trusted Unencoded +        { +                Sender                  Single +                {       ID                      LLUUID  } +        } +        { +                AnimationList Variable +                { AnimID                LLUUID } +                { AnimSequenceID S32 } +        } +} + +// *************************************************************************  // Asset storage messages  // ************************************************************************* diff --git a/scripts/messages/message_template.msg.sha1 b/scripts/messages/message_template.msg.sha1 index 5bc06ec042..db87ad5e77 100755 --- a/scripts/messages/message_template.msg.sha1 +++ b/scripts/messages/message_template.msg.sha1 @@ -1 +1 @@ -337f351910b0c8821cb3d447bc6578516a043c80
\ No newline at end of file +55df2c7135733c5da64ef8f01859b83a433a3a09
\ No newline at end of file diff --git a/scripts/testing/lsl/axon_test_region_driver.lsl b/scripts/testing/lsl/axon_test_region_driver.lsl new file mode 100644 index 0000000000..dcf146a9cf --- /dev/null +++ b/scripts/testing/lsl/axon_test_region_driver.lsl @@ -0,0 +1,54 @@ +list buttons = ["anim start", "anim stop", "step", "verbose on", "verbose off", " "]; +string dialogInfo = "\nPlease make a choice."; +  +key ToucherID; +integer dialogChannel; +integer listenHandle; +integer commandChannel; +  +default +{ +    state_entry() +    { +        dialogChannel = -1 - (integer)("0x" + llGetSubString( (string)llGetKey(), -7, -1) ); +        commandChannel = -2001; +    } +  +    touch_start(integer num_detected) +    { +        ToucherID = llDetectedKey(0); +        llListenRemove(listenHandle); +        listenHandle = llListen(dialogChannel, "", ToucherID, ""); +        llDialog(ToucherID, dialogInfo, buttons, dialogChannel); +        //llSetTimerEvent(60.0); // Here we set a time limit for responses +    } +  +    listen(integer channel, string name, key id, string message) +    { +        if (message == "-") +        { +            llDialog(ToucherID, dialogInfo, buttons, dialogChannel); +            return; +        } +  +        llListenRemove(listenHandle); +        //  stop timer since the menu was clicked +        llSetTimerEvent(0); + +        //llOwnerSay("Sending message " + message + " on channel " + (string)commandChannel); +        llRegionSay(commandChannel, message); +    } +  +    timer() +    { +    //  stop timer +        llSetTimerEvent(0); +  +        llListenRemove(listenHandle); +        //llWhisper(0, "Sorry. You snooze; you lose."); +    } +} + +// Local Variables: +// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/axon_test_region_driver.lsl" +// End: diff --git a/scripts/testing/lsl/cycle_object_animations.lsl b/scripts/testing/lsl/cycle_object_animations.lsl new file mode 100644 index 0000000000..46910e3656 --- /dev/null +++ b/scripts/testing/lsl/cycle_object_animations.lsl @@ -0,0 +1,118 @@ +integer listenHandle;  +integer verbose; +integer current_animation_number; +string NowPlaying; + +say_if_verbose(integer channel, string message) +{ +    if (verbose) +    { +        llSay(channel, message); +    } +} + +stop_all_animations() +{ +    integer count = llGetInventoryNumber(INVENTORY_ANIMATION); +    string ItemName; +    string NowPlaying; +    while (count--) +    { +        ItemName = llGetInventoryName(INVENTORY_ANIMATION, count); +        say_if_verbose(0, "Stopping " + ItemName); +        llStopObjectAnimation(ItemName); +    } +} + +start_cycle_animations() +{ +    current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION); +    next_animation(); // Do first iteration without waiting for timer +    llSetTimerEvent(5.0); +} + +next_animation() +{ +    string ItemName; +    if (NowPlaying != "") +    { +        say_if_verbose(0, "Stopping " + NowPlaying); +        llStopObjectAnimation(NowPlaying); +    } +    if (current_animation_number--) +    { +        ItemName = llGetInventoryName(INVENTORY_ANIMATION, current_animation_number); +        say_if_verbose(0, "Starting " + ItemName); +        llStartObjectAnimation(ItemName); +        NowPlaying = ItemName; +    } +    else +    { +        // Start again at the top +        current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION); +    } +} + +stop_cycle_animations() +{ +    llSetTimerEvent(0); +} + +default +{ +    state_entry() +    { +        say_if_verbose(0, "Animated Object here"); +        listenHandle = llListen(-2001,"","","");   +        verbose = 0; + +        stop_all_animations(); +    } + +    listen(integer channel, string name, key id, string message) +    { +        //llOwnerSay("got message " + name + " " + (string) id + " " + message); +        list words = llParseString2List(message,[" "],[]); +        string command = llList2String(words,0); +        string option = llList2String(words,1); +        if (command=="anim") +        { +            stop_all_animations(); +            if (option=="start") +            { +                start_cycle_animations(); +            } +            else if (option=="stop") +            { +                stop_cycle_animations(); +            } +        } +        if (command=="verbose") +        { +            if (option=="on") +            { +                verbose = 1; +            } +            else if (option=="off") +            { +                verbose = 0; +            } +        } +    } + +    timer() +    { +        say_if_verbose(0, "timer triggered"); +        next_animation(); +    } +     +    touch_start(integer total_number) +    { +        say_if_verbose(0, "Touch started."); +        start_cycle_animations(); +    } +} + +// Local Variables: +// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/cycle_object_animations.lsl" +// End: diff --git a/scripts/testing/lsl/cycle_object_animations_v2.lsl b/scripts/testing/lsl/cycle_object_animations_v2.lsl new file mode 100644 index 0000000000..60879fcce2 --- /dev/null +++ b/scripts/testing/lsl/cycle_object_animations_v2.lsl @@ -0,0 +1,133 @@ +integer listenHandle;  +integer verbose; +integer current_animation_number; +string NowPlaying; + +say_if_verbose(integer channel, string message) +{ +    if (verbose) +    { +        llSay(channel, message); +    } +} + +stop_all_animations() +{ +    list curr_anims = llGetObjectAnimationNames(); +    say_if_verbose(0,"stopping all, curr_anims are " + (string) curr_anims); +    integer length = llGetListLength(curr_anims); +    integer index = 0; +    while (index < length) +    { +        string anim = llList2String(curr_anims, index); +        say_if_verbose(0, "Stopping " + anim); +        llStopObjectAnimation(anim); +        // This check isn't really needed, just included to demonstrate is_animation_running() +        if (is_animation_running(anim)) +        { +            say_if_verbose(0, "ERROR - failed to stop " + anim + "!"); +        } +        ++index; +    } +} + +integer is_animation_running(string anim) +{ +    list curr_anims = llGetObjectAnimationNames(); +    return ~llListFindList(curr_anims, (list)anim); +} + +start_cycle_animations() +{ +    current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION); +    next_animation(); // Do first iteration without waiting for timer +    llSetTimerEvent(5.0); +} + +next_animation() +{ +    string ItemName; +    if (NowPlaying != "") +    { +        say_if_verbose(0, "Stopping " + NowPlaying); +        llStopObjectAnimation(NowPlaying); +    } +    if (current_animation_number--) +    { +        ItemName = llGetInventoryName(INVENTORY_ANIMATION, current_animation_number); +        say_if_verbose(0, "Starting " + ItemName); +        llStartObjectAnimation(ItemName); +        key anim_id = llGetInventoryKey(ItemName); +        say_if_verbose(0, "Started item " + ItemName + " inv key " + (string) anim_id); +        NowPlaying = ItemName; +    } +    else +    { +        // Start again at the top +        current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION); +    } +} + +stop_cycle_animations() +{ +    llSetTimerEvent(0); +} + +default +{ +    state_entry() +    { +        say_if_verbose(0, "Animated Object here"); +        listenHandle = llListen(-2001,"","","");   +        verbose = 0; + +        stop_all_animations(); +    } + +    listen(integer channel, string name, key id, string message) +    { +        //llOwnerSay("got message " + name + " " + (string) id + " " + message); +        list words = llParseString2List(message,[" "],[]); +        string command = llList2String(words,0); +        string option = llList2String(words,1); +        if (command=="anim") +        { +            stop_all_animations(); +            if (option=="start") +            { +                start_cycle_animations(); +            } +            else if (option=="stop") +            { +                stop_cycle_animations(); +            } +        } +        if (command=="verbose") +        { +            if (option=="on") +            { +                verbose = 1; +            } +            else if (option=="off") +            { +                verbose = 0; +            } +        } +    } + +    timer() +    { +        say_if_verbose(0, "timer triggered"); +        next_animation(); +    } +     +    touch_start(integer total_number) +    { +        say_if_verbose(0, "Touch started."); +        start_cycle_animations(); +    } +} + +// Local Variables: +// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/cycle_object_animations_v2.lsl" +// End: diff --git a/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl b/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl new file mode 100644 index 0000000000..cc5b899b67 --- /dev/null +++ b/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl @@ -0,0 +1,90 @@ +integer listenHandle;  +integer verbose; +integer num_steps = 12; +float circle_time = 5.0; +integer circle_step; +vector circle_pos; +vector circle_center; +float circle_radius; + +start_circle(vector center, float radius) +{ +    vector currentPosition = llGetPos(); +    circle_center = center; +    circle_radius = radius; +    circle_step = 0; +    llSetTimerEvent(circle_time/num_steps); +    llTargetOmega(<0.0, 0.0, 1.0>, TWO_PI/circle_time, 1.0); +} + +stop_circle() +{ +    llSetTimerEvent(0); +    llTargetOmega(<0.0, 0.0, 1.0>, TWO_PI/circle_time, 0.0); +    llSetRegionPos(circle_center); +} + +next_circle() +{ +    float rad = (circle_step * TWO_PI)/num_steps; +    float x = circle_center.x + llCos(rad)*circle_radius; +    float y = circle_center.y + llSin(rad)*circle_radius; +    float z = circle_center.z; +    llSetRegionPos(<x,y,z>); +    circle_step = (circle_step+1)%num_steps; +} + +default +{ +    state_entry() +    { +        //llSay(0, "Hello, Avatar!"); +        listenHandle = llListen(-2001,"","","");   +        verbose = 0; +        circle_center = llGetPos(); +    } + +    listen(integer channel, string name, key id, string message) +    { +        //llOwnerSay("got message " + name + " " + (string) id + " " + message); +        list words = llParseString2List(message,[" "],[]); +        string command = llList2String(words,0); +        string option = llList2String(words,1); +        if (command=="anim") +        { +            if (option=="start") +            { +                start_circle(llGetPos(), 3.0); +            } +            else if (option=="stop") +            { +                stop_circle(); +            } +        } +        if (command=="verbose") +        { +            if (option=="on") +            { +                verbose = 1; +            } +            else if (option=="off") +            { +                verbose = 0; +            } +        } +        if (command=="step") +        { +            llSetTimerEvent(0); +            next_circle(); +        } +    } + +    timer() +    { +        next_circle(); +    } +} + +// Local Variables: +// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl" +// End: | 
