diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 42c329721041a06b6239979651fabf789a79237a..b7ed4fc0a48ad694209640ecfe961d11e4ca65ed 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -96,7 +96,6 @@ set(viewer_SOURCE_FILES
     llbottomtray.cpp
     llbox.cpp
     llbreadcrumbview.cpp
-    llbreastmotion.cpp
     llbrowsernotification.cpp
     llbuycurrencyhtml.cpp
     llcallbacklist.cpp
@@ -379,6 +378,7 @@ set(viewer_SOURCE_FILES
     llparcelselection.cpp
     llparticipantlist.cpp
     llpatchvertexarray.cpp
+    llphysicsmotion.cpp
     llplacesinventorybridge.cpp
     llplacesinventorypanel.cpp
     llpopupview.cpp
@@ -636,7 +636,6 @@ set(viewer_HEADER_FILES
     llbottomtray.h
     llbox.h
     llbreadcrumbview.h
-    llbreastmotion.h
     llbuycurrencyhtml.h
     llcallbacklist.h
     llcallfloater.h
@@ -914,6 +913,7 @@ set(viewer_HEADER_FILES
     llparcelselection.h
     llparticipantlist.h
     llpatchvertexarray.h
+    llphysicsmotion.h
     llplacesinventorybridge.h
     llplacesinventorypanel.h
     llpolymesh.h
diff --git a/indra/newview/character/avatar_lad.xml b/indra/newview/character/avatar_lad.xml
index 453ed1baf77df6afd26f651aa68590fef7c5d42d..c4117da893980194cc2ab1829982a348443d31a4 100644
--- a/indra/newview/character/avatar_lad.xml
+++ b/indra/newview/character/avatar_lad.xml
@@ -3794,6 +3794,23 @@
       </param_morph>
     </param>
 
+    <param
+     id="1091"
+     group="0"
+     name="Belly_Gravity"
+     label="Belly Gravity"
+     wearable="shape"
+     edit_group="driven"
+     label_min="Up"
+     label_max="Down"
+     value_default="0"
+     value_min="-2"
+     value_max="2"
+     camera_elevation=".3"
+     camera_distance=".8">
+      <param_morph />
+    </param>
+
     <param
      id="626"
      sex="female"
@@ -4426,6 +4443,23 @@
       </param_morph>
     </param>
 
+    <param
+     id="1089"
+     group="0"
+     name="Butt_Gravity"
+     label="Butt Gravity"
+     wearable="shape"
+     edit_group="driven"
+     label_min="Separate"
+     label_max="Join"
+     value_default="0"
+     value_min="-2"
+     value_max="2"
+     camera_elevation=".3"
+     camera_distance=".8">
+      <param_morph />
+    </param>
+
     <param
      id="152"
      group="1"
@@ -9352,6 +9386,48 @@ render_pass="bump">
 	</param_driver>
     </param>
 
+   <param
+     id="1090"
+     group="0"
+     name="Butt_Gravity_Driver"
+     label="Butt Gravity"
+     wearable="shape"
+     edit_group="shape_legs"
+     edit_group_order="7"
+     label_min="Less"
+     label_max="More"
+     value_default="0"
+     value_min="-2"
+     value_max="2"
+     camera_elevation=".3"
+     camera_distance=".8">
+      <param_driver>
+        <driven
+         id="1089" />
+	</param_driver>
+    </param>
+
+   <param
+     id="1092"
+     group="0"
+     name="Belly_Gravity_Driver"
+     label="Belly Gravity"
+     wearable="shape"
+     edit_group="shape_torso"
+     edit_group_order="14"
+     label_min="Less"
+     label_max="More"
+     value_default="0"
+     value_min="-2"
+     value_max="2"
+     camera_elevation=".3"
+     camera_distance=".8">
+      <param_driver>
+        <driven
+         id="1090" />
+	</param_driver>
+    </param>
+
     <param
      id="828"
      group="0"
diff --git a/indra/newview/llphysicsmotion.cpp b/indra/newview/llphysicsmotion.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9043894009ac8d0e9eb9e30adc24430d79692bc5
--- /dev/null
+++ b/indra/newview/llphysicsmotion.cpp
@@ -0,0 +1,559 @@
+/** 
+ * @file llphysicsmotion.cpp
+ * @brief Implementation of LLPhysicsMotion class.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ * 
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+#include "linden_common.h"
+
+#include "m3math.h"
+#include "v3dmath.h"
+
+#include "llphysicsmotion.h"
+#include "llcharacter.h"
+#include "llviewercontrol.h"
+#include "llviewervisualparam.h"
+#include "llvoavatarself.h"
+
+typedef std::map<std::string, std::string> controller_map_t;
+
+#define MIN_REQUIRED_PIXEL_AREA_BREAST_MOTION 0.f;
+
+inline F64 llsgn(const F64 a)
+{
+	if (a >= 0)
+		return 1;
+	return -1;
+}
+
+class LLPhysicsMotion
+{
+public:
+	LLPhysicsMotion(const std::string &param_user_name,
+			const std::string &param_driven_name,
+			const std::string &joint_name,
+			LLCharacter *character,
+			const LLVector3 &motion_direction_vec,
+			const controller_map_t &controllers) :
+		mParamUserName(param_user_name),
+		mParamDrivenName(param_driven_name),
+		mJointName(joint_name),
+		mMotionDirectionVec(motion_direction_vec),
+		mParamUser(NULL),
+		mParamDriven(NULL),
+
+		mParamControllers(controllers),
+		mCharacter(character),
+		mLastTime(0),
+		mPosition_local(0),
+		mVelocityJoint_local(0),
+		mPositionLastUpdate_local(0),
+		mPositionMin_local(0),
+		mPositionMax_local(0)
+	{
+		mJointState = new LLJointState;
+	}
+
+	BOOL initialize();
+
+	~LLPhysicsMotion() {}
+
+	BOOL onUpdate(F32 time);
+
+	LLPointer<LLJointState> getJointState() 
+	{
+		return mJointState;
+	}
+protected:
+	F32 getParamValue(const std::string& controller_key)
+	{
+		const controller_map_t::const_iterator& entry = mParamControllers.find(controller_key);
+		if (entry == mParamControllers.end())
+		{
+			return 1.0;
+		}
+		const std::string& param_name = (*entry).second.c_str();
+		return mCharacter->getVisualParamWeight(param_name.c_str());
+	}
+
+	F32 toLocal(const LLVector3 &world);
+	F32 calculateVelocity_local(const F32 time_delta);
+	F32 calculateAcceleration_local(F32 velocity_local,
+					const F32 time_delta);
+	
+private:
+	const std::string mParamDrivenName;
+	const std::string mParamUserName;
+	const LLVector3 mMotionDirectionVec;
+	const std::string mJointName;
+
+	F32 mPosition_local;
+	F32 mVelocityJoint_local; // How fast the joint is moving
+	F32 mAccelerationJoint_local; // Acceleration on the joint
+
+	F32 mVelocity_local; // How fast the param is moving
+	F32 mPositionLastUpdate_local;
+	F32 mPositionMin_local;
+	F32 mPositionMax_local;
+	LLVector3 mPosition_world;
+
+	LLViewerVisualParam *mParamUser;
+	LLViewerVisualParam *mParamDriven;
+	const controller_map_t mParamControllers;
+	
+	LLPointer<LLJointState> mJointState;
+	LLCharacter *mCharacter;
+
+	F32 mLastTime;
+				  
+};
+
+
+
+BOOL LLPhysicsMotion::initialize()
+{
+	if (!mJointState->setJoint(mCharacter->getJoint(mJointName.c_str())))
+		return FALSE;
+	mJointState->setUsage(LLJointState::ROT);
+
+	mParamUser = (LLViewerVisualParam*)mCharacter->getVisualParam(mParamUserName.c_str());
+	mParamDriven = (LLViewerVisualParam*)mCharacter->getVisualParam(mParamDrivenName.c_str());
+	if ((mParamUser == NULL) || 
+	    (mParamDriven == NULL))
+	{
+		llinfos << "Failure reading in either of both of [ " << mParamUserName << " : " << mParamDrivenName << " ]" << llendl;
+		return FALSE;
+	}
+	mPositionMin_local = mParamDriven->getMinWeight();
+	mPositionMax_local = mParamDriven->getMaxWeight();
+
+	return TRUE;
+}
+
+LLPhysicsMotionController::LLPhysicsMotionController(const LLUUID &id) : 
+	LLMotion(id),
+	mCharacter(NULL)
+{
+	mName = "breast_motion";
+}
+
+LLPhysicsMotionController::~LLPhysicsMotionController()
+{
+	for (motion_vec_t::iterator iter = mMotions.begin();
+	     iter != mMotions.end();
+	     ++iter)
+	{
+		delete (*iter);
+	}
+}
+
+BOOL LLPhysicsMotionController::onActivate() 
+{ 
+	return TRUE; 
+}
+
+void LLPhysicsMotionController::onDeactivate() 
+{
+}
+
+LLMotion::LLMotionInitStatus LLPhysicsMotionController::onInitialize(LLCharacter *character)
+{
+	mCharacter = character;
+
+	mMotions.clear();
+
+	controller_map_t controllers_cleavage;
+	controllers_cleavage["Mass"] = "Breast_Physics_Mass";
+	controllers_cleavage["Smoothing"] = "Breast_Physics_Smoothing";
+	controllers_cleavage["Gravity"] = "Breast_Physics_Gravity";
+	controllers_cleavage["Damping"] = "Breast_Physics_Side_Damping";
+	controllers_cleavage["Drag"] = "Breast_Physics_Side_Drag";
+	controllers_cleavage["MaxSpeed"] = "Breast_Physics_Side_Max_Velocity";
+	controllers_cleavage["Spring"] = "Breast_Physics_Side_Spring";
+	controllers_cleavage["Gain"] = "Breast_Physics_Side_Gain";
+
+	LLPhysicsMotion *cleavage_motion = new LLPhysicsMotion("Breast_Female_Cleavage_Driver",
+							       "Breast_Female_Cleavage",
+							       "mChest",
+							       character,
+							       LLVector3(-1,0,0),
+							       controllers_cleavage);
+	if (!cleavage_motion->initialize())
+		return STATUS_FAILURE;
+	addMotion(cleavage_motion);
+
+	controller_map_t controllers_bounce;
+	controllers_bounce["Mass"] = "Breast_Physics_Mass";
+	controllers_bounce["Smoothing"] = "Breast_Physics_Smoothing";
+	controllers_bounce["Gravity"] = "Breast_Physics_Gravity";
+	controllers_bounce["Damping"] = "Breast_Physics_UpDown_Damping";
+	controllers_bounce["Drag"] = "Breast_Physics_UpDown_Drag";
+	controllers_bounce["MaxSpeed"] = "Breast_Physics_UpDown_Max_Velocity";
+	controllers_bounce["Spring"] = "Breast_Physics_UpDown_Spring";
+	controllers_bounce["Gain"] = "Breast_Physics_UpDown_Gain";
+
+	LLPhysicsMotion *bounce_motion = new LLPhysicsMotion("Breast_Gravity_Driver",
+							     "Breast_Gravity",
+							     "mChest",
+							     character,
+							     LLVector3(0,0,1),
+							     controllers_bounce);
+	if (!bounce_motion->initialize())
+		return STATUS_FAILURE;
+	addMotion(bounce_motion);
+
+	controller_map_t controllers_butt_bounce;
+	controllers_butt_bounce["Mass"] = "Breast_Physics_Mass";
+	controllers_butt_bounce["Smoothing"] = "Breast_Physics_Smoothing";
+	controllers_butt_bounce["Gravity"] = "Breast_Physics_Gravity";
+	controllers_butt_bounce["Damping"] = "Breast_Physics_Side_Damping";
+	controllers_butt_bounce["Drag"] = "Breast_Physics_Side_Drag";
+	controllers_butt_bounce["MaxSpeed"] = "Breast_Physics_Side_Max_Velocity";
+	controllers_butt_bounce["Spring"] = "Breast_Physics_Side_Spring";
+	controllers_butt_bounce["Gain"] = "Breast_Physics_Side_Gain";
+
+	LLPhysicsMotion *butt_bounce_motion = new LLPhysicsMotion("Butt_Gravity_Driver",
+								  "Butt_Gravity",
+								  "mPelvis",
+								  character,
+								  LLVector3(0,0,-1),
+								  controllers_butt_bounce);
+	if (!butt_bounce_motion->initialize())
+		return STATUS_FAILURE;
+	addMotion(butt_bounce_motion);
+
+	controller_map_t controllers_belly_bounce;
+	controllers_belly_bounce["Mass"] = "Breast_Physics_Mass";
+	controllers_belly_bounce["Smoothing"] = "Breast_Physics_Smoothing";
+	controllers_belly_bounce["Gravity"] = "Breast_Physics_Gravity";
+	controllers_belly_bounce["Damping"] = "Breast_Physics_UpDown_Damping";
+	controllers_belly_bounce["Drag"] = "Breast_Physics_UpDown_Drag";
+	controllers_belly_bounce["MaxSpeed"] = "Breast_Physics_UpDown_Max_Velocity";
+	controllers_belly_bounce["Spring"] = "Breast_Physics_UpDown_Spring";
+	controllers_belly_bounce["Gain"] = "Breast_Physics_UpDown_Gain";
+
+	LLPhysicsMotion *belly_bounce_motion = new LLPhysicsMotion("Big_Belly_Torso",
+								  "Belly Size",
+								  "mChest",
+								  character,
+								   LLVector3(-0.005f,0,0),
+								  controllers_belly_bounce);
+	if (!belly_bounce_motion->initialize())
+		return STATUS_FAILURE;
+	addMotion(belly_bounce_motion);
+
+	return STATUS_SUCCESS;
+}
+
+void LLPhysicsMotionController::addMotion(LLPhysicsMotion *motion)
+{
+	addJointState(motion->getJointState());
+	mMotions.push_back(motion);
+}
+
+F32 LLPhysicsMotionController::getMinPixelArea() 
+{
+	return MIN_REQUIRED_PIXEL_AREA_BREAST_MOTION;
+}
+
+// Local space means "parameter space".
+F32 LLPhysicsMotion::toLocal(const LLVector3 &world)
+{
+	LLJoint *joint = mJointState->getJoint();
+	const LLQuaternion rotation_world = joint->getWorldRotation();
+	
+	LLVector3 dir_world = mMotionDirectionVec * rotation_world;
+	dir_world.normalize();
+	return world * dir_world * mMotionDirectionVec.length(); // dot product
+}
+
+F32 LLPhysicsMotion::calculateVelocity_local(const F32 time_delta)
+{
+	LLJoint *joint = mJointState->getJoint();
+	const LLVector3 position_world = joint->getWorldPosition();
+	const LLQuaternion rotation_world = joint->getWorldRotation();
+	const LLVector3 last_position_world = mPosition_world;
+	const LLVector3 velocity_world = (position_world-last_position_world) / time_delta;
+	const F32 velocity_local = toLocal(velocity_world);
+	return velocity_local;
+}
+
+F32 LLPhysicsMotion::calculateAcceleration_local(const F32 velocity_local,
+						 const F32 time_delta)
+{
+	const F32 smoothing = getParamValue("Smoothing");
+	const F32 acceleration_local = velocity_local - mVelocityJoint_local;
+	
+	const F32 smoothed_acceleration_local = 
+		acceleration_local * 1.0/smoothing + 
+		mAccelerationJoint_local * (smoothing-1.0)/smoothing;
+	
+	return smoothed_acceleration_local;
+}
+
+BOOL LLPhysicsMotionController::onUpdate(F32 time, U8* joint_mask)
+{
+	// Skip if disabled globally.
+	if (!gSavedSettings.getBOOL("AvatarPhysics"))
+	{
+		return TRUE;
+	}
+	
+	if (mCharacter->getSex() != SEX_FEMALE) return TRUE;
+	
+	BOOL update_visuals = FALSE;
+	for (motion_vec_t::iterator iter = mMotions.begin();
+	     iter != mMotions.end();
+	     ++iter)
+	{
+		LLPhysicsMotion *motion = (*iter);
+		update_visuals |= motion->onUpdate(time);
+	}
+		
+	if (update_visuals)
+		mCharacter->updateVisualParams();
+	
+	return TRUE;
+}
+
+
+// Return TRUE if character has to update visual params.
+BOOL LLPhysicsMotion::onUpdate(F32 time)
+{
+	// static FILE *mFileWrite = fopen("c:\\temp\\avatar_data.txt","w");
+	
+	if (!mParamUser || !mParamDriven)
+		return FALSE;
+
+	if (!mLastTime)
+	{
+		mLastTime = time;
+		return FALSE;
+	}
+
+	////////////////////////////////////////////////////////////////////////////////
+	// Get all parameters and settings
+	//
+
+	const F32 time_delta = time - mLastTime;
+	if (time_delta > 3.0 || time_delta <= 0.01)
+	{
+		mLastTime = time;
+		return FALSE;
+	}
+
+	// Higher LOD is better.  This controls the granularity
+	// and frequency of updates for the motions.
+	const F32 lod_factor = LLVOAvatar::sPhysicsLODFactor;
+	if (lod_factor == 0)
+	{
+		return TRUE;
+	}
+
+	F32 behavior_mass = getParamValue("Mass");
+	F32 behavior_gravity = getParamValue("Gravity");
+	F32 behavior_spring = getParamValue("Spring");
+	F32 behavior_gain = getParamValue("Gain");
+	F32 behavior_damping = getParamValue("Damping");
+	F32 behavior_maxspeed = getParamValue("MaxSpeed");
+	F32 behavior_drag = getParamValue("Drag");
+
+	F32 position_user_local = mParamUser->getWeight();
+	F32 position_current_local = mPosition_local;
+
+	//
+	// End parameters and settings
+	////////////////////////////////////////////////////////////////////////////////
+
+
+	////////////////////////////////////////////////////////////////////////////////
+	// Calculate velocity and acceleration in parameter space.
+	//
+	
+	const F32 velocity_joint_local = calculateVelocity_local(time_delta);
+	const F32 acceleration_joint_local = calculateAcceleration_local(velocity_joint_local, time_delta);
+
+	LLJoint *joint = mJointState->getJoint();
+
+	//
+	// End velocity and acceleration
+	////////////////////////////////////////////////////////////////////////////////
+
+
+	////////////////////////////////////////////////////////////////////////////////
+	// Calculate the total force 
+	//
+
+	// Spring force is a restoring force towards the original user-set breast position.
+	// F = kx
+	const F32 spring_length = position_current_local - position_user_local;
+	const F32 force_spring = -spring_length * behavior_spring;
+
+	// Acceleration is the force that comes from the change in velocity of the torso.
+	// F = ma
+	const F32 force_accel = behavior_gain * (acceleration_joint_local * behavior_mass);
+
+	// Gravity always points downward in world space.
+	// F = mg
+	const LLVector3 gravity_world(0,0,1);
+	const F32 force_gravity = behavior_gain * (toLocal(gravity_world) * behavior_gravity * behavior_mass);
+		
+	// Damping is a restoring force that opposes the current velocity.
+	// F = -kv
+	const F32 force_damping = -behavior_damping * mVelocity_local;
+		
+	// Drag is a force imparted by velocity (intuitively it is similar to wind resistance)
+	// F = .5kv^2
+	const F32 force_drag = .5*behavior_drag*velocity_joint_local*velocity_joint_local*llsgn(velocity_joint_local);
+
+	const F32 force_net = (force_accel + 
+			       force_gravity +
+			       force_spring + 
+			       force_damping + 
+			       force_drag);
+
+	//
+	// End total force
+	////////////////////////////////////////////////////////////////////////////////
+
+	
+	////////////////////////////////////////////////////////////////////////////////
+	// Calculate new params
+	//
+
+	// Calculate the new acceleration based on the net force.
+	// a = F/m
+	const F32 acceleration_new_local = force_net / behavior_mass;
+	F32 velocity_new_local = mVelocity_local + acceleration_new_local;
+	velocity_new_local = llclamp(velocity_new_local, 
+				     -behavior_maxspeed*100.0f, behavior_maxspeed*100.0f);
+	
+	// Temporary debugging setting to cause all avatars to move, for profiling purposes.
+	if (gSavedSettings.getBOOL("AvatarPhysicsTest"))
+	{
+		velocity_new_local = sin(time*4.0)*5.0;
+	}
+	// Calculate the new parameters and clamp them to the min/max ranges.
+	F32 position_new_local = position_current_local + velocity_new_local*time_delta;
+	position_new_local = llclamp(position_new_local,
+				     mPositionMin_local, mPositionMax_local);
+	
+	// Set the new parameters.
+	// If the param is disabled, just set the param to the user value.
+	if (behavior_maxspeed == 0)
+	{
+		position_new_local = position_user_local;
+	}
+	mCharacter->setVisualParamWeight(mParamDriven,
+					 position_new_local,
+					 FALSE);
+		
+	//
+	// End calculate new params
+	////////////////////////////////////////////////////////////////////////////////
+	
+	////////////////////////////////////////////////////////////////////////////////
+	// Conditionally update the visual params
+	//
+		
+	// Updating the visual params (i.e. what the user sees) is fairly expensive.
+	// So only update if the params have changed enough, and also take into account
+	// the graphics LOD settings.
+	
+	BOOL update_visuals = FALSE;
+
+	// For non-self, if the avatar is small enough visually, then don't update.
+	const F32 area_for_max_settings = 0.0;
+	const F32 area_for_min_settings = 1400.0;
+	const F32 area_for_this_setting = area_for_max_settings + (area_for_min_settings-area_for_max_settings)*(1.0-lod_factor);
+	const F32 pixel_area = fsqrtf(mCharacter->getPixelArea());
+
+	const BOOL is_self = (dynamic_cast<LLVOAvatarSelf *>(mCharacter) != NULL);
+	if ((pixel_area > area_for_this_setting) || is_self)
+	{
+		// If the parameter hasn't changed enough, then don't update.
+		const F32 position_diff_local = llabs(mPositionLastUpdate_local-position_new_local);
+		const F32 min_delta = (1.0-lod_factor)*(mPositionMax_local-mPositionMin_local)/2.0;
+		if (llabs(position_diff_local) > min_delta)
+		{
+			update_visuals = TRUE;
+			mPositionLastUpdate_local = position_new_local;
+		}
+	}
+
+	update_visuals = TRUE;
+	mPositionLastUpdate_local = position_new_local;
+
+	//
+	// End update visual params
+	////////////////////////////////////////////////////////////////////////////////
+
+	mVelocityJoint_local = velocity_joint_local;
+
+	mVelocity_local = velocity_new_local;
+	mAccelerationJoint_local = acceleration_joint_local;
+	mPosition_local = position_new_local;
+
+	mPosition_world = joint->getWorldPosition();
+	mLastTime = time;
+
+	/*
+	  if (mFileWrite != NULL && is_self)
+	  {
+	  fprintf(mFileWrite,"%f\t%f\t%f \t\t%f \t\t%f\t%f\t%f\t \t\t%f\t%f\t%f\t%f\t%f \t\t%f\t%f\t%f\n",
+	  position_new_local,
+	  velocity_new_local,
+	  acceleration_new_local,
+
+	  time_delta,
+
+	  mPosition_world[0],
+	  mPosition_world[1],
+	  mPosition_world[2],
+
+	  force_net,
+	  force_spring,
+	  force_accel,
+	  force_damping,
+	  force_drag,
+
+	  spring_length,
+	  velocity_joint_local,
+	  acceleration_joint_local
+	  );
+	  }
+	*/
+
+	return update_visuals;
+}
+	
diff --git a/indra/newview/llphysicsmotion.h b/indra/newview/llphysicsmotion.h
new file mode 100644
index 0000000000000000000000000000000000000000..0c0087d269119589b60ddad4e8731bfb193a760a
--- /dev/null
+++ b/indra/newview/llphysicsmotion.h
@@ -0,0 +1,124 @@
+/** 
+ * @file llphysicsmotion.h
+ * @brief Implementation of LLPhysicsMotion class.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ * 
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPHYSICSMOTIONCONTROLLER_H
+#define LL_LLPHYSICSMOTIONCONTROLLER_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+#include "llframetimer.h"
+
+#define PHYSICS_MOTION_FADEIN_TIME 1.0f
+#define PHYSICS_MOTION_FADEOUT_TIME 1.0f
+
+class LLPhysicsMotion;
+
+//-----------------------------------------------------------------------------
+// class LLPhysicsMotion
+//-----------------------------------------------------------------------------
+class LLPhysicsMotionController :
+	public LLMotion
+{
+public:
+	// Constructor
+	LLPhysicsMotionController(const LLUUID &id);
+
+	// Destructor
+	virtual ~LLPhysicsMotionController();
+
+public:
+	//-------------------------------------------------------------------------
+	// functions to support MotionController and MotionRegistry
+	//-------------------------------------------------------------------------
+
+	// static constructor
+	// all subclasses must implement such a function and register it
+	static LLMotion *create(const LLUUID &id) { return new LLPhysicsMotionController(id); }
+
+public:
+	//-------------------------------------------------------------------------
+	// animation callbacks to be implemented by subclasses
+	//-------------------------------------------------------------------------
+
+	// motions must specify whether or not they loop
+	virtual BOOL getLoop() { return TRUE; }
+
+	// motions must report their total duration
+	virtual F32 getDuration() { return 0.0; }
+
+	// motions must report their "ease in" duration
+	virtual F32 getEaseInDuration() { return PHYSICS_MOTION_FADEIN_TIME; }
+
+	// motions must report their "ease out" duration.
+	virtual F32 getEaseOutDuration() { return PHYSICS_MOTION_FADEOUT_TIME; }
+
+	// called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+	virtual F32 getMinPixelArea();
+
+	// motions must report their priority
+	virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+	virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
+
+	// run-time (post constructor) initialization,
+	// called after parameters have been set
+	// must return true to indicate success and be available for activation
+	virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+	// called when a motion is activated
+	// must return TRUE to indicate success, or else
+	// it will be deactivated
+	virtual BOOL onActivate();
+
+	// called per time step
+	// must return TRUE while it is active, and
+	// must return FALSE when the motion is completed.
+	virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+	// called when a motion is deactivated
+	virtual void onDeactivate();
+
+	LLCharacter* getCharacter() { return mCharacter; }
+
+protected:
+	void addMotion(LLPhysicsMotion *motion);
+private:
+	LLCharacter*		mCharacter;
+
+	typedef std::vector<LLPhysicsMotion *> motion_vec_t;
+	motion_vec_t mMotions;
+};
+
+#endif // LL_LLPHYSICSMOTION_H
+
diff --git a/indra/newview/llpolymesh.cpp b/indra/newview/llpolymesh.cpp
index 2942f4befb2ed26e70f5021687084c0218c5d446..4541fa71d55c823f8ce953d74e371ee5f8d123ce 100644
--- a/indra/newview/llpolymesh.cpp
+++ b/indra/newview/llpolymesh.cpp
@@ -602,12 +602,35 @@ BOOL LLPolyMeshSharedData::loadMesh( const std::string& fileName )
 				}
 
 				mMorphData.insert(morph_data);
-				/*
-				if (std::string(morphName) == "Breast_Gravity")
+
+				if (!strcmp(morphName, "Big_Belly_Torso"))
+				{
+					LLPolyMorphData* belly_data = new LLPolyMorphData(*morph_data);
+					belly_data->mName = std::string("Belly_Gravity");
+					for (U32 v=0; v < belly_data->mNumIndices; v++)
+					{
+						// llinfos << "Coord: " << v << "\t" << belly_data->mCoords[v] << llendl;
+						belly_data->mCoords[v][0] = 0;
+						belly_data->mCoords[v][1] = 0;
+						belly_data->mCoords[v][2] = 0.01F;
+					}
+					mMorphData.insert(belly_data);
+				}
+
+				if (!strcmp(morphName, "Small_Butt"))
 				{
-					LLPolyMorphData *morph_data_clone = new LLPolyMorphData(std::string(morphName));
+					llinfos << "Reading small butt" << llendl;
+					LLPolyMorphData* butt_data = new LLPolyMorphData(*morph_data);
+					butt_data->mName = std::string("Butt_Gravity");
+					for (U32 v=0; v < butt_data->mNumIndices; v++)
+					{
+						// llinfos << "Coord: " << v << "\t" << butt_data->mCoords[v] << llendl;
+						butt_data->mCoords[v][0] = 0;
+						butt_data->mCoords[v][1] = 0;
+						butt_data->mCoords[v][2] = 0.01F;
+					}
+					mMorphData.insert(butt_data);
 				}
-				*/
 			}
 
 			S32 numRemaps;
diff --git a/indra/newview/llpolymorph.cpp b/indra/newview/llpolymorph.cpp
index ec41ef08f5762e6bc3bc72beb46e51e01d80d430..5a67fd482a7294319ec83a6ae95d32a9e5f5c357 100644
--- a/indra/newview/llpolymorph.cpp
+++ b/indra/newview/llpolymorph.cpp
@@ -59,6 +59,37 @@ LLPolyMorphData::LLPolyMorphData(const std::string& morph_name)
 	mMesh = NULL;
 }
 
+LLPolyMorphData::LLPolyMorphData(const LLPolyMorphData &rhs) :
+	mName(rhs.mName),
+	mNumIndices(rhs.mNumIndices),
+	mTotalDistortion(rhs.mTotalDistortion),
+	mAvgDistortion(rhs.mAvgDistortion),
+	mMaxDistortion(rhs.mMaxDistortion),
+	mVertexIndices(NULL),
+	mCoords(NULL),
+	mNormals(NULL),
+	mBinormals(NULL),
+	mTexCoords(NULL)
+{
+	const S32 numVertices = mNumIndices;
+
+	mCoords = new LLVector3[numVertices];
+	mNormals = new LLVector3[numVertices];
+	mBinormals = new LLVector3[numVertices];
+	mTexCoords = new LLVector2[numVertices];
+	mVertexIndices = new U32[numVertices];
+	
+	for (S32 v=0; v < numVertices; v++)
+	{
+		mCoords[v] = rhs.mCoords[v];
+		mNormals[v] = rhs.mNormals[v];
+		mBinormals[v] = rhs.mBinormals[v];
+		mTexCoords[v] = rhs.mTexCoords[v];
+		mVertexIndices[v] = rhs.mVertexIndices[v];
+	}
+}
+
+
 //-----------------------------------------------------------------------------
 // ~LLPolyMorphData()
 //-----------------------------------------------------------------------------
diff --git a/indra/newview/llpolymorph.h b/indra/newview/llpolymorph.h
index bc111882b709d8a67593f0d8dd5074662a95d2ba..8a024f2e9e05c55b83bb987f0f36a2d98ff3d86a 100644
--- a/indra/newview/llpolymorph.h
+++ b/indra/newview/llpolymorph.h
@@ -46,6 +46,7 @@ class LLPolyMorphData
 public:
 	LLPolyMorphData(const std::string& morph_name);
 	~LLPolyMorphData();
+	LLPolyMorphData(const LLPolyMorphData &rhs);
 
 	BOOL			loadBinary(LLFILE* fp, LLPolyMeshSharedData *mesh);
 	const std::string& getName() { return mName; }
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 3476dc80e8a4b9745d35295a36eea09fbb739f2c..2c5e728c87d67feb37137b27f2b0bbe747ba847f 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -48,7 +48,7 @@
 #include "llanimationstates.h"
 #include "llavatarnamecache.h"
 #include "llavatarpropertiesprocessor.h"
-#include "llbreastmotion.h"
+#include "llphysicsmotion.h"
 #include "llviewercontrol.h"
 #include "llcallingcard.h"		// IDEVO for LLAvatarTracker
 #include "lldrawpoolavatar.h"
@@ -126,7 +126,7 @@ const LLUUID ANIM_AGENT_HEAD_ROT = LLUUID("e6e8d1dd-e643-fff7-b238-c6b4b056a68d"
 const LLUUID ANIM_AGENT_PELVIS_FIX = LLUUID("0c5dd2a2-514d-8893-d44d-05beffad208b");  //"pelvis_fix"
 const LLUUID ANIM_AGENT_TARGET = LLUUID("0e4896cb-fba4-926c-f355-8720189d5b55");  //"target"
 const LLUUID ANIM_AGENT_WALK_ADJUST	= LLUUID("829bc85b-02fc-ec41-be2e-74cc6dd7215d");  //"walk_adjust"
-const LLUUID ANIM_AGENT_BREAST_MOTION = LLUUID("7360e029-3cb8-ebc4-863e-212df440d987");  //"breast_motion"
+const LLUUID ANIM_AGENT_PHYSICS_MOTION = LLUUID("7360e029-3cb8-ebc4-863e-212df440d987");  //"physics_motion"
 
 
 //-----------------------------------------------------------------------------
@@ -1149,7 +1149,7 @@ void LLVOAvatar::initClass()
 
 	gAnimLibrary.animStateSetString(ANIM_AGENT_BODY_NOISE,"body_noise");
 	gAnimLibrary.animStateSetString(ANIM_AGENT_BREATHE_ROT,"breathe_rot");
-	gAnimLibrary.animStateSetString(ANIM_AGENT_BREAST_MOTION,"breast_motion");
+	gAnimLibrary.animStateSetString(ANIM_AGENT_PHYSICS_MOTION,"physics_motion");
 	gAnimLibrary.animStateSetString(ANIM_AGENT_EDITING,"editing");
 	gAnimLibrary.animStateSetString(ANIM_AGENT_EYE,"eye");
 	gAnimLibrary.animStateSetString(ANIM_AGENT_FLY_ADJUST,"fly_adjust");
@@ -1288,7 +1288,7 @@ void LLVOAvatar::initInstance(void)
 		// motions without a start/stop bit
 		registerMotion( ANIM_AGENT_BODY_NOISE,				LLBodyNoiseMotion::create );
 		registerMotion( ANIM_AGENT_BREATHE_ROT,				LLBreatheMotionRot::create );
-		registerMotion( ANIM_AGENT_BREAST_MOTION,			LLBreastMotion::create );
+		registerMotion( ANIM_AGENT_PHYSICS_MOTION,			LLPhysicsMotionController::create );
 		registerMotion( ANIM_AGENT_EDITING,					LLEditingMotion::create	);
 		registerMotion( ANIM_AGENT_EYE,						LLEyeMotion::create	);
 		registerMotion( ANIM_AGENT_FEMALE_WALK,				LLKeyframeWalkMotion::create );
@@ -1702,7 +1702,7 @@ void LLVOAvatar::startDefaultMotions()
 	startMotion( ANIM_AGENT_EYE );
 	startMotion( ANIM_AGENT_BODY_NOISE );
 	startMotion( ANIM_AGENT_BREATHE_ROT );
-	startMotion( ANIM_AGENT_BREAST_MOTION );
+	startMotion( ANIM_AGENT_PHYSICS_MOTION );
 	startMotion( ANIM_AGENT_HAND_MOTION );
 	startMotion( ANIM_AGENT_PELVIS_FIX );
 
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index f5f90b2912ed39645c77ef73187eead4df06d6b2..3659fb055f743f05ac9f22a4e7919c61c01a0b36 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -50,7 +50,7 @@
 
 extern const LLUUID ANIM_AGENT_BODY_NOISE;
 extern const LLUUID ANIM_AGENT_BREATHE_ROT;
-extern const LLUUID ANIM_AGENT_BREAST_MOTION;
+extern const LLUUID ANIM_AGENT_PHYSICS_MOTION;
 extern const LLUUID ANIM_AGENT_EDITING;
 extern const LLUUID ANIM_AGENT_EYE;
 extern const LLUUID ANIM_AGENT_FLY_ADJUST;
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 30de6d14c620aee140566555bb874d08fd642448..3c7d7d1777aa109fb1fa7ac25d24ef9704149fe6 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -2530,6 +2530,7 @@ If you continue to receive this message, contact the [SUPPORT_SITE].
 <string name="Bushy Eyebrows">Bushy Eyebrows</string>
 <string name="Bushy Hair">Bushy Hair</string>
 <string name="Butt Size">Butt Size</string>
+<string name="Butt Gravity">Butt Gravity</string>
 <string name="bustle skirt">Bustle Skirt</string>
 <string name="no bustle">No Bustle</string>
 <string name="more bustle">More Bustle</string>