diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index cc73ffc32..bd7a541fc 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -348,6 +348,7 @@ set(viewer_SOURCE_FILES
llpanelweb.cpp
llparcelselection.cpp
llpatchvertexarray.cpp
+ llphysicsmotion.cpp
llpolymesh.cpp
llpolymorph.cpp
llprefschat.cpp
@@ -822,6 +823,7 @@ set(viewer_HEADER_FILES
llpanelweb.h
llparcelselection.h
llpatchvertexarray.h
+ llphysicsmotion.h
llpolymesh.h
llpolymorph.h
llprefschat.h
diff --git a/indra/newview/character/avatar_lad.xml b/indra/newview/character/avatar_lad.xml
index 7c320eceb..b2f3aea4c 100755
--- a/indra/newview/character/avatar_lad.xml
+++ b/indra/newview/character/avatar_lad.xml
@@ -2923,6 +2923,8 @@
id="40"
group="1"
name="Male_Head"
+ wearable="shape"
+ edit_group="driven"
value_min="0"
value_max="1">
@@ -3508,6 +3510,8 @@
id="104"
group="1"
name="Big_Belly_Torso"
+ wearable="shape"
+ edit_group="driven"
value_min="0"
value_max="1">
@@ -3824,6 +3828,8 @@
id="855"
group="1"
name="Love_Handles"
+ wearable="shape"
+ edit_group="driven"
value_default="0"
value_min="-1"
value_max="2">
@@ -3879,6 +3885,8 @@
id="100"
group="1"
name="Male_Torso"
+ wearable="shape"
+ edit_group="driven"
label_min="Male_Torso"
value_min="0"
value_max="1">
@@ -4047,9 +4055,66 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4537,15 +4607,62 @@
id="153"
group="1"
name="Male_Legs"
+ wearable="shape"
+ edit_group="driven"
value_min="0"
value_max="1">
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4828,6 +4946,8 @@
id="852"
group="1"
name="skirt_bigbutt"
+ wearable="skirt"
+ edit_group="driven"
label="bigbutt skirt"
label_min="less"
label_max="more"
@@ -4840,6 +4960,8 @@
id="849"
group="1"
name="skirt_belly"
+ wearable="skirt"
+ edit_group="driven"
label="big belly skirt"
value_min="0"
value_max="1">
@@ -4849,6 +4971,8 @@
@@ -4859,6 +4983,8 @@
id="851"
group="1"
name="skirt_chubby"
+ wearable="skirt"
+ edit_group="driven"
label_min="less"
label_max="more"
value_min="0"
@@ -4871,6 +4997,8 @@
id="856"
group="1"
name="skirt_lovehandles"
+ wearable="skirt"
+ edit_group="driven"
label_min="less"
label_max="more"
value_min="-1"
@@ -4888,11 +5016,32 @@
id="857"
group="1"
name="skirt_male"
+ wearable="skirt"
+ edit_group="driven"
value_min="0"
value_max="1">
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 5917d402f..39da9489f 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -7306,7 +7306,8 @@ void LLAgent::createStandardWearables(BOOL female)
TRUE, //WT_UNDERPANTS
FALSE, //WT_SKIRT
FALSE, //WT_ALPHA
- FALSE //WT_TATTOO
+ FALSE, //WT_TATTOO
+ FALSE, //WT_PHYSICS
};
for( S32 i=0; i < WT_COUNT; i++ )
@@ -7918,6 +7919,7 @@ void LLAgent::setWearableOutfit(
wearables_to_remove[WT_SKIRT] = remove && gRlvWearableLocks.canRemove(WT_SKIRT);
wearables_to_remove[WT_ALPHA] = remove && gRlvWearableLocks.canRemove(WT_ALPHA);
wearables_to_remove[WT_TATTOO] = remove && gRlvWearableLocks.canRemove(WT_TATTOO);
+ wearables_to_remove[WT_PHYSICS] = remove && gRlvWearableLocks.canRemove(WT_PHYSICS);
// [/RLVa:KB]
S32 count = wearables.count();
@@ -8199,6 +8201,7 @@ void LLAgent::userRemoveAllClothesStep2( BOOL proceed, void* userdata )
gAgent.removeWearable( WT_SKIRT );
gAgent.removeWearable( WT_ALPHA );
gAgent.removeWearable( WT_TATTOO );
+ gAgent.removeWearable( WT_PHYSICS );
}
}
diff --git a/indra/newview/llassetconverter.cpp b/indra/newview/llassetconverter.cpp
index 53c56a49c..73da5cd26 100644
--- a/indra/newview/llassetconverter.cpp
+++ b/indra/newview/llassetconverter.cpp
@@ -144,7 +144,7 @@ LLAssetType::EType LLAssetConverter::convert(std::string src_filename, std::stri
return LLAssetType::AT_NONE;
}
}
- else if(exten == "eyes" || exten == "gloves" || exten == "hair" || exten == "jacket" || exten == "pants" || exten == "shape" || exten == "shirt" || exten == "shoes" || exten == "skin" || exten == "skirt" || exten == "socks" || exten == "underpants" || exten == "undershirt" || exten == "bodypart" || exten == "clothing")
+ else if(exten == "eyes" || exten == "gloves" || exten == "hair" || exten == "jacket" || exten == "pants" || exten == "shape" || exten == "shirt" || exten == "shoes" || exten == "skin" || exten == "skirt" || exten == "socks" || exten == "underpants" || exten == "undershirt" || exten == "bodypart" || exten == "clothing" || exten == "physics")
{
asset_type = LLAssetType::AT_CLOTHING;
if(!copyFile(src_filename, filename))
diff --git a/indra/newview/lldriverparam.h b/indra/newview/lldriverparam.h
index 7bc0c1544..2e53ce563 100644
--- a/indra/newview/lldriverparam.h
+++ b/indra/newview/lldriverparam.h
@@ -35,6 +35,7 @@
#include "llviewervisualparam.h"
+class LLPhysicsMotion;
class LLVOAvatar;
//-----------------------------------------------------------------------------
@@ -78,6 +79,7 @@ protected:
class LLDriverParam : public LLViewerVisualParam
{
+ friend class LLPhysicsMotion; // physics motion needs to access driven params directly.
public:
LLDriverParam(LLVOAvatar *avatarp);
~LLDriverParam();
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 8648eef7c..5cea197ca 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -693,6 +693,16 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename)
L"Asset Blacklists (*.blacklist)\0*.blacklist\0" \
L"\0";
break;
+ case FFSAVE_PHYSICS:
+ if(filename.empty())
+ {
+ wcsncpy( mFilesW,L"untitled.phy", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"phy";
+ mOFN.lpstrFilter =
+ L"Landmarks (*.phy)\0*.phy\0" \
+ L"\0";
+ break;
//
default:
return FALSE;
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
index ea1e61fc2..feba53423 100644
--- a/indra/newview/llfilepicker.h
+++ b/indra/newview/llfilepicker.h
@@ -139,6 +139,7 @@ public:
FFSAVE_LANDMARK = 34,
FFSAVE_AO = 35,
FFSAVE_BLACKLIST = 36,
+ FFSAVE_PHYSICS = 37,
//
};
diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp
index 39d3ce5ab..2cc35f404 100644
--- a/indra/newview/llfloatercustomize.cpp
+++ b/indra/newview/llfloatercustomize.cpp
@@ -405,7 +405,14 @@ enum ESubpart {
SUBPART_UNDERPANTS,
SUBPART_SKIRT,
SUBPART_ALPHA,
- SUBPART_TATTOO
+ SUBPART_TATTOO,
+ SUBPART_PHYSICS_BREASTS_UPDOWN,
+ SUBPART_PHYSICS_BREASTS_INOUT,
+ SUBPART_PHYSICS_BREASTS_LEFTRIGHT,
+ SUBPART_PHYSICS_BELLY_UPDOWN,
+ SUBPART_PHYSICS_BUTT_UPDOWN,
+ SUBPART_PHYSICS_BUTT_LEFTRIGHT,
+ SUBPART_PHYSICS_ADVANCED
};
struct LLSubpart
@@ -926,6 +933,7 @@ ESubpart LLPanelEditWearable::getDefaultSubpart()
case WT_SKIRT: return SUBPART_SKIRT;
case WT_ALPHA: return SUBPART_ALPHA;
case WT_TATTOO: return SUBPART_TATTOO;
+ case WT_PHYSICS: return SUBPART_PHYSICS_BELLY_UPDOWN;
default: llassert(0); return SUBPART_SHAPE_WHOLE;
}
@@ -1708,6 +1716,7 @@ LLFloaterCustomize::LLFloaterCustomize()
factory_map["Skirt"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, WT_SKIRT) ) );
factory_map["Alpha"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, WT_ALPHA)));
factory_map["Tattoo"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, WT_TATTOO)));
+ factory_map["Physics"] = LLCallbackMap(createWearablePanel, (void*)(new WearablePanelData(this, WT_PHYSICS)));
LLUICtrlFactory::getInstance()->buildFloater(this, "floater_customize.xml", &factory_map);
}
@@ -1741,6 +1750,7 @@ BOOL LLFloaterCustomize::postBuild()
childSetTabChangeCallback("customize tab container", "Skirt", onTabChanged, (void*)WT_SKIRT, onTabPrecommit );
childSetTabChangeCallback("customize tab container", "Alpha", onTabChanged, (void*)WT_ALPHA, onTabPrecommit);
childSetTabChangeCallback("customize tab container", "Tattoo", onTabChanged, (void*)WT_TATTOO, onTabPrecommit);
+ childSetTabChangeCallback("customize tab container", "Physics", onTabChanged, (void*)WT_PHYSICS, onTabPrecommit);
// Remove underwear panels for teens
if (gAgent.isTeen())
diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp
index 4c41ddb66..b770a531d 100644
--- a/indra/newview/llinventoryactions.cpp
+++ b/indra/newview/llinventoryactions.cpp
@@ -462,7 +462,11 @@ void do_create(LLInventoryModel *model, LLInventoryPanel *ptr, std::string type,
LLUUID parent_id = self ? self->getUUID() : gInventory.findCategoryUUIDForType(LLAssetType::AT_BODYPART);
LLFolderBridge::createWearable(parent_id, WT_EYES);
}
-
+ else if ("physics" == type)
+ {
+ LLUUID parent_id = self ? self->getUUID() : gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING);
+ LLFolderBridge::createWearable(parent_id, WT_PHYSICS);
+ }
ptr->getRootFolder()->setNeedsAutoRename(TRUE);
}
diff --git a/indra/newview/llinventorybackup.cpp b/indra/newview/llinventorybackup.cpp
index 4ad9e79bb..a5ce38e59 100644
--- a/indra/newview/llinventorybackup.cpp
+++ b/indra/newview/llinventorybackup.cpp
@@ -209,6 +209,8 @@ LLFilePicker::ESaveFilter LLInventoryBackup::getSaveFilter(LLInventoryItem* item
return LLFilePicker::FFSAVE_UNDERPANTS;
case WT_UNDERSHIRT:
return LLFilePicker::FFSAVE_UNDERSHIRT;
+ case WT_PHYSICS:
+ return LLFilePicker::FFSAVE_PHYSICS;
default:
return LLFilePicker::FFSAVE_ALL;
}
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 3ac7342ae..3455caf19 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -186,6 +186,7 @@ std::string ICON_NAME[ICON_NAME_COUNT] =
"inv_item_skirt.tga",
"inv_item_alpha.tga",
"inv_item_tattoo.tga",
+ "inv_item_physics.png",
"inv_item_animation.tga",
"inv_item_gesture.tga",
@@ -2712,6 +2713,10 @@ void LLFolderBridge::createNewEyes(void* user_data)
LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_EYES);
}
+void LLFolderBridge::createNewPhysics(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_PHYSICS);
+}
// static
void LLFolderBridge::createWearable(LLFolderBridge* bridge, EWearableType type)
{
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index e9f131f68..dcadc2781 100755
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -72,6 +72,7 @@ enum EInventoryIcon
CLOTHING_SKIRT_ICON_NAME,
CLOTHING_ALPHA_ICON_NAME,
CLOTHING_TATTOO_ICON_NAME,
+ CLOTHING_PHYSICS_ICON_NAME,
ANIMATION_ICON_NAME,
GESTURE_ICON_NAME,
@@ -375,6 +376,7 @@ protected:
static void createNewSkin(void* user_data);
static void createNewHair(void* user_data);
static void createNewEyes(void* user_data);
+ static void createNewPhysics(void* user_data);
BOOL checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& typeToCheck);
diff --git a/indra/newview/llinventoryview.cpp b/indra/newview/llinventoryview.cpp
index 1e9ce663a..1d89872c9 100644
--- a/indra/newview/llinventoryview.cpp
+++ b/indra/newview/llinventoryview.cpp
@@ -1521,6 +1521,9 @@ std::string get_item_icon_name(LLAssetType::EType asset_type,
case WT_TATTOO:
idx = CLOTHING_TATTOO_ICON_NAME;
break;
+ case WT_PHYSICS:
+ idx = CLOTHING_PHYSICS_ICON_NAME;
+ break;
default:
// no-op, go with choice above
break;
diff --git a/indra/newview/llphysicsmotion.cpp b/indra/newview/llphysicsmotion.cpp
new file mode 100644
index 000000000..59434a1f1
--- /dev/null
+++ b/indra/newview/llphysicsmotion.cpp
@@ -0,0 +1,747 @@
+/**
+ * @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 "llagent.h"
+#include "llcharacter.h"
+#include "llviewercontrol.h"
+#include "llviewervisualparam.h"
+#include "llvoavatar.h"
+#include "lldriverparam.h"
+
+typedef std::map controller_map_t;
+typedef std::map default_controller_map_t;
+
+#define MIN_REQUIRED_PIXEL_AREA_AVATAR_PHYSICS_MOTION 0.f
+#define TIME_ITERATION_STEP 0.1f
+
+inline F64 llsgn(const F64 a)
+{
+ if (a >= 0)
+ return 1;
+ return -1;
+}
+
+/*
+ At a high level, this works by setting temporary parameters that are not stored
+ in the avatar's list of params, and are not conveyed to other users. We accomplish
+ this by creating some new temporary driven params inside avatar_lad that are then driven
+ by the actual params that the user sees and sets. For example, in the old system,
+ the user sets a param called breast bouyancy, which controls the Z value of the breasts.
+ In our new system, the user still sets the breast bouyancy, but that param is redefined
+ as a driver param so that affects a new temporary driven param that the bounce is applied
+ to.
+*/
+
+class LLPhysicsMotion
+{
+public:
+ /*
+ param_driver_name: The param that controls the params that are being affected by the physics.
+ joint_name: The joint that the body part is attached to. The joint is
+ used to determine the orientation (rotation) of the body part.
+
+ character: The avatar that this physics affects.
+
+ motion_direction_vec: The direction (in world coordinates) that determines the
+ motion. For example, (0,0,1) is up-down, and means that up-down motion is what
+ determines how this joint moves.
+
+ controllers: The various settings (e.g. spring force, mass) that determine how
+ the body part behaves.
+ */
+ LLPhysicsMotion(const std::string ¶m_driver_name,
+ const std::string &joint_name,
+ LLCharacter *character,
+ const LLVector3 &motion_direction_vec,
+ const controller_map_t &controllers) :
+ mParamDriverName(param_driver_name),
+ mJointName(joint_name),
+ mMotionDirectionVec(motion_direction_vec),
+ mParamDriver(NULL),
+ mParamControllers(controllers),
+ mCharacter(character),
+ mLastTime(0),
+ mPosition_local(0),
+ mVelocityJoint_local(0),
+ mPositionLastUpdate_local(0)
+ {
+ mJointState = new LLJointState;
+ }
+
+ BOOL initialize();
+
+ ~LLPhysicsMotion() {}
+
+ BOOL onUpdate(F32 time, bool &bHandled);
+
+ LLPointer 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 sDefaultController[controller_key];
+ }
+ const std::string& param_name = (*entry).second.c_str();
+ return mCharacter->getVisualParamWeight(param_name.c_str());
+ }
+ void setParamValue(LLViewerVisualParam *param,
+ const F32 new_value_local,
+ F32 behavior_maxeffect);
+
+ F32 toLocal(const LLVector3 &world);
+ F32 calculateVelocity_local();
+ F32 calculateAcceleration_local(F32 velocity_local);
+private:
+ const std::string mParamDriverName;
+ const std::string mParamControllerName;
+ 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;
+ LLVector3 mPosition_world;
+
+ LLViewerVisualParam *mParamDriver;
+ const controller_map_t mParamControllers;
+
+ LLPointer mJointState;
+ LLCharacter *mCharacter;
+
+ F32 mLastTime;
+
+ static default_controller_map_t sDefaultController;
+};
+
+default_controller_map_t initDefaultController()
+{
+ default_controller_map_t controller;
+ controller["Mass"] = 0.2f;
+ controller["Gravity"] = 0.0f;
+ controller["Damping"] = .05f;
+ controller["Drag"] = 0.15f;
+ controller["MaxEffect"] = 0.1f;
+ controller["Spring"] = 0.1f;
+ controller["Gain"] = 10.0f;
+ return controller;
+}
+
+default_controller_map_t LLPhysicsMotion::sDefaultController = initDefaultController();
+
+BOOL LLPhysicsMotion::initialize()
+{
+ if (!mJointState->setJoint(mCharacter->getJoint(mJointName.c_str())))
+ return FALSE;
+ mJointState->setUsage(LLJointState::ROT);
+
+ mParamDriver = (LLViewerVisualParam*)mCharacter->getVisualParam(mParamDriverName.c_str());
+ if (mParamDriver == NULL)
+ {
+ llinfos << "Failure reading in [ " << mParamDriverName << " ]" << llendl;
+ return FALSE;
+ }
+
+ 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();
+
+ // Breast Cleavage
+ {
+ controller_map_t controller;
+ controller["Mass"] = "Breast_Physics_Mass";
+ controller["Gravity"] = "Breast_Physics_Gravity";
+ controller["Drag"] = "Breast_Physics_Drag";
+ controller["Damping"] = "Breast_Physics_InOut_Damping";
+ controller["MaxEffect"] = "Breast_Physics_InOut_Max_Effect";
+ controller["Spring"] = "Breast_Physics_InOut_Spring";
+ controller["Gain"] = "Breast_Physics_InOut_Gain";
+ LLPhysicsMotion *motion = new LLPhysicsMotion("Breast_Physics_InOut_Controller",
+ "mChest",
+ character,
+ LLVector3(-1,0,0),
+ controller);
+ if (!motion->initialize())
+ {
+ llassert_always(FALSE);
+ return STATUS_FAILURE;
+ }
+ addMotion(motion);
+ }
+
+ // Breast Bounce
+ {
+ controller_map_t controller;
+ controller["Mass"] = "Breast_Physics_Mass";
+ controller["Gravity"] = "Breast_Physics_Gravity";
+ controller["Drag"] = "Breast_Physics_Drag";
+ controller["Damping"] = "Breast_Physics_UpDown_Damping";
+ controller["MaxEffect"] = "Breast_Physics_UpDown_Max_Effect";
+ controller["Spring"] = "Breast_Physics_UpDown_Spring";
+ controller["Gain"] = "Breast_Physics_UpDown_Gain";
+ LLPhysicsMotion *motion = new LLPhysicsMotion("Breast_Physics_UpDown_Controller",
+ "mChest",
+ character,
+ LLVector3(0,0,1),
+ controller);
+ if (!motion->initialize())
+ {
+ llassert_always(FALSE);
+ return STATUS_FAILURE;
+ }
+ addMotion(motion);
+ }
+
+ // Breast Sway
+ {
+ controller_map_t controller;
+ controller["Mass"] = "Breast_Physics_Mass";
+ controller["Gravity"] = "Breast_Physics_Gravity";
+ controller["Drag"] = "Breast_Physics_Drag";
+ controller["Damping"] = "Breast_Physics_LeftRight_Damping";
+ controller["MaxEffect"] = "Breast_Physics_LeftRight_Max_Effect";
+ controller["Spring"] = "Breast_Physics_LeftRight_Spring";
+ controller["Gain"] = "Breast_Physics_LeftRight_Gain";
+ LLPhysicsMotion *motion = new LLPhysicsMotion("Breast_Physics_LeftRight_Controller",
+ "mChest",
+ character,
+ LLVector3(0,-1,0),
+ controller);
+ if (!motion->initialize())
+ {
+ llassert_always(FALSE);
+ return STATUS_FAILURE;
+ }
+ addMotion(motion);
+ }
+ // Butt Bounce
+ {
+ controller_map_t controller;
+ controller["Mass"] = "Butt_Physics_Mass";
+ controller["Gravity"] = "Butt_Physics_Gravity";
+ controller["Drag"] = "Butt_Physics_Drag";
+ controller["Damping"] = "Butt_Physics_UpDown_Damping";
+ controller["MaxEffect"] = "Butt_Physics_UpDown_Max_Effect";
+ controller["Spring"] = "Butt_Physics_UpDown_Spring";
+ controller["Gain"] = "Butt_Physics_UpDown_Gain";
+ LLPhysicsMotion *motion = new LLPhysicsMotion("Butt_Physics_UpDown_Controller",
+ "mPelvis",
+ character,
+ LLVector3(0,0,-1),
+ controller);
+ if (!motion->initialize())
+ {
+ llassert_always(FALSE);
+ return STATUS_FAILURE;
+ }
+ addMotion(motion);
+ }
+
+ // Butt LeftRight
+ {
+ controller_map_t controller;
+ controller["Mass"] = "Butt_Physics_Mass";
+ controller["Gravity"] = "Butt_Physics_Gravity";
+ controller["Drag"] = "Butt_Physics_Drag";
+ controller["Damping"] = "Butt_Physics_LeftRight_Damping";
+ controller["MaxEffect"] = "Butt_Physics_LeftRight_Max_Effect";
+ controller["Spring"] = "Butt_Physics_LeftRight_Spring";
+ controller["Gain"] = "Butt_Physics_LeftRight_Gain";
+ LLPhysicsMotion *motion = new LLPhysicsMotion("Butt_Physics_LeftRight_Controller",
+ "mPelvis",
+ character,
+ LLVector3(0,-1,0),
+ controller);
+ if (!motion->initialize())
+ {
+ llassert_always(FALSE);
+ return STATUS_FAILURE;
+ }
+ addMotion(motion);
+ }
+
+ // Belly Bounce
+ {
+ controller_map_t controller;
+ controller["Mass"] = "Belly_Physics_Mass";
+ controller["Gravity"] = "Belly_Physics_Gravity";
+ controller["Drag"] = "Belly_Physics_Drag";
+ controller["Damping"] = "Belly_Physics_UpDown_Damping";
+ controller["MaxEffect"] = "Belly_Physics_UpDown_Max_Effect";
+ controller["Spring"] = "Belly_Physics_UpDown_Spring";
+ controller["Gain"] = "Belly_Physics_UpDown_Gain";
+ LLPhysicsMotion *motion = new LLPhysicsMotion("Belly_Physics_UpDown_Controller",
+ "mPelvis",
+ character,
+ LLVector3(0,0,-1),
+ controller);
+ if (!motion->initialize())
+ {
+ llassert_always(FALSE);
+ return STATUS_FAILURE;
+ }
+ addMotion(motion);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+void LLPhysicsMotionController::addMotion(LLPhysicsMotion *motion)
+{
+ addJointState(motion->getJointState());
+ mMotions.push_back(motion);
+}
+
+F32 LLPhysicsMotionController::getMinPixelArea()
+{
+ return MIN_REQUIRED_PIXEL_AREA_AVATAR_PHYSICS_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;
+}
+
+F32 LLPhysicsMotion::calculateVelocity_local()
+{
+ const F32 world_to_model_scale = 100.0f;
+ 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 positionchange_world = (position_world-last_position_world) * world_to_model_scale;
+ const LLVector3 velocity_world = positionchange_world;
+ const F32 velocity_local = toLocal(velocity_world);
+ return velocity_local;
+}
+
+F32 LLPhysicsMotion::calculateAcceleration_local(const F32 velocity_local)
+{
+// const F32 smoothing = getParamValue("Smoothing");
+ static const F32 smoothing = 3.0f; // Removed smoothing param since it's probably not necessary
+ 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;
+ }*/
+
+ BOOL update_visuals = FALSE;
+ bool physics_handled = false;
+ for (motion_vec_t::iterator iter = mMotions.begin();
+ iter != mMotions.end();
+ ++iter)
+ {
+ LLPhysicsMotion *motion = (*iter);
+ bool bHandled;
+ update_visuals |= motion->onUpdate(time,bHandled);
+ physics_handled |= bHandled;
+ }
+
+ if (update_visuals)
+ mCharacter->updateVisualParams();
+
+ if(!physics_handled && mCharacter) //If absolutely nothing was done, and it wasn't due to timers/lod
+ ((LLVOAvatar*)mCharacter)->idleUpdateBoobEffect();
+ return TRUE;
+}
+
+
+// Return TRUE if character has to update visual params.
+BOOL LLPhysicsMotion::onUpdate(F32 time, bool &bHandled)
+{
+ bHandled = false;
+ // static FILE *mFileWrite = fopen("c:\\temp\\avatar_data.txt","w");
+
+ if (!mParamDriver)
+ return FALSE;
+
+ if (!mLastTime)
+ {
+ mLastTime = time;
+ return FALSE;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Get all parameters and settings
+ //
+
+ const F32 time_delta = time - mLastTime;
+
+ bHandled = true;
+ // Don't update too frequently, to avoid precision errors from small time slices.
+ if (time_delta <= .01)
+ {
+ return FALSE;
+ }
+
+ // If less than 1FPS, we don't want to be spending time updating physics at all.
+ if (time_delta > 1.0)
+ {
+ 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;
+ }
+
+ LLJoint *joint = mJointState->getJoint();
+
+ const F32 behavior_mass = getParamValue("Mass");
+ const F32 behavior_gravity = getParamValue("Gravity");
+ const F32 behavior_spring = getParamValue("Spring");
+ const F32 behavior_gain = getParamValue("Gain");
+ const F32 behavior_damping = getParamValue("Damping");
+ const F32 behavior_drag = getParamValue("Drag");
+ const BOOL physics_test = FALSE; // Enable this to simulate bouncing on all parts.
+
+ F32 behavior_maxeffect = getParamValue("MaxEffect");
+ if (physics_test)
+ behavior_maxeffect = 1.0f;
+
+ // Normalize the param position to be from [0,1].
+ // We have to use normalized values because there may be more than one driven param,
+ // and each of these driven params may have its own range.
+ // This means we'll do all our calculations in normalized [0,1] local coordinates.
+ const F32 position_user_local = (mParamDriver->getWeight() - mParamDriver->getMinWeight()) / (mParamDriver->getMaxWeight() - mParamDriver->getMinWeight());
+
+ //
+ // End parameters and settings
+ ////////////////////////////////////////////////////////////////////////////////
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Calculate velocity and acceleration in parameter space.
+ //
+
+ //const F32 velocity_joint_local = calculateVelocity_local(time_iteration_step);
+ const F32 velocity_joint_local = calculateVelocity_local();
+ const F32 acceleration_joint_local = calculateAcceleration_local(velocity_joint_local);
+
+ //
+ // End velocity and acceleration
+ ////////////////////////////////////////////////////////////////////////////////
+
+ BOOL update_visuals = FALSE;
+
+ // Break up the physics into a bunch of iterations so that differing framerates will show
+ // roughly the same behavior.
+ for (F32 time_iteration = 0; time_iteration <= time_delta; time_iteration += TIME_ITERATION_STEP)
+ {
+ F32 time_iteration_step = TIME_ITERATION_STEP;
+ if (time_iteration + TIME_ITERATION_STEP > time_delta)
+ {
+ time_iteration_step = time_delta-time_iteration;
+ }
+
+ // mPositon_local should be in normalized 0,1 range already. Just making sure...
+ const F32 position_current_local = llclamp(mPosition_local,
+ 0.0f,
+ 1.0f);
+ // If the effect is turned off then don't process unless we need one more update
+ // to set the position to the default (i.e. user) position.
+ if ((behavior_maxeffect == 0) && (position_current_local == position_user_local))
+ {
+ bHandled = false; //Let emerald boob stuff do its thing, possibly.
+ return update_visuals;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // 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 = (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;
+ static const F32 max_velocity = 100.0f; // magic number, used to be customizable.
+ F32 velocity_new_local = mVelocity_local + acceleration_new_local*time_iteration_step;
+ velocity_new_local = llclamp(velocity_new_local,
+ -max_velocity, max_velocity);
+
+ // Temporary debugging setting to cause all avatars to move, for profiling purposes.
+ if (physics_test)
+ {
+ velocity_new_local = sin(time*4.0);
+ }
+ // Calculate the new parameters, or remain unchanged if max speed is 0.
+ F32 position_new_local = position_current_local + velocity_new_local*time_iteration_step;
+ if (behavior_maxeffect == 0)
+ position_new_local = position_user_local;
+
+ // Zero out the velocity if the param is being pushed beyond its limits.
+ if ((position_new_local < 0 && velocity_new_local < 0) ||
+ (position_new_local > 1 && velocity_new_local > 0))
+ {
+ velocity_new_local = 0;
+ }
+
+ // Check for NaN values. A NaN value is detected if the variables doesn't equal itself.
+ // If NaN, then reset everything.
+ if ((mPosition_local != mPosition_local) ||
+ (mVelocity_local != mVelocity_local) ||
+ (position_new_local != position_new_local))
+ {
+ position_new_local = 0;
+ mVelocity_local = 0;
+ mVelocityJoint_local = 0;
+ mAccelerationJoint_local = 0;
+ mPosition_local = 0;
+ mPosition_world = LLVector3(0,0,0);
+ }
+
+ const F32 position_new_local_clamped = llclamp(position_new_local,
+ 0.0f,
+ 1.0f);
+
+ LLDriverParam *driver_param = dynamic_cast(mParamDriver);
+ llassert_always(driver_param);
+ if (driver_param)
+ {
+ // If this is one of our "hidden" driver params, then make sure it's
+ // the default value.
+ if ((driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) &&
+ (driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT))
+ {
+ mCharacter->setVisualParamWeight(driver_param,
+ 0,
+ FALSE);
+ }
+ for (LLDriverParam::entry_list_t::iterator iter = driver_param->mDriven.begin();
+ iter != driver_param->mDriven.end();
+ ++iter)
+ {
+ LLDrivenEntry &entry = (*iter);
+ LLViewerVisualParam *driven_param = entry.mParam;
+ setParamValue(driven_param,position_new_local_clamped, behavior_maxeffect);
+ }
+ }
+
+ //
+ // 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.
+
+ // 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(mCharacter) != NULL && ((LLVOAvatar*)mCharacter)->isSelf());
+ if ((pixel_area > area_for_this_setting) || is_self)
+ {
+ const F32 position_diff_local = llabs(mPositionLastUpdate_local-position_new_local_clamped);
+ const F32 min_delta = (1.0001f-lod_factor)*0.4f;
+ if (llabs(position_diff_local) > min_delta)
+ {
+ update_visuals = TRUE;
+ mPositionLastUpdate_local = position_new_local;
+ }
+ }
+
+ //
+ // End update visual params
+ ////////////////////////////////////////////////////////////////////////////////
+
+ mVelocity_local = velocity_new_local;
+ mAccelerationJoint_local = acceleration_joint_local;
+ mPosition_local = position_new_local;
+ }
+ mLastTime = time;
+ mPosition_world = joint->getWorldPosition();
+ mVelocityJoint_local = velocity_joint_local;
+
+
+ /*
+ // Write out debugging info into a spreadsheet.
+ 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;
+}
+
+// Range of new_value_local is assumed to be [0 , 1] normalized.
+void LLPhysicsMotion::setParamValue(LLViewerVisualParam *param,
+ F32 new_value_normalized,
+ F32 behavior_maxeffect)
+{
+ const F32 value_min_local = param->getMinWeight();
+ const F32 value_max_local = param->getMaxWeight();
+ const F32 min_val = 0.5f-behavior_maxeffect/2.0;
+ const F32 max_val = 0.5f+behavior_maxeffect/2.0;
+
+ // Scale from [0,1] to [min_val,max_val]
+ const F32 new_value_rescaled = min_val + (max_val-min_val) * new_value_normalized;
+
+ // Scale from [0,1] to [value_min_local,value_max_local]
+ const F32 new_value_local = value_min_local + (value_max_local-value_min_local) * new_value_rescaled;
+
+ mCharacter->setVisualParamWeight(param,
+ new_value_local,
+ FALSE);
+}
diff --git a/indra/newview/llphysicsmotion.h b/indra/newview/llphysicsmotion.h
new file mode 100644
index 000000000..657698e4f
--- /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 motion_vec_t;
+ motion_vec_t mMotions;
+};
+
+#endif // LL_LLPHYSICSMOTION_H
+
diff --git a/indra/newview/llpolymesh.cpp b/indra/newview/llpolymesh.cpp
index dbe6079ed..a23c199c9 100644
--- a/indra/newview/llpolymesh.cpp
+++ b/indra/newview/llpolymesh.cpp
@@ -49,7 +49,16 @@
#define HEADER_ASCII "Linden Mesh 1.0"
#define HEADER_BINARY "Linden Binary Mesh 1.0"
-extern LLControlGroup gSavedSettings; // read only
+extern LLControlGroup gSavedSettings; // read only
+
+LLPolyMorphData *clone_morph_param_duplicate(const LLPolyMorphData *src_data,
+ const std::string &name);
+LLPolyMorphData *clone_morph_param_direction(const LLPolyMorphData *src_data,
+ const LLVector3 &direction,
+ const std::string &name);
+LLPolyMorphData *clone_morph_param_cleavage(const LLPolyMorphData *src_data,
+ F32 scale,
+ const std::string &name);
//-----------------------------------------------------------------------------
// Global table of loaded LLPolyMeshes
@@ -606,8 +615,60 @@ BOOL LLPolyMeshSharedData::loadMesh( const std::string& fileName )
continue;
}
- mMorphData.insert(morph_data);
- }
+ mMorphData.insert(morph_data);
+
+ if (!strcmp(morphName, "Breast_Female_Cleavage"))
+ {
+ mMorphData.insert(clone_morph_param_cleavage(morph_data,
+ .75f,
+ "Breast_Physics_LeftRight_Driven"));
+ }
+
+ if (!strcmp(morphName, "Breast_Female_Cleavage"))
+ {
+ mMorphData.insert(clone_morph_param_duplicate(morph_data,
+ "Breast_Physics_InOut_Driven"));
+ }
+ if (!strcmp(morphName, "Breast_Gravity"))
+ {
+ mMorphData.insert(clone_morph_param_duplicate(morph_data,
+ "Breast_Physics_UpDown_Driven"));
+ }
+
+ if (!strcmp(morphName, "Big_Belly_Torso"))
+ {
+ mMorphData.insert(clone_morph_param_direction(morph_data,
+ LLVector3(0,0,0.05f),
+ "Belly_Physics_Torso_UpDown_Driven"));
+ }
+
+ if (!strcmp(morphName, "Big_Belly_Legs"))
+ {
+ mMorphData.insert(clone_morph_param_direction(morph_data,
+ LLVector3(0,0,0.05f),
+ "Belly_Physics_Legs_UpDown_Driven"));
+ }
+
+ if (!strcmp(morphName, "skirt_belly"))
+ {
+ mMorphData.insert(clone_morph_param_direction(morph_data,
+ LLVector3(0,0,0.05f),
+ "Belly_Physics_Skirt_UpDown_Driven"));
+ }
+
+ if (!strcmp(morphName, "Small_Butt"))
+ {
+ mMorphData.insert(clone_morph_param_direction(morph_data,
+ LLVector3(0,0,0.05f),
+ "Butt_Physics_UpDown_Driven"));
+ }
+ if (!strcmp(morphName, "Small_Butt"))
+ {
+ mMorphData.insert(clone_morph_param_direction(morph_data,
+ LLVector3(0,0.03f,0),
+ "Butt_Physics_LeftRight_Driven"));
+ }
+ }
S32 numRemaps;
if (fread(&numRemaps, sizeof(S32), 1, fp) == 1)
@@ -1156,4 +1217,55 @@ void LLPolySkeletalDistortion::apply( ESex avatar_sex )
mLastWeight = mCurWeight;
}
+
+LLPolyMorphData *clone_morph_param_duplicate(const LLPolyMorphData *src_data,
+ const std::string &name)
+{
+ LLPolyMorphData* cloned_morph_data = new LLPolyMorphData(*src_data);
+ cloned_morph_data->mName = name;
+ for (U32 v=0; v < cloned_morph_data->mNumIndices; v++)
+ {
+ cloned_morph_data->mCoords[v] = src_data->mCoords[v];
+ cloned_morph_data->mNormals[v] = src_data->mNormals[v];
+ cloned_morph_data->mBinormals[v] = src_data->mBinormals[v];
+ }
+ return cloned_morph_data;
+}
+
+LLPolyMorphData *clone_morph_param_direction(const LLPolyMorphData *src_data,
+ const LLVector3 &direction,
+ const std::string &name)
+{
+ LLPolyMorphData* cloned_morph_data = new LLPolyMorphData(*src_data);
+ cloned_morph_data->mName = name;
+ for (U32 v=0; v < cloned_morph_data->mNumIndices; v++)
+ {
+ cloned_morph_data->mCoords[v] = direction;
+ cloned_morph_data->mNormals[v] = LLVector3(0,0,0);
+ cloned_morph_data->mBinormals[v] = LLVector3(0,0,0);
+ }
+ return cloned_morph_data;
+}
+
+LLPolyMorphData *clone_morph_param_cleavage(const LLPolyMorphData *src_data,
+ F32 scale,
+ const std::string &name)
+{
+ LLPolyMorphData* cloned_morph_data = new LLPolyMorphData(*src_data);
+ cloned_morph_data->mName = name;
+ for (U32 v=0; v < cloned_morph_data->mNumIndices; v++)
+ {
+ cloned_morph_data->mCoords[v] = src_data->mCoords[v]*scale;
+ cloned_morph_data->mNormals[v] = src_data->mNormals[v]*scale;
+ cloned_morph_data->mBinormals[v] = src_data->mBinormals[v]*scale;
+ if (cloned_morph_data->mCoords[v][1] < 0)
+ {
+ cloned_morph_data->mCoords[v][1] *= -1;
+ cloned_morph_data->mNormals[v][1] *= -1;
+ cloned_morph_data->mBinormals[v][1] *= -1;
+ }
+ }
+ return cloned_morph_data;
+}
+
// End
diff --git a/indra/newview/llpolymorph.cpp b/indra/newview/llpolymorph.cpp
index 3a57b6f9f..72a332440 100644
--- a/indra/newview/llpolymorph.cpp
+++ b/indra/newview/llpolymorph.cpp
@@ -64,6 +64,36 @@ 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()
//-----------------------------------------------------------------------------
@@ -292,10 +322,22 @@ BOOL LLPolyMorphTarget::setInfo(LLPolyMorphTargetInfo* info)
}
}
- mMorphData = mMesh->getMorphData(getInfo()->mMorphName);
+ std::string morph_param_name = getInfo()->mMorphName;
+
+ mMorphData = mMesh->getMorphData(morph_param_name);
if (!mMorphData)
{
- llwarns << "No morph target named " << getInfo()->mMorphName << " found in mesh." << llendl;
+ const std::string driven_tag = "_Driven";
+ U32 pos = morph_param_name.find(driven_tag);
+ if (pos > 0)
+ {
+ morph_param_name = morph_param_name.substr(0,pos);
+ mMorphData = mMesh->getMorphData(morph_param_name);
+ }
+ }
+ if (!mMorphData)
+ {
+ llwarns << "No morph target named " << morph_param_name << " found in mesh." << llendl;
return FALSE; // Continue, ignoring this tag
}
return TRUE;
@@ -445,6 +487,16 @@ void LLPolyMorphTarget::apply( ESex avatar_sex )
mLastSex = avatar_sex;
+ // Check for NaN condition (NaN is detected if a variable doesn't equal itself.
+ if (mCurWeight != mCurWeight)
+ {
+ mCurWeight = 0.0;
+ }
+ if (mLastWeight != mLastWeight)
+ {
+ mLastWeight = mCurWeight+.001;
+ }
+
// perform differential update of morph
F32 delta_weight = ( getSex() & avatar_sex ) ? (mCurWeight - mLastWeight) : (getDefaultWeight() - mLastWeight);
// store last weight
diff --git a/indra/newview/llpolymorph.h b/indra/newview/llpolymorph.h
index f8dd52ca3..3fdaf3785 100644
--- a/indra/newview/llpolymorph.h
+++ b/indra/newview/llpolymorph.h
@@ -51,6 +51,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/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 956e31502..b75c3345e 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -3913,6 +3913,7 @@ class LLEditEnableCustomizeAvatar : public view_listener_t
}
};
+
class LLEditEnableChangeDisplayname : public view_listener_t
{
bool handleEvent(LLPointer event, const LLSD& userdata)
@@ -8894,6 +8895,10 @@ class LLEditEnableTakeOff : public view_listener_t
{
new_value = LLAgent::selfHasWearable((void *)WT_TATTOO);
}
+ if (clothing == "physics")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_PHYSICS);
+ }
// [RLVa:KB] - Checked: 2009-07-07 (RLVa-1.1.3b) | Modified: RLVa-1.1.3b | OK
// Why aren't they using LLWearable::typeNameToType()? *confuzzled*
@@ -8957,6 +8962,10 @@ class LLEditTakeOff : public view_listener_t
{
LLAgent::userRemoveWearable((void*)WT_TATTOO);
}
+ else if (clothing == "physics")
+ {
+ LLAgent::userRemoveWearable((void*)WT_PHYSICS);
+ }
else if (clothing == "all")
{
LLAgent::userRemoveAllClothes(NULL);
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 5f1749ddd..c1d9f11a9 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -103,6 +103,8 @@
#include "llavatarname.h"
#include "llavatarnamecache.h"
+#include "llphysicsmotion.h"
+
// [RLVa:KB] - Checked: 2010-04-01 (RLVa-1.2.0c)
#include "rlvhandler.h"
// [/RLVa:KB]
@@ -128,6 +130,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_PHYSICS_MOTION = LLUUID("7360e029-3cb8-ebc4-863e-212df440d987"); //"physics_motion"
//-----------------------------------------------------------------------------
@@ -720,6 +723,7 @@ BOOL LLVOAvatar::sShowAnimationDebug = FALSE;
BOOL LLVOAvatar::sShowFootPlane = FALSE;
BOOL LLVOAvatar::sVisibleInFirstPerson = FALSE;
F32 LLVOAvatar::sLODFactor = 1.f;
+F32 LLVOAvatar::sPhysicsLODFactor = 1.f;
BOOL LLVOAvatar::sUseImpostors = FALSE;
BOOL LLVOAvatar::sJointDebug = FALSE;
@@ -1016,6 +1020,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
// motions without a start/stop bit
registerMotion( ANIM_AGENT_BODY_NOISE, LLBodyNoiseMotion::create );
registerMotion( ANIM_AGENT_BREATHE_ROT, LLBreatheMotionRot::create );
+ registerMotion( ANIM_AGENT_PHYSICS_MOTION, LLPhysicsMotionController::create );
registerMotion( ANIM_AGENT_EDITING, LLEditingMotion::create );
registerMotion( ANIM_AGENT_EYE, LLEyeMotion::create );
registerMotion( ANIM_AGENT_FLY_ADJUST, LLFlyAdjustMotion::create );
@@ -1970,6 +1975,7 @@ void LLVOAvatar::startDefaultMotions()
startMotion( ANIM_AGENT_EYE );
startMotion( ANIM_AGENT_BODY_NOISE );
startMotion( ANIM_AGENT_BREATHE_ROT );
+ startMotion( ANIM_AGENT_PHYSICS_MOTION );
startMotion( ANIM_AGENT_HAND_MOTION );
startMotion( ANIM_AGENT_PELVIS_FIX );
@@ -2791,7 +2797,7 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
idleUpdateAppearanceAnimation();
if (detailed_update)
{
- idleUpdateBoobEffect();
+ //idleUpdateBoobEffect();
idleUpdateLipSync( voice_enabled );
idleUpdateLoadingEffect();
idleUpdateBelowWater(); // wind effect uses this
@@ -9151,6 +9157,13 @@ void LLVOAvatar::wearableUpdated(EWearableType type, BOOL upload_result)
}
}
}
+
+ // Physics type has no associated baked textures, but change of params needs to be sent to
+ // other avatars.
+ if (isSelf() && type == WT_PHYSICS)
+ {
+ gAgent.sendAgentSetAppearance();
+ }
}
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 4bedcbb10..0ea151b12 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -55,6 +55,7 @@
extern const LLUUID ANIM_AGENT_BODY_NOISE;
extern const LLUUID ANIM_AGENT_BREATHE_ROT;
+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;
@@ -629,6 +630,7 @@ public:
static BOOL sDebugInvisible;
static BOOL sShowAttachmentPoints;
static F32 sLODFactor; // user-settable LOD factor
+ static F32 sPhysicsLODFactor; // user-settable physics LOD factor
static BOOL sJointDebug; // output total number of joints being touched for each avatar
static BOOL sDebugAvatarRotation;
static F32 sAvMorphTime;
diff --git a/indra/newview/llwearable.cpp b/indra/newview/llwearable.cpp
index 14c6220c5..e85726166 100644
--- a/indra/newview/llwearable.cpp
+++ b/indra/newview/llwearable.cpp
@@ -72,6 +72,7 @@ const std::string LLWearable::sTypeName[ WT_COUNT+1 ] =
"skirt",
"alpha",
"tattoo",
+ "physics",
"invalid"
};
@@ -93,6 +94,7 @@ const std::string LLWearable::sTypeLabel[ WT_COUNT+1 ] =
"Skirt",
"Alpha",
"Tattoo",
+ "Physics",
"invalid"
};
@@ -118,6 +120,7 @@ LLAssetType::EType LLWearable::typeToAssetType(EWearableType wearable_type)
case WT_SKIRT:
case WT_ALPHA:
case WT_TATTOO:
+ case WT_PHYSICS:
return LLAssetType::AT_CLOTHING;
default:
return LLAssetType::AT_NONE;
diff --git a/indra/newview/llwearable.h b/indra/newview/llwearable.h
index 253ae97b0..72d876359 100644
--- a/indra/newview/llwearable.h
+++ b/indra/newview/llwearable.h
@@ -58,7 +58,8 @@ enum EWearableType // If you change this, update LLWearable::getTypeName(), get
WT_SKIRT = 12,
WT_ALPHA = 13,
WT_TATTOO = 14,
- WT_COUNT = 15,
+ WT_PHYSICS = 15,
+ WT_COUNT = 16,
WT_INVALID = 255
};
diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp
index 8fb992e0c..94b88fb0c 100644
--- a/indra/newview/rlvhandler.cpp
+++ b/indra/newview/rlvhandler.cpp
@@ -1993,7 +1993,7 @@ ERlvCmdRet RlvHandler::onGetOutfit(const RlvCommand& rlvCmd, std::string& strRep
const EWearableType wtRlvTypes[] =
{
WT_GLOVES, WT_JACKET, WT_PANTS, WT_SHIRT, WT_SHOES, WT_SKIRT, WT_SOCKS,
- WT_UNDERPANTS, WT_UNDERSHIRT, WT_SKIN, WT_EYES, WT_HAIR, WT_SHAPE, WT_ALPHA, WT_TATTOO
+ WT_UNDERPANTS, WT_UNDERSHIRT, WT_SKIN, WT_EYES, WT_HAIR, WT_SHAPE, WT_ALPHA, WT_TATTOO, WT_PHYSICS
};
for (int idxType = 0, cntType = sizeof(wtRlvTypes) / sizeof(EWearableType); idxType < cntType; idxType++)
diff --git a/indra/newview/skins/default/textures/inv_item_physics.png b/indra/newview/skins/default/textures/inv_item_physics.png
new file mode 100644
index 000000000..360baec46
Binary files /dev/null and b/indra/newview/skins/default/textures/inv_item_physics.png differ
diff --git a/indra/newview/skins/default/xui/en-us/floater_inventory.xml b/indra/newview/skins/default/xui/en-us/floater_inventory.xml
index 4832c5132..d42d855d6 100644
--- a/indra/newview/skins/default/xui/en-us/floater_inventory.xml
+++ b/indra/newview/skins/default/xui/en-us/floater_inventory.xml
@@ -160,6 +160,10 @@
mouse_opaque="true" name="New Alpha" width="125">
+
+
+
+
+
+
+
diff --git a/indra/newview/skins/default/xui/en-us/menu_viewer.xml b/indra/newview/skins/default/xui/en-us/menu_viewer.xml
index a73400803..aa3f5b756 100644
--- a/indra/newview/skins/default/xui/en-us/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en-us/menu_viewer.xml
@@ -196,7 +196,12 @@
-
+
+
+
+