diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index b4e930d0625fb33d84a08e0f2bd137e8f8715e3d..3787a25a9b20d81fc1cbea14131bf09c6ef2509d 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/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1ecd3305ed8bf807915ac95b6e252e679bcbba14
--- /dev/null
+++ b/indra/newview/llcontrolavatar.cpp
@@ -0,0 +1,119 @@
+/**
+ * @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"
+
+LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) :
+    LLVOAvatar(id, pcode, regionp),
+    mPlaying(false)
+{
+    mIsControlAvatar = true;
+}
+
+// virtual
+LLControlAvatar::~LLControlAvatar()
+{
+}
+
+void LLControlAvatar::matchTransform(LLVOVolume *obj)
+{
+	setPositionAgent(obj->getRenderPosition());
+	//slamPosition();
+
+    LLQuaternion fix_axes_rot(-F_PI_BY_TWO, LLVector3(0,0,1));
+    LLQuaternion obj_rot = obj->getRotation();
+    LLQuaternion result_rot = fix_axes_rot * obj_rot;
+	setRotation(result_rot);
+    mRoot->setWorldRotation(result_rot);
+    mRoot->setPosition(obj->getRenderPosition());
+}
+
+// Based on LLViewerJointAttachment::setupDrawable(), without the attaching part.
+void LLControlAvatar::updateGeom(LLVOVolume *obj)
+{
+	if (!obj->mDrawable)
+		return;
+	if (obj->mDrawable->isActive())
+	{
+		obj->mDrawable->makeStatic(FALSE);
+	}
+	obj->mDrawable->makeActive();
+	gPipeline.markMoved(obj->mDrawable);
+	gPipeline.markTextured(obj->mDrawable); // face may need to change draw pool to/from POOL_HUD
+	obj->mDrawable->setState(LLDrawable::USE_BACKLIGHT);
+
+	LLViewerObject::const_child_list_t& child_list = obj->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(obj->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+    obj->markForUpdate(TRUE);
+
+    matchTransform(obj);
+    //addAttachmentOverridesForObject(obj);
+}
+
+LLControlAvatar *LLControlAvatar::createControlAvatar(LLVOVolume *obj)
+{
+    // TRIF Lifted from LLPreviewAnimation
+	LLControlAvatar *cav = (LLControlAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), CO_FLAG_CONTROL_AVATAR);
+	cav->createDrawable(&gPipeline);
+	cav->mIsDummy = TRUE;
+	cav->mSpecialRenderMode = 1;
+	//cav->setPositionAgent(obj->getRenderPosition());
+	//cav->slamPosition();
+	//cav->setRotation(obj->getRotation());
+	cav->updateJointLODs();
+	cav->updateGeometry(cav->mDrawable);
+	cav->startMotion(ANIM_AGENT_STAND, 5.0f);
+	cav->hideSkirt();
+
+	// stop extraneous animations
+	cav->stopMotion( ANIM_AGENT_HEAD_ROT, TRUE );
+	cav->stopMotion( ANIM_AGENT_EYE, TRUE );
+	cav->stopMotion( ANIM_AGENT_BODY_NOISE, TRUE );
+	cav->stopMotion( ANIM_AGENT_BREATHE_ROT, TRUE );
+
+    // Sync up position/rotation with object
+    cav->matchTransform(obj);
+
+    return cav;
+}
+
diff --git a/indra/newview/llcontrolavatar.h b/indra/newview/llcontrolavatar.h
new file mode 100644
index 0000000000000000000000000000000000000000..c8b1039363bb8fdcf8f2e4999e4e2300c156189f
--- /dev/null
+++ b/indra/newview/llcontrolavatar.h
@@ -0,0 +1,50 @@
+/**
+ * @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	~LLControlAvatar();
+
+    void matchTransform(LLVOVolume *obj);
+    void updateGeom(LLVOVolume *obj);
+        
+    static LLControlAvatar *createControlAvatar(LLVOVolume *obj);
+
+    bool mPlaying;
+};
+
+#endif //LL_CONTROLAVATAR_H
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index b221221f16e7bd0d45c051405f36c631bb0e061c..fa4fd7c5333549727d2269ff0304b10f69baec2e 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -467,7 +467,8 @@ void LLDrawPoolAvatar::renderShadow(S32 pass)
 	}
 	LLVOAvatar *avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get();
 
-	if (avatarp->isDead() || avatarp->mIsDummy || avatarp->mDrawable.isNull())
+// AXON fix
+	if (avatarp->isDead() || /*avatarp->mIsDummy ||*/ avatarp->mDrawable.isNull())
 	{
 		return;
 	}
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index c68f6b8a156aa23ca19bded30c9c3b7ee2401b08..eddbe16482276d7ce5137270ad4848fd74ba2b13 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -6907,7 +6907,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 2b62ccd62fad726fb1c15372638925209f121225..7fd87f99d36f7e83da7f43360db711b68059ef2e 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"
@@ -5099,22 +5100,28 @@ void process_object_animation(LLMessageSystem *mesgsys, void **user_data)
 	S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList);
 	LL_WARNS() << "AXON handle object animation here, num_blocks " << num_blocks << LL_ENDL;
 
-	//avatarp->mSignaledAnimations.clear();
+    LLControlAvatar *avatarp = volp->mControlAvatar;
+    if (!avatarp->mPlaying)
+    {
+        avatarp->mPlaying = true;
+        avatarp->updateGeom(volp);
+    }
+	avatarp->mSignaledAnimations.clear();
     volp->setDebugText(llformat("Animations %d", num_blocks));
 	
     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);
-        //avatarp->mSignaledAnimations[animation_id] = anim_sequence_id;
+        avatarp->mSignaledAnimations[animation_id] = anim_sequence_id;
         LL_INFOS() << "AXON got object animation request for object " 
                    << uuid << " animation id " << animation_id << LL_ENDL;
     }
 
-	if (num_blocks)
+	if (num_blocks >= 0)
 	{
         LL_INFOS() << "AXON process animation state changes here" << LL_ENDL;
-		//avatarp->processAnimationStateChanges();
+		avatarp->processAnimationStateChanges();
 	}
 }
 
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 030c0ca9f6dfbf9bebfa6d079a9d8eb27e8dd9b9..ff808ba0797774a3f393fbbf1149f10e56208114 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),
@@ -369,11 +377,18 @@ void LLViewerObject::markDead()
 			((LLViewerObject *)getParent())->removeChild(this);
 		}
 		LLUUID mesh_id;
+        // FIXME AXON - need to do this for control avatars too
 		if (av && LLVOAvatar::getRiggedMeshID(this,mesh_id))
 		{
 			// This case is needed for indirectly attached mesh objects.
 			av->resetJointsOnDetach(mesh_id);
 		}
+        if (mControlAvatar)
+        {
+            LLControlAvatar *av = mControlAvatar;
+            mControlAvatar = NULL;
+            av->markDead();
+        }
 
 		// Mark itself as dead
 		mDead = TRUE;
@@ -1378,7 +1393,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 +1663,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 
 				U8 state;
 				mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num );
-				mState = state;
+				mAttachmentState = state;
 				break;
 			}
 
@@ -1671,7 +1686,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 		U8		state;
 
 		dp->unpackU8(state, "State");
-		mState = state;
+		mAttachmentState = state;
 
 		switch(update_type)
 		{
@@ -3857,7 +3872,7 @@ const LLVector3 LLViewerObject::getRenderPosition() const
 	if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED))
 	{
 		LLVOAvatar* avatar = getAvatar();
-		if (avatar)
+		if (avatar && !mControlAvatar)
 		{
 			return avatar->getPositionAgent();
 		}
@@ -3993,6 +4008,10 @@ void LLViewerObject::setPosition(const LLVector3 &pos, BOOL damped)
 		// position caches need to be up to date on root objects
 		updatePositionCaches();
 	}
+    if (mControlAvatar)
+    {
+        mControlAvatar->matchTransform(dynamic_cast<LLVOVolume*>(this));
+    }
 }
 
 void LLViewerObject::setPositionGlobal(const LLVector3d &pos_global, BOOL damped)
@@ -6358,6 +6377,10 @@ const std::string& LLViewerObject::getAttachmentItemName() const
 //virtual
 LLVOAvatar* LLViewerObject::getAvatar() const
 {
+    if (mControlAvatar)
+    {
+        return mControlAvatar;
+    }
 	if (isAttachment())
 	{
 		LLViewerObject* vobj = (LLViewerObject*) getParent();
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 24fcf0517ec8f0975f8819ca6d044f88c0979e8c..e727567957a31c4be5f7d387630c0f0ed9dfa393 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;
@@ -374,7 +375,7 @@ class LLViewerObject
 
 	void sendShapeUpdate();
 
-	U8 getState()							{ return mState; }
+	U8 getAttachmentState()							{ return mAttachmentState; }
 
 	F32 getAppAngle() const					{ return mAppAngle; }
 	F32 getPixelArea() const				{ return mPixelArea; }
@@ -683,6 +684,8 @@ class LLViewerObject
 
 	static			BOOL		sUseSharedDrawables;
 
+    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 +696,10 @@ class LLViewerObject
 	// 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 +787,7 @@ class LLViewerObject
 	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 8f98d66c0c334e9acd37ed12897fcf7580426ca1..1f99119d8271e65d394d4bcf7745bf4dc4a5ee66 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -1942,12 +1942,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 94c751acc66b3d40b84626b60099a779d60edc08..72b2b99004435675e0ded9d1f8c4baa8318db866 100644
--- a/indra/newview/llviewerobjectlist.h
+++ b/indra/newview/llviewerobjectlist.h
@@ -67,7 +67,7 @@ class LLViewerObjectList
 	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/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 3968266c277bf9abd4f9d8e45126dab2a41ea6a1..7ba264f35ad6b5f5bb6e8f89607ba1db1219c7ac 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"
@@ -663,7 +664,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mLastUpdateRequestCOFVersion(-1),
 	mLastUpdateReceivedCOFVersion(-1),
 	mCachedMuteListUpdateTime(0),
-	mCachedInMuteList(false)
+	mCachedInMuteList(false),
+    mIsControlAvatar(false)
 {
 	LL_DEBUGS("AvatarRender") << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << LL_ENDL;
 
@@ -1942,7 +1944,7 @@ void LLVOAvatar::resetSkeleton(bool reset_animations)
 //-----------------------------------------------------------------------------
 void LLVOAvatar::releaseMeshData()
 {
-	if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || mIsDummy)
+	if (sInstances.size() < AVATAR_RELEASE_THRESHOLD)// || mIsDummy)
 	{
 		return;
 	}
@@ -2738,7 +2740,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
+            // TRIF skip cloud effects for dummy avs as well
+			if (!mIsDummy && !isTooComplex()) // do not generate particles for overly-complex avatars
 			{
 				setParticleSource(particle_parameters, getID());
 			}
@@ -3530,7 +3533,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 	}
 
 	// change animation time quanta based on avatar render load
-	if (!isSelf() && !mIsDummy)
+	if (!isSelf())// && !mIsDummy)
 	{
 		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);
@@ -3652,7 +3655,8 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 		//--------------------------------------------------------------------
 		// Propagate viewer object rotation to root of avatar
 		//--------------------------------------------------------------------
-		if (!isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS))
+        // FIXME TRIF - just skipping this for now for all dummy avs
+		if (!mIsDummy && !isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS))
 		{
 			LLQuaternion iQ;
 			LLVector3 upDir( 0.0f, 0.0f, 1.0f );
@@ -4041,7 +4045,7 @@ void LLVOAvatar::updateVisibility()
 
 	if (mIsDummy)
 	{
-		visible = TRUE;
+		visible = FALSE;
 	}
 	else if (mDrawable.isNull())
 	{
@@ -4356,7 +4360,7 @@ U32 LLVOAvatar::renderSkinned()
 		{
 			if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender)
 			{
-				if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy)
+				if (isTextureVisible(TEX_HEAD_BAKED))// || mIsDummy)
 				{
 					LLViewerJoint* head_mesh = getViewerJoint(MESH_ID_HEAD);
 					if (head_mesh)
@@ -4366,7 +4370,7 @@ U32 LLVOAvatar::renderSkinned()
 					first_pass = FALSE;
 				}
 			}
-			if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy)
+			if (isTextureVisible(TEX_UPPER_BAKED))// || mIsDummy)
 			{
 				LLViewerJoint* upper_mesh = getViewerJoint(MESH_ID_UPPER_BODY);
 				if (upper_mesh)
@@ -4376,7 +4380,7 @@ U32 LLVOAvatar::renderSkinned()
 				first_pass = FALSE;
 			}
 			
-			if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy)
+			if (isTextureVisible(TEX_LOWER_BAKED))// || mIsDummy)
 			{
 				LLViewerJoint* lower_mesh = getViewerJoint(MESH_ID_LOWER_BODY);
 				if (lower_mesh)
@@ -4435,6 +4439,8 @@ U32 LLVOAvatar::renderTransparent(BOOL first_pass)
 		}
 		// Can't test for baked hair being defined, since that won't always be the case (not all viewers send baked hair)
 		// TODO: 1.25 will be able to switch this logic back to calling isTextureVisible();
+        if (!mIsDummy)
+        {
 		if ( (getImage(TEX_HAIR_BAKED, 0) && getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE)
 			|| LLDrawPoolAlpha::sShowDebugAlpha)		
 		{
@@ -4445,6 +4451,7 @@ U32 LLVOAvatar::renderTransparent(BOOL first_pass)
 			}
 			first_pass = FALSE;
 		}
+        }
 		if (LLPipeline::sImpostorRender)
 		{
 			gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
@@ -5521,12 +5528,26 @@ void LLVOAvatar::rebuildAttachmentOverrides()
 //-----------------------------------------------------------------------------
 void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)
 {
-	LLVOAvatar *av = vo->getAvatarAncestor();
+    bool non_attached_case = false;
+    // FIXME TRIF - will this work if vo has child objects?
+    if (vo->mControlAvatar)
+    {
+        non_attached_case = true;
+    }
+    LLVOAvatar *av;
+    if (non_attached_case)
+    {
+        av = vo->mControlAvatar;
+    }
+    else
+    {
+        av = vo->getAvatarAncestor();
 	if (!av || (av != this))
 	{
 		LL_WARNS("Avatar") << "called with invalid avatar" << LL_ENDL;
         return;
 	}
+    }
 
     LLScopedContextString str("addAttachmentOverridesForObject " + av->getFullname());
     
@@ -5554,7 +5575,7 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)
 	LLUUID currentId = vobj->getVolume()->getParams().getSculptID();						
 	const LLMeshSkinInfo*  pSkinData = gMeshRepo.getSkinInfo( currentId, vobj );
 
-	if ( vobj && vobj->isAttachment() && vobj->isMesh() && pSkinData )
+	if ( vobj && (vobj->isAttachment()||non_attached_case) && vobj->isMesh() && pSkinData )
 	{
 		const int bindCnt = pSkinData->mAlternateBindMatrix.size();								
         const int jointCnt = pSkinData->mJointNames.size();
@@ -5738,6 +5759,7 @@ void LLVOAvatar::showAttachmentOverrides(bool verbose) const
 //-----------------------------------------------------------------------------
 // resetJointsOnDetach
 //-----------------------------------------------------------------------------
+// TRIF handle NPC case
 void LLVOAvatar::resetJointsOnDetach(LLViewerObject *vo)
 {
 	LLVOAvatar *av = vo->getAvatarAncestor();
@@ -5766,6 +5788,7 @@ void LLVOAvatar::resetJointsOnDetach(LLViewerObject *vo)
 //-----------------------------------------------------------------------------
 // resetJointsOnDetach
 //-----------------------------------------------------------------------------
+// TRIF handle NPC case
 void LLVOAvatar::resetJointsOnDetach(const LLUUID& mesh_id)
 {	
 	//Subsequent joints are relative to pelvis
@@ -6288,7 +6311,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.
@@ -6812,6 +6835,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)
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index bd89d4ef23ab7b8bd2cad23d0a5546418deb6dc8..2832f940f41b00b0930b2069acf1cd053220cc9f 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -233,6 +233,8 @@ class LLVOAvatar :
 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]);
 
@@ -440,6 +442,12 @@ class LLVOAvatar :
 
 	VisualMuteSettings		mVisuallyMuteSetting;			// Always or never visually mute this AV
 
+	//--------------------------------------------------------------------
+	// NPC status
+	//--------------------------------------------------------------------
+public:
+    bool mIsControlAvatar;
+
 	//--------------------------------------------------------------------
 	// Morph masks
 	//--------------------------------------------------------------------
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index aa5d82a096681b97ced6e22d9a8bbbff8948a33b..2137bf4eb3dfbb6b7d020996a83656f58b58abce 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -2789,7 +2789,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 de63a3963cdcd78b905e0af861137af1c5f69a47..e7e4e6f2286a547c61b07b048c72cffca59f8350 100644
--- a/indra/newview/llvograss.cpp
+++ b/indra/newview/llvograss.cpp
@@ -92,7 +92,8 @@ LLVOGrass::~LLVOGrass()
 
 void LLVOGrass::updateSpecies()
 {
-	mSpecies = mState;
+	// TRIF is grass still even supported? This use of state seems odd.
+	mSpecies = getAttachmentState();
 	
 	if (!sSpeciesTable.count(mSpecies))
 	{
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index ed0205704f085b5f87401a9177107514f1175ec4..a2f417a5cca0e98222db46ad030d82997ebc004e 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;
@@ -3383,7 +3388,7 @@ void LLVOVolume::updateRadius()
 
 BOOL LLVOVolume::isAttachment() const
 {
-	return mState != 0 ;
+	return mAttachmentState != 0 ;
 }
 
 BOOL LLVOVolume::isHUDAttachment() const
@@ -3391,7 +3396,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 );
 }
 
@@ -4825,10 +4830,31 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
                           vobj->isMesh() && 
 						  gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID(), vobj);
 
+            bool rigged_non_attachment = !vobj->isAttachment() &&
+                                         vobj->isMesh() && 
+                                         gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID(), vobj);
+
+            if (rigged_non_attachment)
+            {
+                if (!vobj->mControlAvatar)
+                {
+                    vobj->mControlAvatar = LLControlAvatar::createControlAvatar(vobj);
+                    vobj->mControlAvatar->addAttachmentOverridesForObject(vobj);
+                    vobj->requestInventory();
+                }
+                if (vobj->mControlAvatar)
+                {
+                    pAvatarVO = vobj->mControlAvatar;
+                }
+            }
+
 			bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic();
 
+            // TRIF why this variable? Only different from rigged if
+            // there are no LLFaces associated with the drawable.
 			bool is_rigged = false;
 
+            // TRIF handle NPC case
             if (rigged && pAvatarVO)
             {
                 pAvatarVO->addAttachmentOverridesForObject(vobj);
@@ -4855,7 +4881,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 				//sum up face verts and indices
 				drawablep->updateFaceSize(i);
 			
-				if (rigged) 
+				if (rigged || (vobj->mControlAvatar && vobj->mControlAvatar->mPlaying))
 				{
 					if (!facep->isState(LLFace::RIGGED))
 					{ //completely reset vertex buffer