diff options
61 files changed, 3045 insertions, 700 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/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp index a3d5679f65..89335a20f5 100644 --- a/indra/llcharacter/lljoint.cpp +++ b/indra/llcharacter/lljoint.cpp @@ -881,7 +881,7 @@ void LLJoint::setWorldRotation( const LLQuaternion& rot )  //--------------------------------------------------------------------  const LLVector3& LLJoint::getScale()  { -	return mXform.getScale();	 +    return mXform.getScale();  }  //-------------------------------------------------------------------- diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index 35e76f1d9d..4c3a676382 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -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 diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index 9d9c64f4f0..bcd0bfc685 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -150,10 +150,13 @@ public:  	BOOL isPaused() const { return mPaused; }  	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); 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 abe5fda603..627ca05f14 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/llvolume.cpp b/indra/llmath/llvolume.cpp index 5068c9c685..f9d24be055 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -1,5 +1,4 @@  /**  -   * @file llvolume.cpp   *   * $LicenseInfo:firstyear=2002&license=viewerlgpl$ 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/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index bfa65666b5..c847cf653f 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1599,6 +1599,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; @@ -2026,3 +2028,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 19d9d52817..9216c04229 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 b4e930d062..3787a25a9b 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -149,6 +149,7 @@ set(viewer_SOURCE_FILES      llcommunicationchannel.cpp      llcompilequeue.cpp      llconfirmationmanager.cpp +    llcontrolavatar.cpp      llconversationlog.cpp      llconversationloglist.cpp      llconversationloglistitem.cpp @@ -769,6 +770,7 @@ set(viewer_HEADER_FILES      llcommunicationchannel.h      llcompilequeue.h      llconfirmationmanager.h +    llcontrolavatar.h      llconversationlog.h      llconversationloglist.h      llconversationloglistitem.h diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index ecd7c4bc36..5329b76390 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -51,6 +51,7 @@  					<key>tags</key>  						<array>  						<!-- sample entry for debugging specific items	 +                             <string>AXON</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 9f82eb5b85..2fe01ab22f 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2182,6 +2182,28 @@        <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>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>DebugAvatarAppearanceMessage</key>    <map>      <key>Comment</key> @@ -10393,6 +10415,17 @@  		<key>Value</key>  		<integer>0</integer>  	</map> +	<key>RenderForceVolumeLOD</key> +    <map> +      <key>Comment</key> +      <string>Override for all volume LODs</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>S32</string> +      <key>Value</key> +      <integer>-1</integer> +    </map>  	<key>RenderVolumeLODFactor</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 0fb811a386..9f13a944ac 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 3f4a111a9a..fc41697367 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -592,6 +592,7 @@ static void settings_to_globals()  	LLVertexBuffer::sUseVAO = gSavedSettings.getBOOL("RenderUseVAO");  	LLImageGL::sGlobalUseAnisotropic	= gSavedSettings.getBOOL("RenderAnisotropic");  	LLImageGL::sCompressTextures		= gSavedSettings.getBOOL("RenderCompressTextures"); +	LLVOVolume::sForceLOD				= gSavedSettings.getS32("RenderForceVolumeLOD");  	LLVOVolume::sLODFactor				= gSavedSettings.getF32("RenderVolumeLODFactor");  	LLVOVolume::sDistanceFactor			= 1.f-LLVOVolume::sLODFactor * 0.1f;  	LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor"); diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp new file mode 100644 index 0000000000..7e89a97c03 --- /dev/null +++ b/indra/newview/llcontrolavatar.cpp @@ -0,0 +1,404 @@ +/** + * @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" + +LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) : +    LLVOAvatar(id, pcode, regionp), +    mPlaying(false), +    mGlobalScale(1.0f), +    mMarkedForDeath(false) +{ +    mIsControlAvatar = true; +    mEnableDefaultMotions = false; +} + +// virtual +LLControlAvatar::~LLControlAvatar() +{ +} + +// virtual +void LLControlAvatar::initInstance() +{ +	// AXON - 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(); +} + +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 +        { +            setPositionAgent(mRootVolp->getRenderPosition()); +            LLQuaternion obj_rot = mRootVolp->getRotation(); +            LLQuaternion result_rot = obj_rot; +            setRotation(result_rot); +            mRoot->setWorldRotation(result_rot); +            mRoot->setPosition(mRootVolp->getRenderPosition()); +        } +    } +} + +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; +        // AXON - 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(); + +    // AXON testing scale + +    // 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; +     +	cav->createDrawable(&gPipeline); +	cav->mIsDummy = TRUE; +	cav->mSpecialRenderMode = 1; +	cav->updateJointLODs(); +	cav->updateGeometry(cav->mDrawable); +	cav->hideSkirt(); + +    // 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; +        S32 total_tris = 0; +        S32 total_verts = 0; +        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; +            lod_string += llformat("%d",volp->getLOD()); +            if (volp && volp->mDrawable) +            { +                if (volp->mDrawable->isActive()) +                { +                    active_string += "A"; +                } +                else +                { +                    active_string += "S"; +                } +                if (volp->isRiggedMesh()) +                { +                    // Rigged/animateable mesh +                    type_string += "R"; +                } +                else if (volp->isMesh()) +                { +                    // Static mesh +                    type_string += "M"; +                } +                else +                { +                    // Any other prim +                    type_string += "P"; +                } +            } +            else +            { +                active_string += "-"; +                type_string += "-"; +            } +        } +        addDebugText(llformat("CAV obj %d anim %d active %s", +                              total_linkset_count, animated_volume_count, active_string.c_str())); +        addDebugText(llformat("types %s lods %s", type_string.c_str(), lod_string.c_str())); +        addDebugText(llformat("tris %d verts %d", total_tris, total_verts)); +        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())); +        //addDebugText(llformat("anim time %.1f (step %f factor %f)",  +        //                      mMotionController.getAnimTime(), +        //                      mMotionController.getTimeStep(),  +        //                      mMotionController.getTimeFactor())); +         +    } +    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("AXON") << "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; +        for (std::map<LLUUID,S32>::iterator anim_it = volp->mObjectSignaledAnimations.begin(); +             anim_it != volp->mObjectSignaledAnimations.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; +            } +        } +    } +    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; +} diff --git a/indra/newview/llcontrolavatar.h b/indra/newview/llcontrolavatar.h new file mode 100644 index 0000000000..a0f7912d24 --- /dev/null +++ b/indra/newview/llcontrolavatar.h @@ -0,0 +1,83 @@ +/** + * @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 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(); + +    bool mPlaying; + +    F32 mGlobalScale; + +    LLVOVolume *mRootVolp; + +    bool mMarkedForDeath; + +}; + +#endif //LL_CONTROLAVATAR_H diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index f956023358..6799c3f862 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -50,6 +50,7 @@  #include "llviewerobjectlist.h"  #include "llviewerwindow.h"  #include "llvocache.h" +#include "llcontrolavatar.h"  const F32 MIN_INTERPOLATE_DISTANCE_SQUARED = 0.001f * 0.001f;  const F32 MAX_INTERPOLATE_DISTANCE_SQUARED = 10.f * 10.f; @@ -550,7 +551,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); diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 8128790eb6..0d8e302123 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -467,7 +467,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->mIsDummy && !avatarp->isControlAvatar()) || avatarp->mDrawable.isNull())  	{  		return;  	} @@ -1707,13 +1707,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; @@ -1927,13 +1921,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; diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 3d5e2d356e..eebdb62aff 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -330,11 +330,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);  			} diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index da84a6b8f8..3989adf8d4 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -1755,9 +1755,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;      }  } diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index df708013fc..7c48d9b233 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -4021,6 +4021,38 @@ void LLMeshRepository::uploadError(LLSD& args)  	mUploadErrorQ.push(args);  } +F32 LLMeshRepository::getEstTrianglesMax(LLUUID mesh_id) +{ +    F32 triangles_max = 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) +        { +            LLSD& header = iter->second; +            if (header.has("404") +                || !header.has("lowest_lod") +                || (header.has("version") && header["version"].asInteger() > MAX_MESH_VERSION)) +            { +                return 0.f; +            } + +            S32 bytes_high = header["high_lod"]["size"].asInteger(); +            S32 bytes_med = header["medium_lod"]["size"].asInteger(); +            S32 bytes_low = header["low_lod"]["size"].asInteger(); +            S32 bytes_lowest = header["lowest_lod"]["size"].asInteger(); +            S32 bytes_max = llmax(bytes_high, bytes_med, bytes_low, bytes_lowest); + +            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"); +            triangles_max = llmax((F32) bytes_max-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle; +        } +    } +    return triangles_max; +} +  F32 LLMeshRepository::getStreamingCost(LLUUID mesh_id, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value)  {      if (mThread && mesh_id.notNull()) diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 23af837f6f..d2eac449f7 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -472,6 +472,8 @@ public:  	static LLDeadmanTimer sQuiescentTimer;		// Time-to-complete-mesh-downloads after significant events +    // Estimated triangle count of the largest LOD +    F32 getEstTrianglesMax(LLUUID mesh_id);  	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); diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 0bf4d48421..8f2cf5bb89 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -342,9 +342,14 @@ void LLPanelObject::getState( )  	}  	// can move or rotate only linked group with move permissions, or sub-object with move and modify perms -	BOOL enable_move	= objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && !objectp->isAttachment() && (objectp->permModify() || !gSavedSettings.getBOOL("EditLinkedParts")); + +    // AXON REVIEW BEFORE RELEASE, behavior during edit is glitchy. +    // it's not entirely clear what the motivation is to have 3 +    // different rules for enablement. At least the difference between +    // move and rotate looks like just a parens error, have updated accordingly. +	BOOL enable_move	= objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && ((objectp->permModify() && !objectp->isAttachment()) || !gSavedSettings.getBOOL("EditLinkedParts"));  	BOOL enable_scale	= objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && objectp->permModify(); -	BOOL enable_rotate	= objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && ( (objectp->permModify() && !objectp->isAttachment()) || !gSavedSettings.getBOOL("EditLinkedParts")); +	BOOL enable_rotate	= objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && ((objectp->permModify() && !objectp->isAttachment()) || !gSavedSettings.getBOOL("EditLinkedParts"));  	S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();  	BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME )) diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index b1895bfc9b..6c17fb9f8d 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) @@ -347,7 +357,23 @@ 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 (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); @@ -587,6 +613,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); @@ -896,6 +923,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/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 983a7ca1ae..2e489fbc09 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" @@ -662,6 +663,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; @@ -686,6 +691,10 @@ bool LLSelectMgr::enableLinkObjects()  			new_value = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly);  		}  	} +    if (!LLSelectMgr::getInstance()->getSelection()->checkAnimatedObjectLinkable()) +    { +        new_value = false; +    }  	return new_value;  } @@ -6628,7 +6637,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 @@ -6646,23 +6658,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 @@ -6739,6 +6740,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() @@ -7375,6 +7429,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 7ef0032645..eaeeba576c 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -339,6 +339,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) @@ -742,6 +745,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; @@ -843,7 +848,7 @@ private:  	LLFrameTimer			mEffectsTimer;  	BOOL					mForceSelection; -	LLAnimPauseRequest		mPauseRequest; +    std::vector<LLAnimPauseRequest>	mPauseRequests;  };  // *DEPRECATED: For callbacks or observers, use diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index df5756cf11..b4116026fa 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -872,16 +872,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(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 89cc66cf33..acf5ce2f92 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2387,6 +2387,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/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index f473000657..0e169408ff 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -1751,8 +1751,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/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 7c1921b143..66f5bf3c37 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -208,6 +208,12 @@ static bool handleAnisotropicChanged(const LLSD& newvalue)  	return true;  } +static bool handleForceLODChanged(const LLSD& newvalue) +{ +	LLVOVolume::sForceLOD = (F32) newvalue.asReal(); +	return true; +} +  static bool handleVolumeLODChanged(const LLSD& newvalue)  {  	LLVOVolume::sLODFactor = (F32) newvalue.asReal(); @@ -626,6 +632,7 @@ void settings_setup_listeners()  	gSavedSettings.getControl("RenderAvatarCloth")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));  	gSavedSettings.getControl("WindLightUseAtmosShaders")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));  	gSavedSettings.getControl("RenderGammaFull")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); +	gSavedSettings.getControl("RenderForceVolumeLOD")->getSignal()->connect(boost::bind(&handleForceLODChanged, _2));  	gSavedSettings.getControl("RenderVolumeLODFactor")->getSignal()->connect(boost::bind(&handleVolumeLODChanged, _2));  	gSavedSettings.getControl("RenderAvatarLODFactor")->getSignal()->connect(boost::bind(&handleAvatarLODChanged, _2));  	gSavedSettings.getControl("RenderAvatarPhysicsLODFactor")->getSignal()->connect(boost::bind(&handleAvatarPhysicsLODChanged, _2)); 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 912b0e0b04..f927f64542 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1077,6 +1077,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; @@ -6908,7 +6912,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) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 2144c7d481..24af491391 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -55,6 +55,7 @@  #include "llagentcamera.h"  #include "llcallingcard.h"  #include "llbuycurrencyhtml.h" +#include "llcontrolavatar.h"  #include "llfirstuse.h"  #include "llfloaterbump.h"  #include "llfloaterbuyland.h" @@ -103,6 +104,7 @@  #include "llviewerwindow.h"  #include "llvlmanager.h"  #include "llvoavatarself.h" +#include "llvovolume.h"  #include "llworld.h"  #include "pipeline.h"  #include "llfloaterworldmap.h" @@ -4982,12 +4984,15 @@ 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)  	{ @@ -4999,6 +5004,7 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)  	S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList);  	S32 num_source_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationSourceList); +	//clear animation flags  	avatarp->mSignaledAnimations.clear();  	if (avatarp->isSelf()) @@ -5069,6 +5075,95 @@ 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); + +    LLViewerObject *objp = gObjectList.findObject(uuid); +    if (!objp) +    { +		LL_WARNS("Messaging") << "AXON Received animation state for unknown object" << uuid << LL_ENDL; +        return; +    } +     +	LLVOVolume *volp = dynamic_cast<LLVOVolume*>(objp); +    if (!volp) +    { +		LL_WARNS("Messaging") << "AXON Received animation state for non-volume object" << uuid << LL_ENDL; +        return; +    } + +    if (!volp->isAnimatedObject()) +    { +		LL_WARNS("Messaging") << "AXON Received animation state for non-animated object" << uuid << LL_ENDL; +        return; +    } + +    LLControlAvatar *avatarp = volp->getControlAvatar(); +    if (!avatarp) +    { +        LL_WARNS("Messaging") << "AXON no control avatar, ignoring" << LL_ENDL; +        return; +    } +     +	S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList); +	LL_DEBUGS("AXON") << "processing object animation requests, num_blocks " << num_blocks << LL_ENDL; + +#if 1  +    // Here we go into skinned mode once, the first time we get an +    // animation request, and then stay there. This is probably the +    // normally desired behavior. +    if (!avatarp->mPlaying) +    { +        avatarp->mPlaying = true; +        if (!avatarp->mRootVolp->isAnySelected()) +        { +            avatarp->updateVolumeGeom(); +            avatarp->mRootVolp->recursiveMarkForUpdate(TRUE); +        } +    } +#else// AXON REMOVE BEFORE RELEASE? +    // In this block we switch back into static mode when no animations are +    // playing. This is mostly useful for debugging. +    if (num_blocks > 0 && !avatarp->mPlaying) +    { +        avatarp->mPlaying = true; +        if (!avatarp->mRootVolp->isAnySelected()) +        { +            avatarp->updateVolumeGeom(); +            avatarp->mRootVolp->recursiveMarkForUpdate(TRUE); +        } +    } +    else if (num_blocks == 0 && avatarp->mPlaying) +    { +        avatarp->mPlaying = false; +        if (!avatarp->mRootVolp->isAnySelected()) +        { +            avatarp->updateVolumeGeom(); +            avatarp->mRootVolp->recursiveMarkForUpdate(TRUE); +        } +    } +#endif + +	volp->mObjectSignaledAnimations.clear(); +	 +    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); +        volp->mObjectSignaledAnimations[animation_id] = anim_sequence_id; +        LL_DEBUGS("AXON") << "got object animation request for object "  +                          << uuid << " animation id " << animation_id << LL_ENDL; +    } + +    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 b0eaa37541..cef6f79812 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -95,6 +95,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 5de4029542..2e4c995be4 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" @@ -138,7 +139,7 @@ const F32 PHYSICS_TIMESTEP = 1.f / 45.f;  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)  {  	LLViewerObject *res = NULL;  	LL_RECORD_BLOCK_TIME(FTM_CREATE_OBJECT); @@ -166,6 +167,12 @@ LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pco  			}  			res = gAgentAvatarp;  		} +		else if (flags & CO_FLAG_CONTROL_AVATAR) +		{ +            LLControlAvatar *avatar = new LLControlAvatar(id, pcode, regionp); +			avatar->initInstance(); +			res = avatar; +		}  		else  		{  			LLVOAvatar *avatar = new LLVOAvatar(id, pcode, regionp);  @@ -235,6 +242,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), @@ -259,7 +267,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), @@ -363,17 +371,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->removeAttachmentOverridesForObject(mesh_id); +            } +        } +        if (getControlAvatar()) +        { +            unlinkControlAvatar(); +        }  		// Mark itself as dead  		mDead = TRUE; @@ -674,6 +688,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; @@ -1378,7 +1404,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); @@ -1648,7 +1674,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,  				U8 state;  				mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); -				mState = state; +				mAttachmentState = state;  				break;  			} @@ -1671,7 +1697,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,  		U8		state;  		dp->unpackU8(state, "State"); -		mState = state; +		mAttachmentState = state;  		switch(update_type)  		{ @@ -2904,6 +2930,63 @@ void LLViewerObject::fetchInventoryFromServer()  	}  } +LLControlAvatar *LLViewerObject::getControlAvatar() +{ +    return getRootEdit()->mControlAvatar.get(); +} + +LLControlAvatar *LLViewerObject::getControlAvatar() const +{ +    return getRootEdit()->mControlAvatar.get(); +} + +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); +    } +    if (getControlAvatar()) +    { +        getControlAvatar()->addAttachmentOverridesForObject(this); +    } +    else +    { +        LL_WARNS() << "no control avatar found!" << LL_ENDL; +    } +} + +void LLViewerObject::unlinkControlAvatar() +{ +    if (getControlAvatar()) +    { +        getControlAvatar()->removeAttachmentOverridesForObject(this); +    } +    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; @@ -3513,6 +3596,45 @@ F32 LLViewerObject::getLinksetPhysicsCost()  	return mLinksetPhysicsCost;  } +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; +        est_tris += child->recursiveGetEstTrianglesMax(); +    } +    return est_tris; +} + +S32 LLViewerObject::getAnimatedObjectMaxTris() const +{ +    S32 max_tris = 0; +    if (gSavedSettings.getBOOL("AnimatedObjectsIgnoreLimits")) // AXON REMOVE AFTER SERVER TESTING DONE +    { +        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::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const  {  	return 0.f; @@ -3528,6 +3650,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; @@ -3631,7 +3805,6 @@ void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */)  	}  } -  void LLViewerObject::setLineWidthForWindowSize(S32 window_width)  {  	if (window_width < 700) @@ -3859,7 +4032,7 @@ const LLVector3 LLViewerObject::getRenderPosition() const  	if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED))  	{  		LLVOAvatar* avatar = getAvatar(); -		if (avatar) +		if (avatar && !getControlAvatar())  		{  			return avatar->getPositionAgent();  		} @@ -3883,7 +4056,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;  	} @@ -5121,7 +5294,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(); @@ -5460,6 +5639,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; @@ -5859,6 +6043,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()) @@ -5910,6 +6105,11 @@ void LLViewerObject::setRegion(LLViewerRegion *regionp)  		child->setRegion(regionp);  	} +    if (mControlAvatar) +    { +        mControlAvatar->setRegion(regionp); +    } +  	setChanged(MOVED | SILHOUETTE);  	updateDrawable(FALSE);  } @@ -6358,6 +6558,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 7a490f6957..0bfc513dc9 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -47,6 +47,7 @@ class LLAgent;			// TODO: Get rid of this.  class LLAudioSource;  class LLAudioSourceVO;  class LLColor4; +class LLControlAvatar;  class LLDataPacker;  class LLDataPackerBinaryBuffer;  class LLDrawable; @@ -220,6 +221,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 +234,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 @@ -356,9 +360,15 @@ public:  	virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE); +    S32 getAnimatedObjectMaxTris() const; +    F32 recursiveGetEstTrianglesMax() const; +    virtual F32 getEstTrianglesMax() const;  	virtual F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL, F32* unscaled_value = NULL) 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 +384,7 @@ public:  	void sendShapeUpdate(); -	U8 getState()							{ return mState; } +	U8 getAttachmentState()							{ return mAttachmentState; }  	F32 getAppAngle() const					{ return mAppAngle; }  	F32 getPixelArea() const				{ return mPixelArea; } @@ -411,7 +421,8 @@ public:  	void setIcon(LLViewerTexture* icon_image);  	void clearIcon(); -	void markForUpdate(BOOL priority); +    void recursiveMarkForUpdate(BOOL priority); +	virtual void markForUpdate(BOOL priority);  	void updateVolume(const LLVolumeParams& volume_params);  	virtual	void updateSpatialExtents(LLVector4a& min, LLVector4a& max);  	virtual F32 getBinRadius(); @@ -683,6 +694,21 @@ 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(); + +    virtual bool isAnimatedObject() const; + +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 @@ -693,8 +719,10 @@ protected:  	// updateInventory.  	void doUpdateInventory(LLPointer<LLViewerInventoryItem>& item, U8 key, bool is_new); +    // Flags for createObject +    static const S32 CO_FLAG_CONTROL_AVATAR = 1 << 1; -	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); @@ -782,7 +810,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 diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 3c83e3a006..6b7e81cb65 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -862,8 +862,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; @@ -900,7 +900,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 @@ -1947,12 +1947,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; 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/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 3f479802aa..28a81981a2 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2861,6 +2861,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"); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index eae8f2cc56..bc5cca4a65 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -44,6 +44,7 @@  #include "llavatarnamecache.h"  #include "llavatarpropertiesprocessor.h"  #include "llavatarrendernotifier.h" +#include "llcontrolavatar.h"  #include "llexperiencecache.h"  #include "llphysicsmotion.h"  #include "llviewercontrol.h" @@ -109,6 +110,8 @@  #include "llcallstack.h"  #include "llrendersphere.h" +#include <boost/lexical_cast.hpp> +  extern F32 SPEED_ADJUST_MAX;  extern F32 SPEED_ADJUST_MAX_SEC;  extern F32 ANIM_SPEED_MAX; @@ -142,7 +145,7 @@ const LLUUID ANIM_AGENT_PHYSICS_MOTION = LLUUID("7360e029-3cb8-ebc4-863e-212df44  //-----------------------------------------------------------------------------  // Constants  //----------------------------------------------------------------------------- -const F32 DELTA_TIME_MIN = 0.01f;	// we clamp measured deltaTime to this +const F32 DELTA_TIME_MIN = 0.01f;	// we clamp measured delta_time to this  const F32 DELTA_TIME_MAX = 0.2f;	// range to insure stability of computations.  const F32 PELVIS_LAG_FLYING		= 0.22f;// pelvis follow half life while flying @@ -617,6 +620,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  	LLViewerObject(id, pcode, regionp),  	mSpecialRenderMode(0),  	mAttachmentSurfaceArea(0.f), +	mAttachmentVisibleTriangleCount(0), +	mAttachmentEstTriangleCount(0.f),  	mReportedVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN),  	mTurning(FALSE),  	mLastSkeletonSerialNum( 0 ), @@ -663,7 +668,9 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  	mLastUpdateRequestCOFVersion(-1),  	mLastUpdateReceivedCOFVersion(-1),  	mCachedMuteListUpdateTime(0), -	mCachedInMuteList(false) +	mCachedInMuteList(false), +    mIsControlAvatar(false), +    mEnableDefaultMotions(true)  {  	LL_DEBUGS("AvatarRender") << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << LL_ENDL; @@ -718,6 +725,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,  	mCurrentGesticulationLevel = 0; +      	mRuthTimer.reset();  	mRuthDebugTimer.reset();  	mDebugExistenceTimer.reset(); @@ -1099,7 +1107,7 @@ void LLVOAvatar::cleanupClass()  }  // virtual -void LLVOAvatar::initInstance(void) +void LLVOAvatar::initInstance()  {  	//-------------------------------------------------------------------------  	// register motions @@ -1222,8 +1230,6 @@ const LLVector3 LLVOAvatar::getRenderPosition() const  	{  		return getPosition() * mDrawable->getParent()->getRenderMatrix();  	} -	 -	  }  void LLVOAvatar::updateDrawable(BOOL force_damped) @@ -1240,6 +1246,10 @@ void LLVOAvatar::onShift(const LLVector4a& shift_vector)  void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)  { +    if (mDrawable.isNull()) +    { +        return; +    }  	if (isImpostor() && !needsImpostorUpdate())  	{  		LLVector3 delta = getRenderPosition() - @@ -1404,13 +1414,29 @@ void LLVOAvatar::renderCollisionVolumes()          static F32 sphere_scale = 1.0f;          static F32 center_dot_scale = 0.05f; -        static LLVector3 CV_COLOR_OCCLUDED(0.0f, 0.0f, 1.0f); -        static LLVector3 CV_COLOR_VISIBLE(0.5f, 0.5f, 1.0f); -        static LLVector3 DOT_COLOR_OCCLUDED(1.0f, 1.0f, 1.0f); -        static LLVector3 DOT_COLOR_VISIBLE(1.0f, 1.0f, 1.0f); +        static LLVector3 BLUE(0.0f, 0.0f, 1.0f); +        static LLVector3 PASTEL_BLUE(0.5f, 0.5f, 1.0f); +        static LLVector3 RED(1.0f, 0.0f, 0.0f); +        static LLVector3 PASTEL_RED(1.0f, 0.5f, 0.5f); +        static LLVector3 WHITE(1.0f, 1.0f, 1.0f); +         -        render_sphere_and_line(begin_pos, end_pos, sphere_scale, CV_COLOR_OCCLUDED, CV_COLOR_VISIBLE); -        render_sphere_and_line(begin_pos, end_pos, center_dot_scale, DOT_COLOR_OCCLUDED, DOT_COLOR_VISIBLE); +        LLVector3 cv_color_occluded; +        LLVector3 cv_color_visible; +        LLVector3 dot_color_occluded(WHITE); +        LLVector3 dot_color_visible(WHITE); +        if (isControlAvatar()) +        { +            cv_color_occluded = RED; +            cv_color_visible = PASTEL_RED; +        } +        else +        { +            cv_color_occluded = BLUE; +            cv_color_visible = PASTEL_BLUE; +        } +        render_sphere_and_line(begin_pos, end_pos, sphere_scale, cv_color_occluded, cv_color_visible); +        render_sphere_and_line(begin_pos, end_pos, center_dot_scale, dot_color_occluded, dot_color_visible);          gGL.popMatrix();      } @@ -1422,9 +1448,6 @@ void LLVOAvatar::renderCollisionVolumes()  		mNameText->lineSegmentIntersect(unused, unused, unused, TRUE);  	} - -	mDebugText.clear(); -	addDebugText(ostr.str());  }  void LLVOAvatar::renderBones() @@ -1595,6 +1618,11 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&  		return FALSE;  	} +    if (isControlAvatar()) +    { +        return FALSE; +    } +      	if (lineSegmentBoundingBox(start, end))  	{  		for (S32 i = 0; i < mNumCollisionVolumes; ++i) @@ -1680,6 +1708,7 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&  	return FALSE;  } +// virtual  LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end,  									  S32 face,  									  BOOL pick_transparent, @@ -1796,7 +1825,11 @@ void LLVOAvatar::buildCharacter()  		mAahMorph = getVisualParam( "Express_Open_Mouth" );  	} -	startDefaultMotions(); +    // Currently disabled for control avatars (animated objects), enabled for all others. +    if (mEnableDefaultMotions) +    { +        startDefaultMotions(); +    }  	//-------------------------------------------------------------------------  	// restart any currently active motions @@ -1942,7 +1975,8 @@ void LLVOAvatar::resetSkeleton(bool reset_animations)  //-----------------------------------------------------------------------------  void LLVOAvatar::releaseMeshData()  { -	if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || mIsDummy) +	if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || +        (mIsDummy && !isControlAvatar()))  	{  		return;  	} @@ -1962,15 +1996,15 @@ void LLVOAvatar::releaseMeshData()  		LLFace* facep = mDrawable->getFace(0);  		if (facep)  		{ -		facep->setSize(0, 0); -		for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++) -		{ -			facep = mDrawable->getFace(i); +            facep->setSize(0, 0); +            for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++) +            { +                facep = mDrawable->getFace(i);  				if (facep)  				{ -			facep->setSize(0, 0); -		} -	} +                    facep->setSize(0, 0); +                } +            }  		}  	} @@ -1994,6 +2028,10 @@ void LLVOAvatar::releaseMeshData()  void LLVOAvatar::restoreMeshData()  {  	llassert(!isSelf()); +    if (mDrawable.isNull()) +    { +        return; +    }  	//LL_INFOS() << "Restoring" << LL_ENDL;  	mMeshValid = TRUE; @@ -2303,7 +2341,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time)  	{	  		LL_RECORD_BLOCK_TIME(FTM_JOINT_UPDATE); -		if (mIsSitting && getParent()) +		if (isSitting() && getParent())  		{  			LLViewerObject *root_object = (LLViewerObject*)getRoot();  			LLDrawable* drawablep = root_object->mDrawable; @@ -2469,7 +2507,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)  		// (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing)  		//-------------------------------------------------------------------------------------------- -		if ( mIsSitting ) +		if ( isSitting() )  		{  			LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] );  			mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot->getWorldPosition() + headOffset ); @@ -2593,13 +2631,16 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update)  		}  	} -	mDrawable->movePartition(); -	 -	//force a move if sitting on an active object -	if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive()) -	{ -		gPipeline.markMoved(mDrawable, TRUE); -	} +    if (mDrawable.notNull()) +    { +        mDrawable->movePartition(); +         +        //force a move if sitting on an active object +        if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive()) +        { +            gPipeline.markMoved(mDrawable, TRUE); +        } +    }  }  void LLVOAvatar::idleUpdateAppearanceAnimation() @@ -2760,7 +2801,8 @@ void LLVOAvatar::idleUpdateLoadingEffect()  																 LLPartData::LL_PART_EMISSIVE_MASK | // LLPartData::LL_PART_FOLLOW_SRC_MASK |  																 LLPartData::LL_PART_TARGET_POS_MASK ); -			if (!isTooComplex()) // do not generate particles for overly-complex avatars +			// do not generate particles for dummy or overly-complex avatars +			if (!mIsDummy && !isTooComplex())  			{  				setParticleSource(particle_parameters, getID());  			} @@ -3360,8 +3402,7 @@ bool LLVOAvatar::isInMuteList()  void LLVOAvatar::updateDebugText()  { -	// clear debug text -	mDebugText.clear(); +    // Leave mDebugText uncleared here, in case a derived class has added some state first  	if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))  	{ @@ -3406,7 +3447,7 @@ void LLVOAvatar::updateDebugText()  		if (hover_offset[2] != 0.0)  		{  			debug_line += llformat(" hov_z: %.3f", hover_offset[2]); -			debug_line += llformat(" %s", (mIsSitting ? "S" : "T")); +			debug_line += llformat(" %s", (isSitting() ? "S" : "T"));  			debug_line += llformat("%s", (isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED) ? "G" : "-"));  		}          LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition(); @@ -3422,12 +3463,14 @@ void LLVOAvatar::updateDebugText()  		addDebugText(debug_line);  	} +  	if (gSavedSettings.getBOOL("DebugAvatarCompositeBaked"))  	{  		if (!mBakedTextureDebugText.empty())  			addDebugText(mBakedTextureDebugText);  	} +    // Develop -> Avatar -> Animation Info  	if (LLVOAvatar::sShowAnimationDebug)  	{  		for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin(); @@ -3437,8 +3480,27 @@ void LLVOAvatar::updateDebugText()  			if (motionp->getMinPixelArea() < getPixelArea())  			{  				std::string output; -				if (motionp->getName().empty()) +                std::string motion_name = motionp->getName(); +				if (motion_name.empty())  				{ +                    if (isControlAvatar()) +                    { +                        LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this); +                        // Try to get name from inventory of associated object +                        LLVOVolume *volp = control_av->mRootVolp; +                        if (volp) +                        { +                            volp->requestInventory();  +                            LLViewerInventoryItem* item = volp->getInventoryItemByAsset(motionp->getID()); +                            if (item) +                            { +                                motion_name = item->getName(); +                            } +                        } +                    } +                } +                if (motion_name.empty()) +                {  					std::string name;  					if (gAgent.isGodlikeWithoutAdminMenuFakery() || isSelf())  					{ @@ -3492,7 +3554,6 @@ void LLVOAvatar::updateDebugText()  					{  						name = LLUUID::null.asString();  					} -  					output = llformat("%s - %d",  							  name.c_str(),  							  (U32)motionp->getPriority()); @@ -3500,8 +3561,8 @@ void LLVOAvatar::updateDebugText()  				else  				{  					output = llformat("%s - %d", -							  motionp->getName().c_str(), -							  (U32)motionp->getPriority()); +                                      motion_name.c_str(), +                                      (U32)motionp->getPriority());  				}  				addDebugText(output);  			} @@ -3517,51 +3578,125 @@ void LLVOAvatar::updateDebugText()  	{  		setDebugText(mDebugText);  	} -	mDebugText.clear(); - +    mDebugText.clear();  }  //------------------------------------------------------------------------ -// updateCharacter() -// called on both your avatar and other avatars +// updateFootstepSounds +// Factored out from updateCharacter() +// Generate footstep sounds when feet hit the ground  //------------------------------------------------------------------------ -BOOL LLVOAvatar::updateCharacter(LLAgent &agent) -{	 -	updateDebugText(); -	 -	if (!mIsBuilt) -	{ -		return FALSE; -	} +void LLVOAvatar::updateFootstepSounds() +{ +    if (mIsDummy) +    { +        return; +    } +     +	//------------------------------------------------------------------------- +	// Find the ground under each foot, these are used for a variety +	// of things that follow +	//------------------------------------------------------------------------- +	LLVector3 ankle_left_pos_agent = mFootLeftp->getWorldPosition(); +	LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition(); -	BOOL visible = isVisible(); +	LLVector3 ankle_left_ground_agent = ankle_left_pos_agent; +	LLVector3 ankle_right_ground_agent = ankle_right_pos_agent; +    LLVector3 normal; +	resolveHeightAgent(ankle_left_pos_agent, ankle_left_ground_agent, normal); +	resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal); -	// For fading out the names above heads, only let the timer -	// run if we're visible. -	if (mDrawable.notNull() && !visible) +	F32 leftElev = llmax(-0.2f, ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]); +	F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]); + +	if (!isSitting())  	{ -		mTimeVisible.reset(); +		//------------------------------------------------------------------------- +		// Figure out which foot is on ground +		//------------------------------------------------------------------------- +		if (!mInAir) +		{ +			if ((leftElev < 0.0f) || (rightElev < 0.0f)) +			{ +				ankle_left_pos_agent = mFootLeftp->getWorldPosition(); +				ankle_right_pos_agent = mFootRightp->getWorldPosition(); +				leftElev = ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]; +				rightElev = ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]; +			} +		}  	} +	 +	const LLUUID AGENT_FOOTSTEP_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_LAND}; +	const S32 NUM_AGENT_FOOTSTEP_ANIMS = LL_ARRAY_SIZE(AGENT_FOOTSTEP_ANIMS); -	//-------------------------------------------------------------------- -	// the rest should only be done occasionally for far away avatars -	//-------------------------------------------------------------------- +	if ( gAudiop && isAnyAnimationSignaled(AGENT_FOOTSTEP_ANIMS, NUM_AGENT_FOOTSTEP_ANIMS) ) +	{ +		BOOL playSound = FALSE; +		LLVector3 foot_pos_agent; + +		BOOL onGroundLeft = (leftElev <= 0.05f); +		BOOL onGroundRight = (rightElev <= 0.05f); + +		// did left foot hit the ground? +		if ( onGroundLeft && !mWasOnGroundLeft ) +		{ +			foot_pos_agent = ankle_left_pos_agent; +			playSound = TRUE; +		} +		// did right foot hit the ground? +		if ( onGroundRight && !mWasOnGroundRight ) +		{ +			foot_pos_agent = ankle_right_pos_agent; +			playSound = TRUE; +		} + +		mWasOnGroundLeft = onGroundLeft; +		mWasOnGroundRight = onGroundRight; + +		if ( playSound ) +		{ +			const F32 STEP_VOLUME = 0.1f; +			const LLUUID& step_sound_id = getStepSound(); + +			LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent); + +			if (LLViewerParcelMgr::getInstance()->canHearSound(foot_pos_global) +				&& !LLMuteList::getInstance()->isMuted(getID(), LLMute::flagObjectSounds)) +			{ +				gAudiop->triggerSound(step_sound_id, getID(), STEP_VOLUME, LLAudioEngine::AUDIO_TYPE_AMBIENT, foot_pos_global); +			} +		} +	} +} + +//------------------------------------------------------------------------ +// computeUpdatePeriod() +// Factored out from updateCharacter() +// Set new value for mUpdatePeriod based on distance and various other factors. +//------------------------------------------------------------------------ +void LLVOAvatar::computeUpdatePeriod() +{  	bool visually_muted = isVisuallyMuted(); -	if (visible && (!isSelf() || visually_muted) && !mIsDummy && sUseImpostors && !mNeedsAnimUpdate && !sFreezeCounter) +	if (mDrawable.notNull() +        && isVisible()  +        && (!isSelf() || visually_muted) +        && !mIsDummy +        && sUseImpostors +        && !mNeedsAnimUpdate  +        && !sFreezeCounter)  	{  		const LLVector4a* ext = mDrawable->getSpatialExtents();  		LLVector4a size;  		size.setSub(ext[1],ext[0]);  		F32 mag = size.getLength3().getF32()*0.5f; -  		F32 impostor_area = 256.f*512.f*(8.125f - LLVOAvatar::sLODFactor*8.f);  		if (visually_muted)  		{ // visually muted avatars update at 16 hz  			mUpdatePeriod = 16;  		} -		else if (   ! shouldImpostor() +		else if (! shouldImpostor()  				 || mDrawable->mDistanceWRTCamera < 1.f + mag)  		{   // first 25% of max visible avatars are not impostored  			// also, don't impostor avatars whose bounding box may be penetrating the  @@ -3585,63 +3720,215 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  			//nearby avatars, update the impostors more frequently.  			mUpdatePeriod = 4;  		} - -		visible = (LLDrawable::getCurrentFrame()+mID.mData[0])%mUpdatePeriod == 0 ? TRUE : FALSE;  	}  	else  	{  		mUpdatePeriod = 1;  	} +} -	// don't early out for your own avatar, as we rely on your animations playing reliably -	// for example, the "turn around" animation when entering customize avatar needs to trigger -	// even when your avatar is offscreen -	if (!visible && !isSelf()) -	{ -		updateMotions(LLCharacter::HIDDEN_UPDATE); -		return FALSE; -	} +//------------------------------------------------------------------------ +// updateOrientation() +// Factored out from updateCharacter() +// This is used by updateCharacter() to update the avatar's orientation: +// - updates mTurning state +// - updates rotation of the mRoot joint in the skeleton +// - for self, calls setControlFlags() to notify the simulator about any turns +//------------------------------------------------------------------------ +void LLVOAvatar::updateOrientation(LLAgent& agent, F32 speed, F32 delta_time) +{ +    LLQuaternion iQ; +    LLVector3 upDir( 0.0f, 0.0f, 1.0f ); +			 +    // Compute a forward direction vector derived from the primitive rotation +    // and the velocity vector.  When walking or jumping, don't let body deviate +    // more than 90 from the view, if necessary, flip the velocity vector. -	// change animation time quanta based on avatar render load -	if (!isSelf() && !mIsDummy) +    LLVector3 primDir; +    if (isSelf()) +    { +        primDir = agent.getAtAxis() - projected_vec(agent.getAtAxis(), agent.getReferenceUpVector()); +        primDir.normalize(); +    } +    else +    { +        primDir = getRotation().getMatrix3().getFwdRow(); +    } +    LLVector3 velDir = getVelocity(); +    velDir.normalize(); +    if ( mSignaledAnimations.find(ANIM_AGENT_WALK) != mSignaledAnimations.end()) +    { +        F32 vpD = velDir * primDir; +        if (vpD < -0.5f) +        { +            velDir *= -1.0f; +        } +    } +    LLVector3 fwdDir = lerp(primDir, velDir, clamp_rescale(speed, 0.5f, 2.0f, 0.0f, 1.0f)); +    if (isSelf() && gAgentCamera.cameraMouselook()) +    { +        // make sure fwdDir stays in same general direction as primdir +        if (gAgent.getFlying()) +        { +            fwdDir = LLViewerCamera::getInstance()->getAtAxis(); +        } +        else +        { +            LLVector3 at_axis = LLViewerCamera::getInstance()->getAtAxis(); +            LLVector3 up_vector = gAgent.getReferenceUpVector(); +            at_axis -= up_vector * (at_axis * up_vector); +            at_axis.normalize(); +					 +            F32 dot = fwdDir * at_axis; +            if (dot < 0.f) +            { +                fwdDir -= 2.f * at_axis * dot; +                fwdDir.normalize(); +            } +        } +    } + +    LLQuaternion root_rotation = mRoot->getWorldMatrix().quaternion(); +    F32 root_roll, root_pitch, root_yaw; +    root_rotation.getEulerAngles(&root_roll, &root_pitch, &root_yaw); + +    // When moving very slow, the pelvis is allowed to deviate from the +    // forward direction to allow it to hold its position while the torso +    // and head turn.  Once in motion, it must conform however. +    BOOL self_in_mouselook = isSelf() && gAgentCamera.cameraMouselook(); + +    LLVector3 pelvisDir( mRoot->getWorldMatrix().getFwdRow4().mV ); + +    static LLCachedControl<F32> s_pelvis_rot_threshold_slow(gSavedSettings, "AvatarRotateThresholdSlow", 60.0); +    static LLCachedControl<F32> s_pelvis_rot_threshold_fast(gSavedSettings, "AvatarRotateThresholdFast", 2.0); + +    F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, s_pelvis_rot_threshold_slow, s_pelvis_rot_threshold_fast); +						 +    if (self_in_mouselook) +    { +        pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR; +    } +    pelvis_rot_threshold *= DEG_TO_RAD; + +    F32 angle = angle_between( pelvisDir, fwdDir ); + +    // The avatar's root is allowed to have a yaw that deviates widely +    // from the forward direction, but if roll or pitch are off even +    // a little bit we need to correct the rotation. +    if(root_roll < 1.f * DEG_TO_RAD +       && root_pitch < 5.f * DEG_TO_RAD) +    { +        // smaller correction vector means pelvis follows prim direction more closely +        if (!mTurning && angle > pelvis_rot_threshold*0.75f) +        { +            mTurning = TRUE; +        } + +        // use tighter threshold when turning +        if (mTurning) +        { +            pelvis_rot_threshold *= 0.4f; +        } + +        // am I done turning? +        if (angle < pelvis_rot_threshold) +        { +            mTurning = FALSE; +        } + +        LLVector3 correction_vector = (pelvisDir - fwdDir) * clamp_rescale(angle, pelvis_rot_threshold*0.75f, pelvis_rot_threshold, 1.0f, 0.0f); +        fwdDir += correction_vector; +    } +    else +    { +        mTurning = FALSE; +    } + +    // Now compute the full world space rotation for the whole body (wQv) +    LLVector3 leftDir = upDir % fwdDir; +    leftDir.normalize(); +    fwdDir = leftDir % upDir; +    LLQuaternion wQv( fwdDir, leftDir, upDir ); + +    if (isSelf() && mTurning) +    { +        if ((fwdDir % pelvisDir) * upDir > 0.f) +        { +            gAgent.setControlFlags(AGENT_CONTROL_TURN_RIGHT); +        } +        else +        { +            gAgent.setControlFlags(AGENT_CONTROL_TURN_LEFT); +        } +    } + +    // Set the root rotation, but do so incrementally so that it +    // lags in time by some fixed amount. +    //F32 u = LLSmoothInterpolation::getInterpolant(PELVIS_LAG); +    F32 pelvis_lag_time = 0.f; +    if (self_in_mouselook) +    { +        pelvis_lag_time = PELVIS_LAG_MOUSELOOK; +    } +    else if (mInAir) +    { +        pelvis_lag_time = PELVIS_LAG_FLYING; +        // increase pelvis lag time when moving slowly +        pelvis_lag_time *= clamp_rescale(mSpeedAccum, 0.f, 15.f, 3.f, 1.f); +    } +    else +    { +        pelvis_lag_time = PELVIS_LAG_WALKING; +    } + +    F32 u = llclamp((delta_time / pelvis_lag_time), 0.0f, 1.0f);	 + +    mRoot->setWorldRotation( slerp(u, mRoot->getWorldRotation(), wQv) ); +} + +//------------------------------------------------------------------------ +// updateTimeStep() +// Factored out from updateCharacter(). +// +// Updates the time step used by the motion controller, based on area +// and avatar count criteria.  This will also stop the +// ANIM_AGENT_WALK_ADJUST animation under some circumstances. +// ------------------------------------------------------------------------ +void LLVOAvatar::updateTimeStep() +{ +    bool is_pure_dummy = mIsDummy && !isControlAvatar(); +	if (!isSelf() && !is_pure_dummy) // ie, non-self avatars, and animated objects will be affected.  	{ +        // AXON note that sInstances counts animated objects and +        // standard avatars in the same bucket. Is this desirable?  		F32 time_quantum = clamp_rescale((F32)sInstances.size(), 10.f, 35.f, 0.f, 0.25f);  		F32 pixel_area_scale = clamp_rescale(mPixelArea, 100, 5000, 1.f, 0.f);  		F32 time_step = time_quantum * pixel_area_scale; +        // Extrema: +        //   If number of avs is 10 or less, time_step is unmodified (flagged with 0.0). +        //   If area of av is 5000 or greater, time_step is unmodified (flagged with 0.0). +        //   If number of avs is 35 or greater, and area of av is 100 or less, +        //   time_step takes the maximum possible value of 0.25. +        //   Other situations will give values within the (0, 0.25) range.  		if (time_step != 0.f)  		{  			// disable walk motion servo controller as it doesn't work with motion timesteps  			stopMotion(ANIM_AGENT_WALK_ADJUST);  			removeAnimationData("Walk Speed");  		} +        // See SL-763 - playback with altered time step does not +        // appear to work correctly, odd behavior for distant avatars. +        // As of 11-2017, LLMotionController::updateMotions() will +        // ignore the value here. Need to re-enable if it's every +        // fixed.  		mMotionController.setTimeStep(time_step); -		//		LL_INFOS() << "Setting timestep to " << time_quantum * pixel_area_scale << LL_ENDL; -	} - -	if (getParent() && !mIsSitting) -	{ -		sitOnObject((LLViewerObject*)getParent());  	} -	else if (!getParent() && mIsSitting && !isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED)) -	{ -		getOffObject(); -	} - -	//-------------------------------------------------------------------- -	// create local variables in world coords for region position values -	//-------------------------------------------------------------------- -	F32 speed; -	LLVector3 normal; +} -	LLVector3 xyVel = getVelocity(); -	xyVel.mV[VZ] = 0.0f; -	speed = xyVel.length(); -	// remembering the value here prevents a display glitch if the -	// animation gets toggled during this update. -	bool was_sit_ground_constrained = isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED); -	 -	if (!(mIsSitting && getParent())) +void LLVOAvatar::updateRootPositionAndRotation(LLAgent& agent, F32 speed, bool was_sit_ground_constrained)  +{ +	if (!(isSitting() && getParent()))  	{  		// This case includes all configurations except sitting on an  		// object, so does include ground sit. @@ -3655,7 +3942,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  		{  			mTimeLast = animation_time; -			// put the pelvis at slaved position/mRotation +			// Initially put the pelvis at slaved position/mRotation  			// SL-315  			mRoot->setWorldPosition( getPositionAgent() ); // first frame  			mRoot->setWorldRotation( getRotation() ); @@ -3664,9 +3951,9 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  		//--------------------------------------------------------------------  		// dont' let dT get larger than 1/5th of a second  		//-------------------------------------------------------------------- -		F32 deltaTime = animation_time - mTimeLast; +		F32 delta_time = animation_time - mTimeLast; -		deltaTime = llclamp( deltaTime, DELTA_TIME_MIN, DELTA_TIME_MAX ); +		delta_time = llclamp( delta_time, DELTA_TIME_MIN, DELTA_TIME_MAX );  		mTimeLast = animation_time;  		mSpeedAccum = (mSpeedAccum * 0.95f) + (speed * 0.05f); @@ -3685,7 +3972,8 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  		root_pos = gAgent.getPosGlobalFromAgent(getRenderPosition());  		root_pos.mdV[VZ] += getVisualParamWeight(AVATAR_HOVER); - +        // AXON need to review mInAir calcs for animated objects, if the value even matters. +        LLVector3 normal;  		resolveHeightGlobal(root_pos, ground_under_pelvis, normal);  		F32 foot_to_ground = (F32) (root_pos.mdV[VZ] - mPelvisToFoot - ground_under_pelvis.mdV[VZ]);				  		BOOL in_air = ((!LLWorld::getInstance()->getRegionFromPosGlobal(ground_under_pelvis)) ||  @@ -3707,185 +3995,156 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  		// correct for the fact that the pelvis is not necessarily the center   		// of the agent's physical representation  		root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot; -		if (!mIsSitting && !was_sit_ground_constrained) +		if (!isSitting() && !was_sit_ground_constrained)  		{  			root_pos += LLVector3d(getHoverOffset());  		} -		 -		LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos); - - -		if (newPosition != mRoot->getXform()->getWorldPosition()) -		{		 -			mRoot->touch(); -			// SL-315 -			mRoot->setWorldPosition( newPosition ); // regular update				 -		} +        LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this); +        if (cav) +        { +            cav->matchVolumeTransform(); +        } +        else +        { +            LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos); +            if (newPosition != mRoot->getXform()->getWorldPosition()) +            {		 +                mRoot->touch(); +                // SL-315 +                mRoot->setWorldPosition( newPosition ); // regular update				 +            } +        }  		//--------------------------------------------------------------------  		// Propagate viewer object rotation to root of avatar  		//-------------------------------------------------------------------- -		if (!isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS)) +		if (!isControlAvatar() && !isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS))  		{ -			LLQuaternion iQ; -			LLVector3 upDir( 0.0f, 0.0f, 1.0f ); -			 -			// Compute a forward direction vector derived from the primitive rotation -			// and the velocity vector.  When walking or jumping, don't let body deviate -			// more than 90 from the view, if necessary, flip the velocity vector. - -			LLVector3 primDir; -			if (isSelf()) -			{ -				primDir = agent.getAtAxis() - projected_vec(agent.getAtAxis(), agent.getReferenceUpVector()); -				primDir.normalize(); -			} -			else -			{ -				primDir = getRotation().getMatrix3().getFwdRow(); -			} -			LLVector3 velDir = getVelocity(); -			velDir.normalize(); -			if ( mSignaledAnimations.find(ANIM_AGENT_WALK) != mSignaledAnimations.end()) -			{ -				F32 vpD = velDir * primDir; -				if (vpD < -0.5f) -				{ -					velDir *= -1.0f; -				} -			} -			LLVector3 fwdDir = lerp(primDir, velDir, clamp_rescale(speed, 0.5f, 2.0f, 0.0f, 1.0f)); -			if (isSelf() && gAgentCamera.cameraMouselook()) -			{ -				// make sure fwdDir stays in same general direction as primdir -				if (gAgent.getFlying()) -				{ -					fwdDir = LLViewerCamera::getInstance()->getAtAxis(); -				} -				else -				{ -					LLVector3 at_axis = LLViewerCamera::getInstance()->getAtAxis(); -					LLVector3 up_vector = gAgent.getReferenceUpVector(); -					at_axis -= up_vector * (at_axis * up_vector); -					at_axis.normalize(); -					 -					F32 dot = fwdDir * at_axis; -					if (dot < 0.f) -					{ -						fwdDir -= 2.f * at_axis * dot; -						fwdDir.normalize(); -					} -				} -			} - -			LLQuaternion root_rotation = mRoot->getWorldMatrix().quaternion(); -			F32 root_roll, root_pitch, root_yaw; -			root_rotation.getEulerAngles(&root_roll, &root_pitch, &root_yaw); - -			// When moving very slow, the pelvis is allowed to deviate from the -			// forward direction to allow it to hold it's position while the torso -			// and head turn.  Once in motion, it must conform however. -			BOOL self_in_mouselook = isSelf() && gAgentCamera.cameraMouselook(); - -			LLVector3 pelvisDir( mRoot->getWorldMatrix().getFwdRow4().mV ); - -			static LLCachedControl<F32> s_pelvis_rot_threshold_slow(gSavedSettings, "AvatarRotateThresholdSlow", 60.0); -			static LLCachedControl<F32> s_pelvis_rot_threshold_fast(gSavedSettings, "AvatarRotateThresholdFast", 2.0); - -			F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, s_pelvis_rot_threshold_slow, s_pelvis_rot_threshold_fast); -						 -			if (self_in_mouselook) -			{ -				pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR; -			} -			pelvis_rot_threshold *= DEG_TO_RAD; - -			F32 angle = angle_between( pelvisDir, fwdDir ); - -			// The avatar's root is allowed to have a yaw that deviates widely -			// from the forward direction, but if roll or pitch are off even -			// a little bit we need to correct the rotation. -			if(root_roll < 1.f * DEG_TO_RAD -			   && root_pitch < 5.f * DEG_TO_RAD) -			{ -				// smaller correction vector means pelvis follows prim direction more closely -				if (!mTurning && angle > pelvis_rot_threshold*0.75f) -				{ -					mTurning = TRUE; -				} - -				// use tighter threshold when turning -				if (mTurning) -				{ -					pelvis_rot_threshold *= 0.4f; -				} - -				// am I done turning? -				if (angle < pelvis_rot_threshold) -				{ -					mTurning = FALSE; -				} - -				LLVector3 correction_vector = (pelvisDir - fwdDir) * clamp_rescale(angle, pelvis_rot_threshold*0.75f, pelvis_rot_threshold, 1.0f, 0.0f); -				fwdDir += correction_vector; -			} -			else -			{ -				mTurning = FALSE; -			} - -			// Now compute the full world space rotation for the whole body (wQv) -			LLVector3 leftDir = upDir % fwdDir; -			leftDir.normalize(); -			fwdDir = leftDir % upDir; -			LLQuaternion wQv( fwdDir, leftDir, upDir ); - -			if (isSelf() && mTurning) -			{ -				if ((fwdDir % pelvisDir) * upDir > 0.f) -				{ -					gAgent.setControlFlags(AGENT_CONTROL_TURN_RIGHT); -				} -				else -				{ -					gAgent.setControlFlags(AGENT_CONTROL_TURN_LEFT); -				} -			} - -			// Set the root rotation, but do so incrementally so that it -			// lags in time by some fixed amount. -			//F32 u = LLSmoothInterpolation::getInterpolant(PELVIS_LAG); -			F32 pelvis_lag_time = 0.f; -			if (self_in_mouselook) -			{ -				pelvis_lag_time = PELVIS_LAG_MOUSELOOK; -			} -			else if (mInAir) -			{ -				pelvis_lag_time = PELVIS_LAG_FLYING; -				// increase pelvis lag time when moving slowly -				pelvis_lag_time *= clamp_rescale(mSpeedAccum, 0.f, 15.f, 3.f, 1.f); -			} -			else -			{ -				pelvis_lag_time = PELVIS_LAG_WALKING; -			} - -			F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f);	 - -			mRoot->setWorldRotation( slerp(u, mRoot->getWorldRotation(), wQv) ); -			 +            // AXON - should we always skip for control avatars? +            // Rotation fixups for avatars in motion, some may be +            // relevant. +            updateOrientation(agent, speed, delta_time);  		}  	}  	else if (mDrawable.notNull())  	{ +        // Sitting on an object - mRoot is slaved to mDrawable orientation.  		LLVector3 pos = mDrawable->getPosition();  		pos += getHoverOffset() * mDrawable->getRotation();  		// SL-315  		mRoot->setPosition(pos);  		mRoot->setRotation(mDrawable->getRotation());  	} +} + +//------------------------------------------------------------------------ +// updateCharacter() +// +// This is called for all avatars, so there are 4 possible situations: +// +// 1) Avatar is your own. In this case the class is LLVOAvatarSelf, +// isSelf() is true, and agent specifies the corresponding agent +// information for you. In all the other cases, agent is irrelevant +// and it would be less confusing if it were null or something. +// +// 2) Avatar is controlled by another resident. Class is LLVOAvatar, +// and isSelf() is false. +// +// 3) Avatar is the controller for an animated object. Class is +// LLControlAvatar and mIsDummy is true. Avatar is a purely +// viewer-side entity with no representation on the simulator. +// +// 4) Avatar is a "dummy" avatar used in some areas of the UI, such as +// when previewing uploaded animations. Class is LLVOAvatar, and +// mIsDummy is true. Avatar is purely viewer-side with no +// representation on the simulator. +// +//------------------------------------------------------------------------ +BOOL LLVOAvatar::updateCharacter(LLAgent &agent) +{	 +	updateDebugText(); +	 +	if (!mIsBuilt) +	{ +		return FALSE; +	} + +	BOOL visible = isVisible(); +    bool is_control_avatar = isControlAvatar(); // capture state to simplify tracing +	bool is_attachment = false; +	if (is_control_avatar) +	{ +        LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this); +		is_attachment = cav && cav->mRootVolp && cav->mRootVolp->isAttachment(); // For attached animated objects +	} + +    LLScopedContextString str("updateCharacter " + getFullname() + " is_control_avatar " +                              + boost::lexical_cast<std::string>(is_control_avatar)  +                              + " is_attachment " + boost::lexical_cast<std::string>(is_attachment)); + +	// For fading out the names above heads, only let the timer +	// run if we're visible. +	if (mDrawable.notNull() && !visible) +	{ +		mTimeVisible.reset(); +	} + +	//-------------------------------------------------------------------- +	// The rest should only be done occasionally for far away avatars. +    // Set mUpdatePeriod and visible based on distance and other criteria. +	//-------------------------------------------------------------------- +    computeUpdatePeriod(); +    visible = (LLDrawable::getCurrentFrame()+mID.mData[0])%mUpdatePeriod == 0 ? TRUE : FALSE; + +	//-------------------------------------------------------------------- +    // Early out if not visible and not self +	// don't early out for your own avatar, as we rely on your animations playing reliably +	// for example, the "turn around" animation when entering customize avatar needs to trigger +	// even when your avatar is offscreen +	//-------------------------------------------------------------------- +	if (!visible && !isSelf()) +	{ +		updateMotions(LLCharacter::HIDDEN_UPDATE); +		return FALSE; +	} + +	//-------------------------------------------------------------------- +	// change animation time quanta based on avatar render load +    // AXON how should control avs be handled here? +	//-------------------------------------------------------------------- +    // SL-763 the time step quantization does not currently work. +    //updateTimeStep(); +     +	//-------------------------------------------------------------------- +    // Update sitting state based on parent and active animation info. +	//-------------------------------------------------------------------- +	if (getParent() && !isSitting()) +	{ +		sitOnObject((LLViewerObject*)getParent()); +	} +	else if (!getParent() && isSitting() && !isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED)) +	{ +		getOffObject(); +	} + +	//-------------------------------------------------------------------- +	// create local variables in world coords for region position values +	//-------------------------------------------------------------------- +	LLVector3 xyVel = getVelocity(); +	xyVel.mV[VZ] = 0.0f; +	F32 speed = xyVel.length(); +	// remembering the value here prevents a display glitch if the +	// animation gets toggled during this update. +	bool was_sit_ground_constrained = isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED); + +	//-------------------------------------------------------------------- +    // This does a bunch of state updating, including figuring out +    // whether av is in the air, setting mRoot position and rotation +    // In some cases, calls updateOrientation() for a lot of the +    // work +    // -------------------------------------------------------------------- +    updateRootPositionAndRotation(agent, speed, was_sit_ground_constrained);  	//-------------------------------------------------------------------------  	// Update character motions @@ -3904,7 +4163,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  	}  	// Special handling for sitting on ground. -	if (!getParent() && (mIsSitting || was_sit_ground_constrained)) +	if (!getParent() && (isSitting() || was_sit_ground_constrained))  	{  		F32 off_z = LLVector3d(getHoverOffset()).mdV[VZ]; @@ -3921,90 +4180,18 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  	// update head position  	updateHeadOffset(); -	//------------------------------------------------------------------------- -	// Find the ground under each foot, these are used for a variety -	// of things that follow -	//------------------------------------------------------------------------- -	LLVector3 ankle_left_pos_agent = mFootLeftp->getWorldPosition(); -	LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition(); - -	LLVector3 ankle_left_ground_agent = ankle_left_pos_agent; -	LLVector3 ankle_right_ground_agent = ankle_right_pos_agent; -	resolveHeightAgent(ankle_left_pos_agent, ankle_left_ground_agent, normal); -	resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal); - -	F32 leftElev = llmax(-0.2f, ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]); -	F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]); - -	if (!mIsSitting) -	{ -		//------------------------------------------------------------------------- -		// Figure out which foot is on ground -		//------------------------------------------------------------------------- -		if (!mInAir) -		{ -			if ((leftElev < 0.0f) || (rightElev < 0.0f)) -			{ -				ankle_left_pos_agent = mFootLeftp->getWorldPosition(); -				ankle_right_pos_agent = mFootRightp->getWorldPosition(); -				leftElev = ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]; -				rightElev = ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]; -			} -		} -	} -	 -	//-------------------------------------------------------------------------  	// Generate footstep sounds when feet hit the ground -	//------------------------------------------------------------------------- -	const LLUUID AGENT_FOOTSTEP_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_LAND}; -	const S32 NUM_AGENT_FOOTSTEP_ANIMS = LL_ARRAY_SIZE(AGENT_FOOTSTEP_ANIMS); - -	if ( gAudiop && isAnyAnimationSignaled(AGENT_FOOTSTEP_ANIMS, NUM_AGENT_FOOTSTEP_ANIMS) ) -	{ -		BOOL playSound = FALSE; -		LLVector3 foot_pos_agent; - -		BOOL onGroundLeft = (leftElev <= 0.05f); -		BOOL onGroundRight = (rightElev <= 0.05f); - -		// did left foot hit the ground? -		if ( onGroundLeft && !mWasOnGroundLeft ) -		{ -			foot_pos_agent = ankle_left_pos_agent; -			playSound = TRUE; -		} - -		// did right foot hit the ground? -		if ( onGroundRight && !mWasOnGroundRight ) -		{ -			foot_pos_agent = ankle_right_pos_agent; -			playSound = TRUE; -		} - -		mWasOnGroundLeft = onGroundLeft; -		mWasOnGroundRight = onGroundRight; - -		if ( playSound ) -		{ -			const F32 STEP_VOLUME = 0.1f; -			const LLUUID& step_sound_id = getStepSound(); - -			LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent); - -			if (LLViewerParcelMgr::getInstance()->canHearSound(foot_pos_global) -				&& !LLMuteList::getInstance()->isMuted(getID(), LLMute::flagObjectSounds)) -			{ -				gAudiop->triggerSound(step_sound_id, getID(), STEP_VOLUME, LLAudioEngine::AUDIO_TYPE_AMBIENT, foot_pos_global); -			} -		} -	} +    updateFootstepSounds(); +	// Update child joints as needed.  	mRoot->updateWorldMatrixChildren(); -	//mesh vertices need to be reskinned -	mNeedsSkin = TRUE; +	// System avatar mesh vertices need to be reskinned. +    mNeedsSkin = TRUE; +  	return TRUE;  } +  //-----------------------------------------------------------------------------  // updateHeadOffset()  //----------------------------------------------------------------------------- @@ -4019,7 +4206,7 @@ void LLVOAvatar::updateHeadOffset()  	{  		midEyePt = midEyePt * ~mDrawable->getWorldRotation();  	} -	if (mIsSitting) +	if (isSitting())  	{  		mHeadOffset = midEyePt;	  	} @@ -4115,7 +4302,7 @@ void LLVOAvatar::updateVisibility()  	if (mIsDummy)  	{ -		visible = TRUE; +		visible = FALSE;  	}  	else if (mDrawable.isNull())  	{ @@ -4229,7 +4416,8 @@ void LLVOAvatar::updateVisibility()  	}  	else  	{ -		if (mMeshValid && mMeshInvisibleTime.getElapsedTimeF32() > TIME_BEFORE_MESH_CLEANUP) +		if (mMeshValid && +            (isControlAvatar() || mMeshInvisibleTime.getElapsedTimeF32() > TIME_BEFORE_MESH_CLEANUP))  		{  			releaseMeshData();  		} @@ -4264,6 +4452,11 @@ U32 LLVOAvatar::renderSkinned()  		return num_indices;  	} +    if (mDrawable.isNull()) +    { +		return num_indices; +    } +  	LLFace* face = mDrawable->getFace(0);  	bool needs_rebuild = !face || !face->getVertexBuffer() || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY); @@ -4426,11 +4619,12 @@ U32 LLVOAvatar::renderSkinned()  		}  		BOOL first_pass = TRUE; +        bool is_pure_dummy = mIsDummy && !isControlAvatar();  		if (!LLDrawPoolAvatar::sSkipOpaque)  		{  			if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender)  			{ -				if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy) +				if (isTextureVisible(TEX_HEAD_BAKED) || is_pure_dummy)  				{  					LLViewerJoint* head_mesh = getViewerJoint(MESH_ID_HEAD);  					if (head_mesh) @@ -4440,7 +4634,7 @@ U32 LLVOAvatar::renderSkinned()  					first_pass = FALSE;  				}  			} -			if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy) +			if (isTextureVisible(TEX_UPPER_BAKED) || is_pure_dummy)  			{  				LLViewerJoint* upper_mesh = getViewerJoint(MESH_ID_UPPER_BODY);  				if (upper_mesh) @@ -4450,7 +4644,7 @@ U32 LLVOAvatar::renderSkinned()  				first_pass = FALSE;  			} -			if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy) +			if (isTextureVisible(TEX_LOWER_BAKED) || is_pure_dummy)  			{  				LLViewerJoint* lower_mesh = getViewerJoint(MESH_ID_LOWER_BODY);  				if (lower_mesh) @@ -4507,18 +4701,15 @@ U32 LLVOAvatar::renderTransparent(BOOL first_pass)  			}  			first_pass = FALSE;  		} -		// Can't test for baked hair being defined, since that won't always be the case (not all viewers send baked hair) -		// TODO: 1.25 will be able to switch this logic back to calling isTextureVisible(); -		if ( (getImage(TEX_HAIR_BAKED, 0) && getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE) -			|| LLDrawPoolAlpha::sShowDebugAlpha)		 -		{ -			LLViewerJoint* hair_mesh = getViewerJoint(MESH_ID_HAIR); -			if (hair_mesh) -			{ -				num_indices += hair_mesh->render(mAdjustedPixelArea, first_pass, mIsDummy); -			} -			first_pass = FALSE; -		} +		if (isTextureVisible(TEX_HAIR_BAKED)) +        { +            LLViewerJoint* hair_mesh = getViewerJoint(MESH_ID_HAIR); +            if (hair_mesh) +            { +                num_indices += hair_mesh->render(mAdjustedPixelArea, first_pass, mIsDummy); +            } +            first_pass = FALSE; +        }  		if (LLPipeline::sImpostorRender)  		{  			gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); @@ -4558,7 +4749,9 @@ U32 LLVOAvatar::renderRigid()  		gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f);  	} -	if (isTextureVisible(TEX_EYES_BAKED)  || mIsDummy) +    bool is_pure_dummy = mIsDummy && !isControlAvatar(); + +	if (isTextureVisible(TEX_EYES_BAKED)  || is_pure_dummy)  	{  		LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT);  		LLViewerJoint* eyeball_right = getViewerJoint(MESH_ID_EYEBALL_RIGHT); @@ -5105,10 +5298,13 @@ void LLVOAvatar::processAnimationStateChanges()  		startMotion(ANIM_AGENT_WALK_ADJUST);  		stopMotion(ANIM_AGENT_FLY_ADJUST);  	} -	else if (mInAir && !mIsSitting) +	else if (mInAir && !isSitting())  	{  		stopMotion(ANIM_AGENT_WALK_ADJUST); -		startMotion(ANIM_AGENT_FLY_ADJUST); +        if (mEnableDefaultMotions) +        { +            startMotion(ANIM_AGENT_FLY_ADJUST); +        }  	}  	else  	{ @@ -5118,13 +5314,19 @@ void LLVOAvatar::processAnimationStateChanges()  	if ( isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) )  	{ -		startMotion(ANIM_AGENT_TARGET); +        if (mEnableDefaultMotions) +        { +            startMotion(ANIM_AGENT_TARGET); +        }  		stopMotion(ANIM_AGENT_BODY_NOISE);  	}  	else  	{  		stopMotion(ANIM_AGENT_TARGET); -		startMotion(ANIM_AGENT_BODY_NOISE); +        if (mEnableDefaultMotions) +        { +            startMotion(ANIM_AGENT_BODY_NOISE); +        }  	}  	// clear all current animations @@ -5479,14 +5681,14 @@ bool LLVOAvatar::getRiggedMeshID(LLViewerObject* pVO, LLUUID& mesh_id)  		LLVOVolume* pVObj = pVO->mDrawable->getVOVolume();  		if ( pVObj )  		{ -			const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID(), pVObj ); +			const LLMeshSkinInfo* pSkinData = pVObj->getSkinInfo();  			if (pSkinData   				&& pSkinData->mJointNames.size() > JOINT_COUNT_REQUIRED_FOR_FULLRIG	// full rig  				&& pSkinData->mAlternateBindMatrix.size() > 0 ) -					{				 -						mesh_id = pSkinData->mMeshID; -						return true; -					} +            {				 +                mesh_id = pSkinData->mMeshID; +                return true; +            }  		}  	}  	return false; @@ -5533,8 +5735,7 @@ bool LLVOAvatar::jointIsRiggedTo(const std::string& joint_name, const LLViewerOb  		return false;  	} -	LLUUID currentId = vobj->getVolume()->getParams().getSculptID();						 -	const LLMeshSkinInfo*  pSkinData = gMeshRepo.getSkinInfo( currentId, vobj ); +	const LLMeshSkinInfo* pSkinData = vobj->getSkinInfo();  	if ( vobj && vobj->isAttachment() && vobj->isMesh() && pSkinData )  	{ @@ -5570,7 +5771,18 @@ void LLVOAvatar::rebuildAttachmentOverrides()  {      LLScopedContextString str("rebuildAttachmentOverrides " + getFullname()); -    // Attachment points +    // Handle the case that we're resetting the skeleton of an animated object. +    LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this); +    if (control_av) +    { +        LLVOVolume *volp = control_av->mRootVolp; +        if (volp) +        { +            addAttachmentOverridesForObject(volp); +        } +    } + +    // Attached objects  	for (attachment_map_t::iterator iter = mAttachmentPoints.begin();  		 iter != mAttachmentPoints.end();  		 ++iter) @@ -5581,24 +5793,30 @@ void LLVOAvatar::rebuildAttachmentOverrides()              for (LLViewerJointAttachment::attachedobjs_vec_t::iterator at_it = attachment_pt->mAttachedObjects.begin();  				 at_it != attachment_pt->mAttachedObjects.end(); ++at_it)              { -                addAttachmentOverridesForObject(*at_it); +                LLViewerObject *vo = *at_it; +                // Attached animated objects affect joints in their control +                // avs, not the avs to which they are attached. +                if (!vo->isAnimatedObject()) +                { +                    addAttachmentOverridesForObject(vo); +                }              }          }      }  } +  //-----------------------------------------------------------------------------  // addAttachmentPosOverridesForObject  //-----------------------------------------------------------------------------  void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)  { -	LLVOAvatar *av = vo->getAvatarAncestor(); -	if (!av || (av != this)) -	{ +    if (vo->getAvatar() != this && vo->getAvatarAncestor() != this) +    {  		LL_WARNS("Avatar") << "called with invalid avatar" << LL_ENDL;          return; -	} +    } -    LLScopedContextString str("addAttachmentOverridesForObject " + av->getFullname()); +    LLScopedContextString str("addAttachmentOverridesForObject " + vo->getAvatar()->getFullname());  	// Process all children  	LLViewerObject::const_child_list_t& children = vo->getChildren(); @@ -5621,10 +5839,9 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)  	{  		return;  	} -	LLUUID currentId = vobj->getVolume()->getParams().getSculptID();						 -	const LLMeshSkinInfo*  pSkinData = gMeshRepo.getSkinInfo( currentId, vobj ); +	const LLMeshSkinInfo*  pSkinData = vobj->getSkinInfo(); -	if ( vobj && vobj->isAttachment() && vobj->isMesh() && pSkinData ) +	if ( vobj && vobj->isMesh() && pSkinData )  	{  		const int bindCnt = pSkinData->mAlternateBindMatrix.size();								          const int jointCnt = pSkinData->mJointNames.size(); @@ -5806,14 +6023,14 @@ void LLVOAvatar::showAttachmentOverrides(bool verbose) const  }  //----------------------------------------------------------------------------- -// resetJointsOnDetach +// removeAttachmentOverridesForObject  //----------------------------------------------------------------------------- -void LLVOAvatar::resetJointsOnDetach(LLViewerObject *vo) +void LLVOAvatar::removeAttachmentOverridesForObject(LLViewerObject *vo)  { -	LLVOAvatar *av = vo->getAvatarAncestor(); -	if (!av || (av != this)) +    if (vo->getAvatar() != this && vo->getAvatarAncestor() != this)  	{  		LL_WARNS("Avatar") << "called with invalid avatar" << LL_ENDL; +        return;  	}  	// Process all children @@ -5822,21 +6039,21 @@ void LLVOAvatar::resetJointsOnDetach(LLViewerObject *vo)  		 it != children.end(); ++it)  	{  		LLViewerObject *childp = *it; -		resetJointsOnDetach(childp); +		removeAttachmentOverridesForObject(childp);  	}  	// Process self.  	LLUUID mesh_id;  	if (getRiggedMeshID(vo,mesh_id))  	{ -		resetJointsOnDetach(mesh_id); +		removeAttachmentOverridesForObject(mesh_id);  	}  }  //----------------------------------------------------------------------------- -// resetJointsOnDetach +// removeAttachmentOverridesForObject  //----------------------------------------------------------------------------- -void LLVOAvatar::resetJointsOnDetach(const LLUUID& mesh_id) +void LLVOAvatar::removeAttachmentOverridesForObject(const LLUUID& mesh_id)  {	  	//Subsequent joints are relative to pelvis  	avatar_joint_list_t::iterator iter = mSkeleton.begin(); @@ -5914,6 +6131,7 @@ void LLVOAvatar::getGround(const LLVector3 &in_pos_agent, LLVector3 &out_pos_age  	LLVector3d z_vec(0.0f, 0.0f, 1.0f);  	LLVector3d p0_global, p1_global; +    // AXON UPDATE FOR CONTROL AVS?  	if (mIsDummy)  	{  		outNorm.setVec(z_vec); @@ -5943,6 +6161,7 @@ F32 LLVOAvatar::getTimeDilation()  //-----------------------------------------------------------------------------  F32 LLVOAvatar::getPixelArea() const  { +    // AXON UPDATE FOR CONTROL AVATARS  	if (mIsDummy)  	{  		return 100000.f; @@ -6358,7 +6577,7 @@ void LLVOAvatar::removeChild(LLViewerObject *childp)  LLViewerJointAttachment* LLVOAvatar::getTargetAttachmentPoint(LLViewerObject* viewer_object)  { -	S32 attachmentID = ATTACHMENT_ID_FROM_STATE(viewer_object->getState()); +	S32 attachmentID = ATTACHMENT_ID_FROM_STATE(viewer_object->getAttachmentState());  	// This should never happen unless the server didn't process the attachment point  	// correctly, but putting this check in here to be safe. @@ -6450,19 +6669,63 @@ U32 LLVOAvatar::getNumAttachments() const  //-----------------------------------------------------------------------------  // canAttachMoreObjects() +// Returns true if we can attach <n> more objects.  //----------------------------------------------------------------------------- -BOOL LLVOAvatar::canAttachMoreObjects() const +BOOL LLVOAvatar::canAttachMoreObjects(U32 n) const  { -	return (getNumAttachments() < MAX_AGENT_ATTACHMENTS); +	return (getNumAttachments() + n) <= MAX_AGENT_ATTACHMENTS;  }  //----------------------------------------------------------------------------- -// canAttachMoreObjects() -// Returns true if we can attach <n> more objects. +// getNumAnimatedObjectAttachments()  //----------------------------------------------------------------------------- -BOOL LLVOAvatar::canAttachMoreObjects(U32 n) const +U32 LLVOAvatar::getNumAnimatedObjectAttachments() const  { -	return (getNumAttachments() + n) <= MAX_AGENT_ATTACHMENTS; +	U32 num_attachments = 0; +	for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); +		 iter != mAttachmentPoints.end(); +		 ++iter) +	{ +		const LLViewerJointAttachment *attachment_pt = (*iter).second; +		num_attachments += attachment_pt->getNumAnimatedObjects(); +	} +	return num_attachments; +} + +//----------------------------------------------------------------------------- +// getMaxAnimatedObjectAttachments() +// Gets from simulator feature if available, otherwise 0. +//----------------------------------------------------------------------------- +S32 LLVOAvatar::getMaxAnimatedObjectAttachments() const +{ +    S32 max_attach = 0; +    // AXON REMOVE AFTER SERVER TESTING DONE +    if (gSavedSettings.getBOOL("AnimatedObjectsIgnoreLimits")) +    { +        max_attach = MAX_AGENT_ATTACHMENTS; +    } +    else +    { +        if (gAgent.getRegion()) +        { +            LLSD features; +            gAgent.getRegion()->getSimulatorFeatures(features); +            if (features.has("AnimatedObjects")) +            { +                max_attach = features["AnimatedObjects"]["MaxAgentAnimatedObjectAttachments"].asInteger(); +            } +        } +    } +    return max_attach; +} + +//----------------------------------------------------------------------------- +// canAttachMoreAnimatedObjects() +// Returns true if we can attach <n> more animated objects. +//----------------------------------------------------------------------------- +BOOL LLVOAvatar::canAttachMoreAnimatedObjects(U32 n) const +{ +	return (getNumAnimatedObjectAttachments() + n) <= getMaxAnimatedObjectAttachments();  }  //----------------------------------------------------------------------------- @@ -6553,7 +6816,7 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )  	LLUUID mesh_id;  	if (getRiggedMeshID(pVO, mesh_id))  	{ -		resetJointsOnDetach(mesh_id); +		removeAttachmentOverridesForObject(mesh_id);  		if ( gAgentCamera.cameraCustomizeAvatar() )  		{  			gAgent.unpauseAnimation(); @@ -6714,7 +6977,10 @@ void LLVOAvatar::getOffObject()  	mRoot->setRotation(cur_rotation_world);  	mRoot->getXform()->update(); -	startMotion(ANIM_AGENT_BODY_NOISE); +    if (mEnableDefaultMotions) +    { +        startMotion(ANIM_AGENT_BODY_NOISE); +    }  	if (isSelf())  	{ @@ -6874,6 +7140,7 @@ void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color)  BOOL LLVOAvatar::isVisible() const  { +    // AXON should we flag control avs as invisible?  	return mDrawable.notNull()  		&& (!mOrphaned || isSelf())  		&& (mDrawable->isVisible() || mIsDummy); @@ -6882,6 +7149,11 @@ BOOL LLVOAvatar::isVisible() const  // Determine if we have enough avatar data to render  bool LLVOAvatar::getIsCloud() const  { +	if (mIsDummy) +	{ +		return false; +	} +  	return (   ((const_cast<LLVOAvatar*>(this))->visualParamWeightsAreDefault())// Do we have a shape?  			|| (   !isTextureDefined(TEX_LOWER_BAKED)  				|| !isTextureDefined(TEX_UPPER_BAKED) @@ -7161,9 +7433,9 @@ bool LLVOAvatar::isTooComplex() const          // so that unlimited will completely disable the overly complex impostor rendering          // yes, this leaves them vulnerable to griefing objects... their choice          too_complex = (   max_render_cost > 0 -                       && (   mVisualComplexity > max_render_cost -                           || (max_attachment_area > 0.0f && mAttachmentSurfaceArea > max_attachment_area) -                           )); +                          && (mVisualComplexity > max_render_cost +                                 || (max_attachment_area > 0.0f && mAttachmentSurfaceArea > max_attachment_area) +                              ));  	}  	return too_complex; @@ -8846,6 +9118,11 @@ void LLVOAvatar::updateFreezeCounter(S32 counter)  BOOL LLVOAvatar::updateLOD()  { +    if (mDrawable.isNull()) +    { +        return FALSE; +    } +      	if (isImpostor() && 0 != mDrawable->getNumFaces() && mDrawable->getFace(0)->hasGeometry())  	{  		return TRUE; @@ -8886,10 +9163,10 @@ U32 LLVOAvatar::getPartitionType() const  void LLVOAvatar::updateImpostors()  {  	LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD; -	LLCharacter::sAllowInstancesChange = FALSE; -	for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin(); -		iter != LLCharacter::sInstances.end(); ++iter) +    std::vector<LLCharacter*> instances_copy = LLCharacter::sInstances; +	for (std::vector<LLCharacter*>::iterator iter = instances_copy.begin(); +		iter != instances_copy.end(); ++iter)  	{  		LLVOAvatar* avatar = (LLVOAvatar*) *iter;  		if (!avatar->isDead() && avatar->isVisible() @@ -8986,6 +9263,17 @@ void LLVOAvatar::updateImpostorRendering(U32 newMaxNonImpostorsValue)  void LLVOAvatar::idleUpdateRenderComplexity()  { +    if (isControlAvatar()) +    { +        LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this); +        bool is_attachment = cav && cav->mRootVolp && cav->mRootVolp->isAttachment(); // For attached animated objects +        if (is_attachment) +        { +            // ARC for animated object attachments is accounted with the avatar they're attached to. +            return; +        } +    } +      // Render Complexity      calculateUpdateRenderComplexity(); // Update mVisualComplexity if needed	 @@ -9033,10 +9321,16 @@ void LLVOAvatar::idleUpdateRenderComplexity()  		// Visual rank  		info_line = llformat("%d rank", mVisibilityRank);  		// Use grey for imposters, white for normal rendering or no impostors -		info_color.set(isImpostor() ? LLColor4::grey : LLColor4::white); +		info_color.set(isImpostor() ? LLColor4::grey : (isControlAvatar() ? LLColor4::yellow : LLColor4::white));  		info_style = LLFontGL::NORMAL;  		mText->addLine(info_line, info_color, info_style); +        // Triangle count +        mText->addLine(std::string("VisTris ") + LLStringOps::getReadableNumber(mAttachmentVisibleTriangleCount),  +                       info_color, info_style); +        mText->addLine(std::string("EstMaxTris ") + LLStringOps::getReadableNumber(mAttachmentEstTriangleCount),  +                       info_color, info_style); +  		// Attachment Surface Area  		static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit", 1000.0f);  		info_line = llformat("%.0f m^2", mAttachmentSurfaceArea); @@ -9055,22 +9349,13 @@ void LLVOAvatar::idleUpdateRenderComplexity()  			info_color.set(LLColor4::grey);  			info_style = LLFontGL::NORMAL;  		} +  		mText->addLine(info_line, info_color, info_style);  		updateText(); // corrects position  	}  } -void LLVOAvatar::addAttachmentArea(F32 delta_area) -{ -    mAttachmentSurfaceArea   += delta_area; -} - -void LLVOAvatar::subtractAttachmentArea(F32 delta_area) -{ -    mAttachmentSurfaceArea   = delta_area > mAttachmentSurfaceArea ? 0.0 : mAttachmentSurfaceArea - delta_area; -} -  void LLVOAvatar::updateVisualComplexity()  {  	LL_DEBUGS("AvatarRender") << "avatar " << getID() << " appearance changed" << LL_ENDL; @@ -9078,6 +9363,136 @@ void LLVOAvatar::updateVisualComplexity()  	mVisualComplexityStale = true;  } +// Account for the complexity of a single top-level object associated +// with an avatar. This will be either an attached object or an animated +// object. +void LLVOAvatar::accountRenderComplexityForObject( +    const LLViewerObject *attached_object, +    const F32 max_attachment_complexity, +    LLVOVolume::texture_cost_t& textures, +    U32& cost, +    hud_complexity_list_t& hud_complexity_list) +{ +    if (attached_object && !attached_object->isHUDAttachment()) +    { +        mAttachmentVisibleTriangleCount += attached_object->recursiveGetTriangleCount(); +        mAttachmentEstTriangleCount += attached_object->recursiveGetEstTrianglesMax(); +        mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea(); + +        textures.clear(); +        const LLDrawable* drawable = attached_object->mDrawable; +        if (drawable) +        { +            const LLVOVolume* volume = drawable->getVOVolume(); +            if (volume) +            { +                F32 attachment_total_cost = 0; +                F32 attachment_volume_cost = 0; +                F32 attachment_texture_cost = 0; +                F32 attachment_children_cost = 0; +                // AXON placeholder value, will revisit in testing. +                const F32 animated_object_attachment_surcharge = 20000; + +                if (attached_object->isAnimatedObject()) +                { +                    attachment_volume_cost += animated_object_attachment_surcharge; +                } +                attachment_volume_cost += volume->getRenderCost(textures); + +                const_child_list_t children = volume->getChildren(); +                for (const_child_list_t::const_iterator child_iter = children.begin(); +                     child_iter != children.end(); +                     ++child_iter) +                { +                    LLViewerObject* child_obj = *child_iter; +                    LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj ); +                    if (child) +                    { +                        attachment_children_cost += child->getRenderCost(textures); +                    } +                } + +                for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin(); +                     volume_texture != textures.end(); +                     ++volume_texture) +                { +                    // add the cost of each individual texture in the linkset +                    attachment_texture_cost += volume_texture->second; +                } +                attachment_total_cost = attachment_volume_cost + attachment_texture_cost + attachment_children_cost; +                LL_DEBUGS("ARCdetail") << "Attachment costs " << attached_object->getAttachmentItemID() +                                       << " total: " << attachment_total_cost +                                       << ", volume: " << attachment_volume_cost +                                       << ", textures: " << attachment_texture_cost +                                       << ", " << volume->numChildren() +                                       << " children: " << attachment_children_cost +                                       << LL_ENDL; +                // Limit attachment complexity to avoid signed integer flipping of the wearer's ACI +                cost += (U32)llclamp(attachment_total_cost, MIN_ATTACHMENT_COMPLEXITY, max_attachment_complexity); +            } +        } +    } +    if (isSelf() +        && attached_object +        && attached_object->isHUDAttachment() +        && !attached_object->isTempAttachment() +        && attached_object->mDrawable) +    { +        textures.clear(); + +        mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea(); + +        const LLVOVolume* volume = attached_object->mDrawable->getVOVolume(); +        if (volume) +        { +            LLHUDComplexity hud_object_complexity; +            hud_object_complexity.objectName = attached_object->getAttachmentItemName(); +            hud_object_complexity.objectId = attached_object->getAttachmentItemID(); +            std::string joint_name; +            gAgentAvatarp->getAttachedPointName(attached_object->getAttachmentItemID(), joint_name); +            hud_object_complexity.jointName = joint_name; +            // get cost and individual textures +            hud_object_complexity.objectsCost += volume->getRenderCost(textures); +            hud_object_complexity.objectsCount++; + +            LLViewerObject::const_child_list_t& child_list = attached_object->getChildren(); +            for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); +                 iter != child_list.end(); ++iter) +            { +                LLViewerObject* childp = *iter; +                const LLVOVolume* chld_volume = dynamic_cast<LLVOVolume*>(childp); +                if (chld_volume) +                { +                    // get cost and individual textures +                    hud_object_complexity.objectsCost += chld_volume->getRenderCost(textures); +                    hud_object_complexity.objectsCount++; +                } +            } + +            hud_object_complexity.texturesCount += textures.size(); + +            for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin(); +                 volume_texture != textures.end(); +                 ++volume_texture) +            { +                // add the cost of each individual texture (ignores duplicates) +                hud_object_complexity.texturesCost += volume_texture->second; +                LLViewerFetchedTexture *tex = LLViewerTextureManager::getFetchedTexture(volume_texture->first); +                if (tex) +                { +                    // Note: Texture memory might be incorect since texture might be still loading. +                    hud_object_complexity.texturesMemoryTotal += tex->getTextureMemory(); +                    if (tex->getOriginalHeight() * tex->getOriginalWidth() >= HUD_OVERSIZED_TEXTURE_DATA_SIZE) +                    { +                        hud_object_complexity.largeTexturesCount++; +                    } +                } +            } +            hud_complexity_list.push_back(hud_object_complexity); +        } +    } +} +  // Calculations for mVisualComplexity value  void LLVOAvatar::calculateUpdateRenderComplexity()  { @@ -9116,7 +9531,25 @@ void LLVOAvatar::calculateUpdateRenderComplexity()  		}          LL_DEBUGS("ARCdetail") << "Avatar body parts complexity: " << cost << LL_ENDL; +        mAttachmentVisibleTriangleCount = 0; +        mAttachmentEstTriangleCount = 0.f; +        mAttachmentSurfaceArea = 0.f; +         +        // A standalone animated object needs to be accounted for +        // using its associated volume. Attached animated objects +        // will be covered by the subsequent loop over attachments. +        LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this); +        if (control_av) +        { +            LLVOVolume *volp = control_av->mRootVolp; +            if (volp && !volp->isAttachment()) +            { +                accountRenderComplexityForObject(volp, max_attachment_complexity, +                                                 textures, cost, hud_complexity_list); +            } +        } +        // Account for complexity of all attachments.  		for (attachment_map_t::const_iterator attachment_point = mAttachmentPoints.begin();   			 attachment_point != mAttachmentPoints.end();  			 ++attachment_point) @@ -9127,112 +9560,8 @@ void LLVOAvatar::calculateUpdateRenderComplexity()  				 ++attachment_iter)  			{  				const LLViewerObject* attached_object = (*attachment_iter); -				if (attached_object && !attached_object->isHUDAttachment()) -				{ -					textures.clear(); -					const LLDrawable* drawable = attached_object->mDrawable; -					if (drawable) -					{ -						const LLVOVolume* volume = drawable->getVOVolume(); -						if (volume) -						{ -                            F32 attachment_total_cost = 0; -                            F32 attachment_volume_cost = 0; -                            F32 attachment_texture_cost = 0; -                            F32 attachment_children_cost = 0; - -							attachment_volume_cost += volume->getRenderCost(textures); - -							const_child_list_t children = volume->getChildren(); -							for (const_child_list_t::const_iterator child_iter = children.begin(); -								  child_iter != children.end(); -								  ++child_iter) -							{ -								LLViewerObject* child_obj = *child_iter; -								LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj ); -								if (child) -								{ -									attachment_children_cost += child->getRenderCost(textures); -								} -							} - -							for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin(); -								 volume_texture != textures.end(); -								 ++volume_texture) -							{ -								// add the cost of each individual texture in the linkset -								attachment_texture_cost += volume_texture->second; -							} -                            attachment_total_cost = attachment_volume_cost + attachment_texture_cost + attachment_children_cost; -                            LL_DEBUGS("ARCdetail") << "Attachment costs " << attached_object->getAttachmentItemID() -                                                   << " total: " << attachment_total_cost -                                                   << ", volume: " << attachment_volume_cost -                                                   << ", textures: " << attachment_texture_cost -                                                   << ", " << volume->numChildren() -                                                   << " children: " << attachment_children_cost -                                                   << LL_ENDL; -                            // Limit attachment complexity to avoid signed integer flipping of the wearer's ACI -                            cost += (U32)llclamp(attachment_total_cost, MIN_ATTACHMENT_COMPLEXITY, max_attachment_complexity); -						} -					} -				} -                if (isSelf() -                    && attached_object -                    && attached_object->isHUDAttachment() -                    && !attached_object->isTempAttachment() -                    && attached_object->mDrawable) -                { -                    textures.clear(); - -                    const LLVOVolume* volume = attached_object->mDrawable->getVOVolume(); -                    if (volume) -                    { -                        LLHUDComplexity hud_object_complexity; -                        hud_object_complexity.objectName = attached_object->getAttachmentItemName(); -                        hud_object_complexity.objectId = attached_object->getAttachmentItemID(); -                        std::string joint_name; -                        gAgentAvatarp->getAttachedPointName(attached_object->getAttachmentItemID(), joint_name); -                        hud_object_complexity.jointName = joint_name; -                        // get cost and individual textures -                        hud_object_complexity.objectsCost += volume->getRenderCost(textures); -                        hud_object_complexity.objectsCount++; - -                        LLViewerObject::const_child_list_t& child_list = attached_object->getChildren(); -                        for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); -                            iter != child_list.end(); ++iter) -                        { -                            LLViewerObject* childp = *iter; -                            const LLVOVolume* chld_volume = dynamic_cast<LLVOVolume*>(childp); -                            if (chld_volume) -                            { -                                // get cost and individual textures -                                hud_object_complexity.objectsCost += chld_volume->getRenderCost(textures); -                                hud_object_complexity.objectsCount++; -                            } -                        } - -                        hud_object_complexity.texturesCount += textures.size(); - -                        for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin(); -                            volume_texture != textures.end(); -                            ++volume_texture) -                        { -                            // add the cost of each individual texture (ignores duplicates) -                            hud_object_complexity.texturesCost += volume_texture->second; -                            LLViewerFetchedTexture *tex = LLViewerTextureManager::getFetchedTexture(volume_texture->first); -                            if (tex) -                            { -                                // Note: Texture memory might be incorect since texture might be still loading. -                                hud_object_complexity.texturesMemoryTotal += tex->getTextureMemory(); -                                if (tex->getOriginalHeight() * tex->getOriginalWidth() >= HUD_OVERSIZED_TEXTURE_DATA_SIZE) -                                { -                                    hud_object_complexity.largeTexturesCount++; -                                } -                            } -                        } -                        hud_complexity_list.push_back(hud_object_complexity); -                    } -                } +                accountRenderComplexityForObject(attached_object, max_attachment_complexity, +                                                 textures, cost, hud_complexity_list);  			}  		} diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index bd89d4ef23..730373d79c 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -50,6 +50,8 @@  #include "llviewertexlayer.h"  #include "material_codes.h"		// LL_MCODE_END  #include "llviewerstats.h" +#include "llvovolume.h" +#include "llavatarrendernotifier.h"  extern const LLUUID ANIM_AGENT_BODY_NOISE;  extern const LLUUID ANIM_AGENT_BREATHE_ROT; @@ -169,7 +171,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, @@ -202,8 +205,8 @@ public:  	LLJoint*		        getJoint(S32 num);  	void 					addAttachmentOverridesForObject(LLViewerObject *vo); -	void					resetJointsOnDetach(const LLUUID& mesh_id); -	void					resetJointsOnDetach(LLViewerObject *vo); +	void					removeAttachmentOverridesForObject(const LLUUID& mesh_id); +	void					removeAttachmentOverridesForObject(LLViewerObject *vo);      bool					jointIsRiggedTo(const std::string& joint_name);      bool					jointIsRiggedTo(const std::string& joint_name, const LLViewerObject *vo);  	void					clearAttachmentOverrides(); @@ -233,6 +236,8 @@ 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) +  private: //aligned members  	LL_ALIGN_16(LLVector4a	mImpostorExtents[2]); @@ -240,8 +245,14 @@ private: //aligned members  	// Updates  	//--------------------------------------------------------------------  public: -	void			updateDebugText(); +	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 +270,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 +436,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 +457,13 @@ public:  	VisualMuteSettings		mVisuallyMuteSetting;			// Always or never visually mute this AV  	//-------------------------------------------------------------------- +	// animated object status +	//-------------------------------------------------------------------- +public: +    bool mIsControlAvatar; +    bool mEnableDefaultMotions; + +	//--------------------------------------------------------------------  	// Morph masks  	//--------------------------------------------------------------------  public: @@ -739,9 +762,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 +784,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   **                                                                            ** diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 10af524ee7..a2144d6466 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -2791,7 +2791,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 de63a3963c..5def0f0c0f 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 f77b48ff80..fbbf0634ff 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -76,8 +76,13 @@  #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"  const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;  const F32 FORCE_CULL_AREA = 8.f; @@ -86,6 +91,7 @@ U32 JOINT_COUNT_REQUIRED_FOR_FULLRIG = 1;  BOOL gAnimateTextures = TRUE;  //extern BOOL gHideSelectedObjects; +S32 LLVOVolume::sForceLOD = -1;  F32 LLVOVolume::sLODFactor = 1.f;  F32	LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop   F32 LLVOVolume::sDistanceFactor = 1.0f; @@ -1096,16 +1102,26 @@ 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(); -	} +    updateVisualComplexity();  }  // sculpt replaces generate() for sculpted surfaces @@ -1217,16 +1233,24 @@ void LLVOVolume::sculpt()  S32	LLVOVolume::computeLODDetail(F32 distance, F32 radius)  {  	S32	cur_detail; -	if (LLPipeline::sDynamicLOD) -	{ -		// We've got LOD in the profile, and in the twist.  Use radius. -		F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance; -		cur_detail = LLVolumeLODGroup::getDetailFromTan(ll_round(tan_angle, 0.01f)); -	} -	else -	{ -		cur_detail = llclamp((S32) (sqrtf(radius)*LLVOVolume::sLODFactor*4.f), 0, 3);		 -	} +    // AXON TEMP REMOVE +    if (LLVOVolume::sForceLOD>=0 && LLVOVolume::sForceLOD<=3) +    { +        cur_detail = LLVOVolume::sForceLOD; +    } +    else +    { +        if (LLPipeline::sDynamicLOD) +        { +            // We've got LOD in the profile, and in the twist.  Use radius. +            F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance; +            cur_detail = LLVolumeLODGroup::getDetailFromTan(ll_round(tan_angle, 0.01f)); +        } +        else +        { +            cur_detail = llclamp((S32) (sqrtf(radius)*LLVOVolume::sLODFactor*4.f), 0, 3);		 +        } +    }  	return cur_detail;  } @@ -1280,22 +1304,34 @@ BOOL LLVOVolume::calcLOD()  	distance *= F_PI/3.f;  	cur_detail = computeLODDetail(ll_round(distance, 0.01f),  -									ll_round(radius, 0.01f)); - +                                  ll_round(radius, 0.01f)); +    if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TRIANGLE_COUNT) && mDrawable->getFace(0)) +    { +        if (isRootEdit() && getChildren().size()>0) +        { +            S32 total_tris = recursiveGetTriangleCount(); +            setDebugText(llformat("TRIS %d TOTAL %d", getTriangleCount(), total_tris)); +        } +        else +        { +            setDebugText(llformat("TRIS %d", getTriangleCount())); +        } +	 +    }  	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)  	{  		mAppAngle = ll_round((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f);  		mLOD = cur_detail;		 -		return TRUE; + +        return TRUE;  	}  	return FALSE; @@ -1312,6 +1348,15 @@ BOOL LLVOVolume::updateLOD()  	if (lod_changed)  	{ +#if 1 +        // AXON debugging +        if (isAnimatedObject() && isRiggedMesh()) +        { +            std::string vobj_name = llformat("Vol%u", (U32) this); +            F32 est_tris = getEstTrianglesMax(); +            LL_DEBUGS("AXONLinkset") << vobj_name << " updateLOD to " << getLOD() << ", tris " << est_tris << LL_ENDL;  +        } +#endif  		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);  		mLODChanged = TRUE;  	} @@ -1386,7 +1431,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) @@ -1394,6 +1440,7 @@ BOOL LLVOVolume::setParent(LLViewerObject* parent)  			gPipeline.markMoved(mDrawable);  			gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);  		} +        updateAnimatedObjectStateOnReparent(old_parent, parent);  	}  	return ret ; @@ -1651,6 +1698,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; @@ -3269,6 +3321,152 @@ 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) +{ +    // AXON - the check against isAnySelected() is "empirically +    // derived": doing rebuildGeom() while in selection trashes the +    // graphics state of animated objects. Skipping this update is OK +    // because we get another one on deselect. + +	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()->removeAttachmentOverridesForObject(this); +        } +        else +        { +            // Making an animated object into a rigged mesh +            getAvatarAncestor()->addAttachmentOverridesForObject(this); +        } +    } +} + +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("AXON") << (U32) 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; +#if 0 +    if (root_is_animated_flag) +    { +        bool root_can_be_animated = root_vol->canBeAnimatedObject(); +        bool this_can_be_animated = (root_vol == this) ? root_can_be_animated : canBeAnimatedObject(); +        if (this_can_be_animated && root_can_be_animated) +        { +            return true; +        } +    } +    return false; +#endif +} + +// 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. +void LLVOVolume::updateAnimatedObjectStateOnReparent(LLViewerObject *old_parent, LLViewerObject *new_parent) +{ +    LLVOVolume *old_volp = dynamic_cast<LLVOVolume*>(old_parent); + +    // AXON - depending on whether animated objects can be attached, +    // we may want to include or remove the isAvatar() check. +    // BUG?? +    if (new_parent && !new_parent->isAvatar()) +    { +        if (mControlAvatar.notNull()) +        { +            LLControlAvatar *av = mControlAvatar; +            mControlAvatar = NULL; +            av->markForDeath(); +        } +        // If this succeeds now, it's because the new_parent is an animated object +        if (isAnimatedObject() && getControlAvatar()) +        { +            getControlAvatar()->addAttachmentOverridesForObject(this); +        } +    } +    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()->removeAttachmentOverridesForObject(this); +        } +    } +} +  //----------------------------------------------------------------------------  void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point) @@ -3330,7 +3528,7 @@ void LLVOVolume::updateRadius()  BOOL LLVOVolume::isAttachment() const  { -	return mState != 0 ; +	return mAttachmentState != 0 ;  }  BOOL LLVOVolume::isHUDAttachment() const @@ -3338,7 +3536,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 );  } @@ -3441,7 +3639,7 @@ 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; @@ -3623,6 +3821,15 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const  	return (U32)shame;  } +F32 LLVOVolume::getEstTrianglesMax() const +{ +	if (isMesh() && getVolume()) +	{ +		return gMeshRepo.getEstTrianglesMax(getVolume()->getParams().getSculptID()); +	} +    return 0.f; +} +  F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const  {  	F32 radius = getScale().length()*0.5f; @@ -3714,6 +3921,24 @@ 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); +        // AXON This is kind of a guess. Better if we could compare +        // the before and after flags directly. What about cases where +        // there's no control avatar for optimization reasons? +        bool was_enabled = (getControlAvatar() != NULL); +        if (enabled != was_enabled) +        { +            LL_DEBUGS("AXON") << (U32) 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(); @@ -3727,10 +3952,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) @@ -3840,6 +4072,21 @@ const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const  	return xform->getWorldMatrix();  } +void LLVOVolume::markForUpdate(BOOL priority) +{  +#if 1 +	// AXON debugging +    if (isAnimatedObject() && isRiggedMesh()) +    { +        std::string vobj_name = llformat("Vol%u", (U32) this); +        F32 est_tris = getEstTrianglesMax(); +        LL_DEBUGS("AXONLinkset") << vobj_name << " markForUpdate, tris " << est_tris << LL_ENDL;  +    } +#endif +    LLViewerObject::markForUpdate(priority);  +    mVolumeChanged = TRUE;  +} +  LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const  {  	LLVector3 ret = pos - getRenderPosition(); @@ -4092,9 +4339,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() @@ -4124,9 +4371,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(); @@ -4134,7 +4379,6 @@ void LLVOVolume::updateRiggedVolume(bool force_update)  	}  	LLVOAvatar* avatar = getAvatar(); -  	if (!avatar)  	{  		clearRiggedVolume(); @@ -4149,7 +4393,6 @@ void LLVOVolume::updateRiggedVolume(bool force_update)  	}  	mRiggedVolume->update(skin, avatar, volume); -  }  static LLTrace::BlockTimerStatHandle FTM_SKIN_RIGGED("Skin"); @@ -4187,6 +4430,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); @@ -4208,6 +4454,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; @@ -4227,12 +4475,19 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons  				min = pos[0];  				max = pos[1]; +                if (i==0) +                { +                    box_min = min; +                    box_max = max; +                }  				for (U32 j = 1; j < dst_face.mNumVertices; ++j)  				{  					min.setMin(min, pos[j]);  					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); @@ -4252,6 +4507,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 @@ -4643,8 +4902,6 @@ static LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj)  void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  { -	 -  	if (group->changeLOD())  	{  		group->mLastUpdateDistance = group->mDistance; @@ -4665,27 +4922,29 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  	group->mBuilt = 1.f; -	LLVOAvatar* pAvatarVO = NULL; +	LLVOAvatar *rigged_av = NULL;  	LLSpatialBridge* bridge = group->getSpatialPartition()->asBridge(); +    LLViewerObject *vobj = NULL; +    LLVOVolume *vol_obj = NULL; +  	if (bridge)  	{ +        vobj = bridge->mDrawable->getVObj(); +        vol_obj = dynamic_cast<LLVOVolume*>(vobj);  		if (bridge->mAvatar.isNull())  		{ -			LLViewerObject* vobj = bridge->mDrawable->getVObj();  			if (vobj)  			{  				bridge->mAvatar = vobj->getAvatar();  			}  		} - -		pAvatarVO = bridge->mAvatar; -	} - -	if (pAvatarVO) -	{ -		pAvatarVO->subtractAttachmentArea( group->mSurfaceArea ); +        rigged_av = bridge->mAvatar;  	} +    if (vol_obj) +    { +        vol_obj->updateVisualComplexity(); +    }  	group->mGeometryBytes = 0;  	group->mSurfaceArea = 0; @@ -4728,7 +4987,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(); @@ -4743,12 +5003,14 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  			}  			LLVOVolume* vobj = drawablep->getVOVolume(); - +              			if (!vobj)  			{  				continue;  			} +            std::string vobj_name = llformat("Vol%u", (U32) vobj); +  			if (vobj->isMesh() &&  				((vobj->getVolume() && !vobj->getVolume()->isMeshAssetLoaded()) || !gMeshRepo.meshRezEnabled()))  			{ @@ -4762,27 +5024,77 @@ 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(); + +#if 1 +            LL_DEBUGS("AXONLinkset") << 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()  +                              << " playing " << (U32) (vobj->getControlAvatar() ? vobj->getControlAvatar()->mPlaying : false) +                              << " frame " << LLFrameTimer::getFrameCount() +                              << LL_ENDL; +#endif +  			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); +            // 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); + +            if (vobj->isAnimatedObject()) +            { +                if (!vobj->getControlAvatar()) +                { +                    F32 tri_count = vobj->getRootEdit()->recursiveGetEstTrianglesMax(); +                    if (tri_count <= 0.f) +                    { +                        LL_DEBUGS("AXON") << vobj_name << " not calling linkControlAvatar(), because no tris" << LL_ENDL; +                    } +                    else +                    { +                        LL_DEBUGS("AXON") << vobj_name << " calling linkControlAvatar()" << LL_ENDL; +                        vobj->linkControlAvatar(); +                    } +                } +                if (vobj->getControlAvatar()) +                { +                    rigged_av = vobj->getControlAvatar(); +                    rigged_av->rebuildAttachmentOverrides(); +                } +            } +            else +            { +                // Not animated but has a control avatar - probably +                // the checkbox has changed since the last rebuild. +                if (vobj->getControlAvatar()) +                { +                    LL_DEBUGS("AXON") << vobj_name << " calling unlinkControlAvatar()" << LL_ENDL; +                    vobj->unlinkControlAvatar(); +                } +            }  			bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic(); -			bool is_rigged = false; +			bool any_rigged_face = false; -            if (rigged && pAvatarVO) +            if (rigged && rigged_av)              { -                pAvatarVO->addAttachmentOverridesForObject(vobj); -				if (!LLApp::isExiting() && pAvatarVO->isSelf() && debugLoggingEnabled("AvatarAttachments")) +                rigged_av->addAttachmentOverridesForObject(vobj); +				if (!LLApp::isExiting() && rigged_av->isSelf() && debugLoggingEnabled("AvatarAttachments"))                  {                      bool verbose = true; -					pAvatarVO->showAttachmentOverrides(verbose); +					rigged_av->showAttachmentOverrides(verbose);  				}              } @@ -4802,7 +5114,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 @@ -4810,7 +5122,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);				 @@ -5154,7 +5466,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  				}		  			} -			if (is_rigged) +			if (any_rigged_face)  			{  				if (!drawablep->isState(LLDrawable::RIGGED))  				{ @@ -5168,6 +5480,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  			else  			{  				drawablep->clearState(LLDrawable::RIGGED); +                vobj->updateRiggedVolume();  			}  		}  	} @@ -5221,9 +5534,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; @@ -5236,11 +5549,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)  	}  	mFaceList.clear(); - -	if (pAvatarVO) -	{ -        pAvatarVO->addAttachmentArea( group->mSurfaceArea ); -	}  }  static LLTrace::BlockTimerStatHandle FTM_REBUILD_MESH_FLUSH("Flush Mesh"); @@ -5269,6 +5577,15 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)  			if (drawablep && !drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL) && !drawablep->isState(LLDrawable::RIGGED) )  			{  				LLVOVolume* vobj = drawablep->getVOVolume(); +#if 1  +                // AXON debugging +                if (vobj->isAnimatedObject() && vobj->isRiggedMesh()) +                { +                    std::string vobj_name = llformat("Vol%u", (U32) vobj); +                    F32 est_tris = vobj->getEstTrianglesMax(); +                    LL_DEBUGS("AXONLinkset") << vobj_name << " rebuildMesh, tris " << est_tris << LL_ENDL;  +        } +#endif  				vobj->preRebuild();  				if (drawablep->isState(LLDrawable::ANIMATED_CHILD)) diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index a331908320..afd6c234ee 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -62,6 +62,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. @@ -132,6 +134,7 @@ public:  	/*virtual*/	const LLMatrix4	getRenderMatrix() const;  				typedef std::map<LLUUID, S32> texture_cost_t;  				U32 	getRenderCost(texture_cost_t &textures) const; +    /*virtual*/	F32		getEstTrianglesMax() 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); } @@ -159,7 +162,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    faceMappingChanged()                    { mFaceMappingChanged=TRUE; };  	/*virtual*/ void	onShift(const LLVector4a &shift_vector); // Called when the drawable shifts @@ -259,12 +262,26 @@ 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; +    void updateAnimatedObjectStateOnReparent(LLViewerObject *old_parent, LLViewerObject *new_parent); + +	std::map<LLUUID, S32> 					mObjectSignaledAnimations; // requested state of Animation name/value +      // Functions that deal with media, or media navigation      // Update this object's media data with the given media data array @@ -298,7 +315,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 @@ -379,6 +399,7 @@ private:  public:  	static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop  	static F32 sLODFactor;				// LOD scale factor +	static S32 sForceLOD;				// LOD override  	static F32 sDistanceFactor;			// LOD distance factor  	static LLPointer<LLObjectMediaDataClient> sObjectMediaClient; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index d3be5fea1a..900c749ee0 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -3350,6 +3350,18 @@ void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags f  {  	if (drawablep && !drawablep->isDead() && assertInitialized())  	{ +#if 1 +        // AXON debugging +        LLVOVolume *vol_obj = drawablep->getVOVolume(); +        if (vol_obj && vol_obj->isAnimatedObject() && vol_obj->isRiggedMesh()) +        { +            std::string vobj_name = llformat("Vol%u", (U32) vol_obj); +            F32 est_tris = vol_obj->getEstTrianglesMax(); +            LL_DEBUGS("AXONLinkset") << vobj_name << " markRebuild, tris " << est_tris  +                                     << " priority " << (S32) priority << " flag " << std::hex << flag << LL_ENDL;  +        } +#endif +      		if (!drawablep->isState(LLDrawable::BUILT))  		{  			priority = TRUE; diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index bba36351d9..4cc4b821bf 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_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index ed3cc26851..63e2ed4bb9 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" @@ -2131,7 +2131,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" @@ -2169,13 +2169,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_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 697d27907d..f8b4948697 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2654,6 +2654,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 9af4b299de..f773ae6845 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -10622,6 +10622,38 @@ 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 size 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 size limit. +  </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 1c0317df1d..6160d9492b 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/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: | 
