summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xetc/message.xml8
-rw-r--r--indra/llappearance/llavatarappearance.cpp27
-rw-r--r--indra/llappearance/llavatarappearance.h2
-rw-r--r--indra/llcharacter/lljoint.cpp2
-rw-r--r--indra/llcharacter/llmotioncontroller.cpp7
-rw-r--r--indra/llcharacter/llmotioncontroller.h6
-rw-r--r--indra/llcommon/llcallstack.h7
-rw-r--r--indra/llcommon/llstring.cpp19
-rw-r--r--indra/llcommon/llstring.h3
-rw-r--r--indra/llmath/llmatrix4a.h6
-rw-r--r--indra/llmath/llvector4a.h7
-rw-r--r--indra/llmath/llvolume.cpp12
-rw-r--r--indra/llmessage/message_prehash.cpp1
-rw-r--r--indra/llmessage/message_prehash.h1
-rw-r--r--indra/llprimitive/lldaeloader.cpp10
-rw-r--r--indra/llprimitive/llmodel.cpp1
-rw-r--r--indra/llprimitive/llprimitive.cpp66
-rw-r--r--indra/llprimitive/llprimitive.h22
-rw-r--r--indra/newview/CMakeLists.txt4
-rw-r--r--indra/newview/app_settings/logcontrol.xml1
-rw-r--r--indra/newview/app_settings/settings.xml33
-rw-r--r--indra/newview/llappearancemgr.cpp5
-rw-r--r--indra/newview/llappviewer.cpp5
-rw-r--r--indra/newview/llappviewerwin32.cpp5
-rw-r--r--indra/newview/llappviewerwin32.h1
-rw-r--r--indra/newview/llcontrolavatar.cpp429
-rw-r--r--indra/newview/llcontrolavatar.h85
-rw-r--r--indra/newview/lldrawable.cpp8
-rw-r--r--indra/newview/lldrawpool.cpp2
-rw-r--r--indra/newview/lldrawpoolavatar.cpp57
-rw-r--r--indra/newview/lldrawpoolavatar.h2
-rw-r--r--indra/newview/llface.cpp23
-rw-r--r--indra/newview/llfloaterbvhpreview.cpp8
-rw-r--r--indra/newview/llfloaterimagepreview.cpp9
-rw-r--r--indra/newview/llfloatermodelpreview.cpp19
-rw-r--r--indra/newview/llmeshrepository.cpp32
-rw-r--r--indra/newview/llmeshrepository.h2
-rw-r--r--indra/newview/llpanelobject.cpp16
-rw-r--r--indra/newview/llpanelvolume.cpp54
-rw-r--r--indra/newview/llpanelvolume.h1
-rw-r--r--indra/newview/llselectmgr.cpp103
-rw-r--r--indra/newview/llselectmgr.h7
-rw-r--r--indra/newview/llspatialpartition.cpp58
-rw-r--r--indra/newview/llspatialpartition.h2
-rw-r--r--indra/newview/llstartup.cpp1
-rw-r--r--indra/newview/lltooldraganddrop.cpp2
-rw-r--r--indra/newview/lltoolpie.cpp23
-rw-r--r--indra/newview/lltoolselect.cpp4
-rw-r--r--indra/newview/lltoolselectrect.cpp4
-rw-r--r--indra/newview/lluiavatar.cpp59
-rw-r--r--indra/newview/lluiavatar.h44
-rw-r--r--indra/newview/llviewerjointattachment.cpp19
-rw-r--r--indra/newview/llviewerjointattachment.h1
-rw-r--r--indra/newview/llviewermenu.cpp46
-rw-r--r--indra/newview/llviewermessage.cpp80
-rw-r--r--indra/newview/llviewermessage.h1
-rw-r--r--indra/newview/llviewerobject.cpp297
-rw-r--r--indra/newview/llviewerobject.h43
-rw-r--r--indra/newview/llviewerobjectlist.cpp56
-rw-r--r--indra/newview/llviewerobjectlist.h2
-rw-r--r--indra/newview/llvieweroctree.cpp2
-rw-r--r--indra/newview/llviewerregion.cpp48
-rw-r--r--indra/newview/llviewerregion.h1
-rw-r--r--indra/newview/llviewerwindow.cpp4
-rw-r--r--indra/newview/llvoavatar.cpp1726
-rw-r--r--indra/newview/llvoavatar.h49
-rw-r--r--indra/newview/llvoavatarself.cpp2
-rw-r--r--indra/newview/llvograss.cpp2
-rw-r--r--indra/newview/llvovolume.cpp559
-rw-r--r--indra/newview/llvovolume.h25
-rw-r--r--indra/newview/pipeline.cpp12
-rw-r--r--indra/newview/pipeline.h9
-rw-r--r--indra/newview/skins/default/xui/en/floater_tools.xml18
-rw-r--r--indra/newview/skins/default/xui/en/menu_object.xml16
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml10
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml40
-rw-r--r--indra/test/test.cpp2
-rwxr-xr-xscripts/messages/message_template.msg26
-rwxr-xr-xscripts/messages/message_template.msg.sha12
-rw-r--r--scripts/testing/lsl/axon_test_region_driver.lsl54
-rw-r--r--scripts/testing/lsl/cycle_object_animations.lsl118
-rw-r--r--scripts/testing/lsl/cycle_object_animations_v2.lsl133
-rw-r--r--scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl90
83 files changed, 3859 insertions, 949 deletions
diff --git a/etc/message.xml b/etc/message.xml
index 6d8160abb5..b444fe6c11 100755
--- a/etc/message.xml
+++ b/etc/message.xml
@@ -236,6 +236,14 @@
<boolean>false</boolean>
</map>
+ <key>ObjectAnimation</key>
+ <map>
+ <key>flavor</key>
+ <string>template</string>
+ <key>trusted-sender</key>
+ <boolean>false</boolean>
+ </map>
+
<key>AvatarAppearance</key>
<map>
<key>flavor</key>
diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index e5089f028f..e35e4edec2 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -1723,7 +1723,7 @@ void LLAvatarAppearance::makeJointAliases(LLAvatarBoneInfo *bone_info)
}
mJointAliasMap[*i] = bone_name;
}
-
+
LLAvatarBoneInfo::child_list_t::const_iterator iter;
for (iter = bone_info->mChildList.begin(); iter != bone_info->mChildList.end(); ++iter)
{
@@ -1738,13 +1738,34 @@ const LLAvatarAppearance::joint_alias_map_t& LLAvatarAppearance::getJointAliases
{
LLAvatarSkeletonInfo::bone_info_list_t::const_iterator iter;
- for (iter = sAvatarSkeletonInfo->mBoneInfoList.begin(); iter != sAvatarSkeletonInfo->mBoneInfoList.end(); ++iter)
+ for (iter = sAvatarSkeletonInfo->mBoneInfoList.begin();
+ iter != sAvatarSkeletonInfo->mBoneInfoList.end();
+ ++iter)
{
//LLAvatarBoneInfo *bone_info = *iter;
makeJointAliases( *iter );
}
+
+ LLAvatarXmlInfo::attachment_info_list_t::iterator attach_iter;
+ for (attach_iter = sAvatarXmlInfo->mAttachmentInfoList.begin();
+ attach_iter != sAvatarXmlInfo->mAttachmentInfoList.end();
+ ++attach_iter)
+ {
+ LLAvatarXmlInfo::LLAvatarAttachmentInfo *info = *attach_iter;
+ std::string bone_name = info->mName;
+
+ // Also accept the name with spaces substituted with
+ // underscores. This gives a mechanism for referencing such joints
+ // in daes, which don't allow spaces.
+ std::string sub_space_to_underscore = bone_name;
+ LLStringUtil::replaceChar(sub_space_to_underscore, ' ', '_');
+ if (sub_space_to_underscore != bone_name)
+ {
+ mJointAliasMap[sub_space_to_underscore] = bone_name;
+ }
+ }
}
-
+
return mJointAliasMap;
}
diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h
index ccd6323ed8..b6bdb652e8 100644
--- a/indra/llappearance/llavatarappearance.h
+++ b/indra/llappearance/llavatarappearance.h
@@ -227,7 +227,7 @@ protected:
** RENDERING
**/
public:
- BOOL mIsDummy; // for special views
+ BOOL mIsDummy; // for special views and animated object controllers; local to viewer
//--------------------------------------------------------------------
// Morph masks
diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
index a3d5679f65..89335a20f5 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -881,7 +881,7 @@ void LLJoint::setWorldRotation( const LLQuaternion& rot )
//--------------------------------------------------------------------
const LLVector3& LLJoint::getScale()
{
- return mXform.getScale();
+ return mXform.getScale();
}
//--------------------------------------------------------------------
diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp
index 35e76f1d9d..3116403616 100644
--- a/indra/llcharacter/llmotioncontroller.cpp
+++ b/indra/llcharacter/llmotioncontroller.cpp
@@ -135,7 +135,7 @@ LLMotionController::LLMotionController()
mLastTime(0.0f),
mHasRunOnce(FALSE),
mPaused(FALSE),
- mPauseTime(0.f),
+ mPausedFrame(0),
mTimeStep(0.f),
mTimeStepCount(0),
mLastInterp(0.f),
@@ -814,6 +814,10 @@ void LLMotionController::updateLoadingMotions()
//-----------------------------------------------------------------------------
void LLMotionController::updateMotions(bool force_update)
{
+ // SL-763: "Distant animated objects run at super fast speed"
+ // The use_quantum optimization or possibly the associated code in setTimeStamp()
+ // does not work as implemented.
+ // Currently setting mTimeStep to nonzero is disabled elsewhere.
BOOL use_quantum = (mTimeStep != 0.f);
// Always update mPrevTimerElapsed
@@ -1125,6 +1129,7 @@ void LLMotionController::pauseAllMotions()
{
//LL_INFOS() << "Pausing animations..." << LL_ENDL;
mPaused = TRUE;
+ mPausedFrame = LLFrameTimer::getFrameCount();
}
}
diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h
index 9d9c64f4f0..637ee4d2bb 100644
--- a/indra/llcharacter/llmotioncontroller.h
+++ b/indra/llcharacter/llmotioncontroller.h
@@ -148,12 +148,16 @@ public:
void pauseAllMotions();
void unpauseAllMotions();
BOOL isPaused() const { return mPaused; }
+ S32 getPausedFrame() const { return mPausedFrame; }
void setTimeStep(F32 step);
+ F32 getTimeStep() const { return mTimeStep; }
void setTimeFactor(F32 time_factor);
F32 getTimeFactor() const { return mTimeFactor; }
+ F32 getAnimTime() const { return mAnimTime; }
+
motion_list_t& getActiveMotions() { return mActiveMotions; }
void incMotionCounts(S32& num_motions, S32& num_loading_motions, S32& num_loaded_motions, S32& num_active_motions, S32& num_deprecated_motions);
@@ -218,7 +222,7 @@ protected:
F32 mLastTime;
BOOL mHasRunOnce;
BOOL mPaused;
- F32 mPauseTime;
+ S32 mPausedFrame;
F32 mTimeStep;
S32 mTimeStepCount;
F32 mLastInterp;
diff --git a/indra/llcommon/llcallstack.h b/indra/llcommon/llcallstack.h
index 1f7a7689d7..5acf04a49f 100644
--- a/indra/llcommon/llcallstack.h
+++ b/indra/llcommon/llcallstack.h
@@ -78,3 +78,10 @@ struct LLContextStatus
};
LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLContextStatus& context_status);
+
+#define dumpStack(tag) \
+ if (debugLoggingEnabled(tag)) \
+ { \
+ LLCallStack cs; \
+ LL_DEBUGS(tag) << "STACK:\n" << "====================\n" << cs << "====================" << LL_ENDL; \
+ }
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index c45db3b185..9a02fecd72 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -869,6 +869,25 @@ std::string LLStringOps::getDatetimeCode (std::string key)
}
}
+std::string LLStringOps::getReadableNumber(F64 num)
+{
+ if (fabs(num)>=1e9)
+ {
+ return llformat("%.2lfB", num / 1e9);
+ }
+ else if (fabs(num)>=1e6)
+ {
+ return llformat("%.2lfM", num / 1e6);
+ }
+ else if (fabs(num)>=1e3)
+ {
+ return llformat("%.2lfK", num / 1e3);
+ }
+ else
+ {
+ return llformat("%.2lf", num);
+ }
+}
namespace LLStringFn
{
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index a4a5b393cb..68ee9db46b 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -211,6 +211,9 @@ public:
static bool getPacificDaylightTime(void) { return sPacificDaylightTime;}
static std::string getDatetimeCode (std::string key);
+
+ // Express a value like 1234567 as "1.23M"
+ static std::string getReadableNumber(F64 num);
};
/**
diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h
index 216334752a..baa3a89176 100644
--- a/indra/llmath/llmatrix4a.h
+++ b/indra/llmath/llmatrix4a.h
@@ -176,4 +176,10 @@ inline void matMul(const LLMatrix4a &a, const LLMatrix4a &b, LLMatrix4a &res)
res.mMatrix[3] = row3;
}
+inline std::ostream& operator<<(std::ostream& s, const LLMatrix4a& m)
+{
+ s << "[" << m.mMatrix[0] << ", " << m.mMatrix[1] << ", " << m.mMatrix[2] << ", " << m.mMatrix[3] << "]";
+ return s;
+}
+
#endif
diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h
index 79d0a44551..222f3cf235 100644
--- a/indra/llmath/llvector4a.h
+++ b/indra/llmath/llvector4a.h
@@ -320,7 +320,7 @@ public:
inline const LLVector4a& operator= ( const LLQuad& rhs );
inline operator LLQuad() const;
-
+
private:
LLQuad mQ;
} LL_ALIGN_POSTFIX(16);
@@ -331,4 +331,9 @@ inline void update_min_max(LLVector4a& min, LLVector4a& max, const LLVector4a& p
max.setMax(max, p);
}
+inline std::ostream& operator<<(std::ostream& s, const LLVector4a& v)
+{
+ s << "(" << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << ")";
+ return s;
+}
#endif
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 24f46d720b..44c10ae394 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -1,5 +1,4 @@
/**
-
* @file llvolume.cpp
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
@@ -2638,6 +2637,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
}
//calculate bounding box
+ // VFExtents change
LLVector4a& min = face.mExtents[0];
LLVector4a& max = face.mExtents[1];
@@ -4733,6 +4733,7 @@ LLVolumeFace::~LLVolumeFace()
{
ll_aligned_free_16(mExtents);
mExtents = NULL;
+ mCenter = NULL;
freeData();
}
@@ -5535,7 +5536,7 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
// S32 i;
S32 grid_size = (profile.size()-1)/4;
-
+ // VFExtents change
LLVector4a& min = mExtents[0];
LLVector4a& max = mExtents[1];
@@ -5812,7 +5813,7 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
LLVector2 cuv;
LLVector2 min_uv, max_uv;
-
+ // VFExtents change
LLVector4a& min = mExtents[0];
LLVector4a& max = mExtents[1];
@@ -6437,14 +6438,17 @@ void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat_in, LLMat
if (offset == 0 && i == 0)
{ //initialize bounding box
+ // VFExtents change
mExtents[0] = mExtents[1] = dst_pos[i];
}
else
{
//stretch bounding box
+ // VFExtents change
update_min_max(mExtents[0], mExtents[1], dst_pos[i]);
}
}
+ LL_DEBUGS("RiggedBox") << "appendFace got extents " << mExtents[0] << ", " << mExtents[1] << " from dst_pos " << LL_ENDL;
new_count = mNumIndices + face.mNumIndices;
@@ -6607,7 +6611,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
{
update_min_max(face_min, face_max, *cur_pos++);
}
-
+ // VFExtents change
mExtents[0] = face_min;
mExtents[1] = face_max;
diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp
index 1ae8a6ac15..f8e11e324e 100644
--- a/indra/llmessage/message_prehash.cpp
+++ b/indra/llmessage/message_prehash.cpp
@@ -818,6 +818,7 @@ char const* const _PREHASH_StateSave = LLMessageStringTable::getInstance()->getS
char const* const _PREHASH_RoleData = LLMessageStringTable::getInstance()->getString("RoleData");
char const* const _PREHASH_AgentAnimation = LLMessageStringTable::getInstance()->getString("AgentAnimation");
char const* const _PREHASH_AvatarAnimation = LLMessageStringTable::getInstance()->getString("AvatarAnimation");
+char const* const _PREHASH_ObjectAnimation = LLMessageStringTable::getInstance()->getString("ObjectAnimation");
char const* const _PREHASH_LogDwellTime = LLMessageStringTable::getInstance()->getString("LogDwellTime");
char const* const _PREHASH_ParcelGodMarkAsContent = LLMessageStringTable::getInstance()->getString("ParcelGodMarkAsContent");
char const* const _PREHASH_UsePhysics = LLMessageStringTable::getInstance()->getString("UsePhysics");
diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h
index 7910fde305..334236fb25 100644
--- a/indra/llmessage/message_prehash.h
+++ b/indra/llmessage/message_prehash.h
@@ -818,6 +818,7 @@ extern char const* const _PREHASH_StateSave;
extern char const* const _PREHASH_RoleData;
extern char const* const _PREHASH_AgentAnimation;
extern char const* const _PREHASH_AvatarAnimation;
+extern char const* const _PREHASH_ObjectAnimation;
extern char const* const _PREHASH_LogDwellTime;
extern char const* const _PREHASH_ParcelGodMarkAsContent;
extern char const* const _PREHASH_UsePhysics;
diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp
index 8401cb976e..8f75d89e5a 100644
--- a/indra/llprimitive/lldaeloader.cpp
+++ b/indra/llprimitive/lldaeloader.cpp
@@ -192,7 +192,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
{
return LLModel::BAD_ELEMENT;
}
-
+ // VFExtents change
face.mExtents[0].set(v[0], v[1], v[2]);
face.mExtents[1].set(v[0], v[1], v[2]);
}
@@ -254,6 +254,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
if (!found)
{
+ // VFExtents change
update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
verts.push_back(cv);
if (verts.size() >= 65535)
@@ -305,6 +306,7 @@ LLModel::EModelStatus load_face_from_dom_triangles(std::vector<LLVolumeFace>& fa
}
face = LLVolumeFace();
+ // VFExtents change
face.mExtents[0].set(v[0], v[1], v[2]);
face.mExtents[1].set(v[0], v[1], v[2]);
point_map.clear();
@@ -383,6 +385,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
if (pos_source)
{
v = pos_source->getFloat_array()->getValue();
+ // VFExtents change
face.mExtents[0].set(v[0], v[1], v[2]);
face.mExtents[1].set(v[0], v[1], v[2]);
}
@@ -482,6 +485,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
if (!found)
{
+ // VFExtents change
update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition());
verts.push_back(cv);
if (verts.size() >= 65535)
@@ -551,6 +555,7 @@ LLModel::EModelStatus load_face_from_dom_polylist(std::vector<LLVolumeFace>& fac
}
face = LLVolumeFace();
+ // VFExtents change
face.mExtents[0].set(v[0], v[1], v[2]);
face.mExtents[1].set(v[0], v[1], v[2]);
verts.clear();
@@ -734,7 +739,7 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac
{
return LLModel::NO_ERRORS;
}
-
+ // VFExtents change
face.mExtents[0] = verts[0].getPosition();
face.mExtents[1] = verts[0].getPosition();
@@ -758,6 +763,7 @@ LLModel::EModelStatus load_face_from_dom_polygons(std::vector<LLVolumeFace>& fac
for (std::map<LLVolumeFace::VertexData, U32>::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter)
{
new_verts[iter->second] = iter->first;
+ // VFExtents change
update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition());
}
diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index 8fbb4f6b96..18f45d3b20 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -276,6 +276,7 @@ void LLModel::normalizeVolumeFaces()
// We shrink the extents so
// that they fall within
// the unit cube.
+ // VFExtents change
face.mExtents[0].add(trans);
face.mExtents[0].mul(scale);
diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp
index bfa65666b5..c847cf653f 100644
--- a/indra/llprimitive/llprimitive.cpp
+++ b/indra/llprimitive/llprimitive.cpp
@@ -1599,6 +1599,8 @@ BOOL LLNetworkData::isValid(U16 param_type, U32 size)
return (size == 17);
case PARAMS_LIGHT_IMAGE:
return (size == 28);
+ case PARAMS_EXTENDED_MESH:
+ return (size == 4);
}
return FALSE;
@@ -2026,3 +2028,67 @@ bool LLLightImageParams::fromLLSD(LLSD& sd)
return false;
}
+
+//============================================================================
+
+LLExtendedMeshParams::LLExtendedMeshParams()
+{
+ mType = PARAMS_EXTENDED_MESH;
+ mFlags = 0;
+}
+
+BOOL LLExtendedMeshParams::pack(LLDataPacker &dp) const
+{
+ dp.packU32(mFlags, "flags");
+
+ return TRUE;
+}
+
+BOOL LLExtendedMeshParams::unpack(LLDataPacker &dp)
+{
+ dp.unpackU32(mFlags, "flags");
+
+ return TRUE;
+}
+
+bool LLExtendedMeshParams::operator==(const LLNetworkData& data) const
+{
+ if (data.mType != PARAMS_EXTENDED_MESH)
+ {
+ return false;
+ }
+
+ const LLExtendedMeshParams *param = (const LLExtendedMeshParams*)&data;
+ if ( (param->mFlags != mFlags) )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void LLExtendedMeshParams::copy(const LLNetworkData& data)
+{
+ const LLExtendedMeshParams *param = (LLExtendedMeshParams*)&data;
+ mFlags = param->mFlags;
+}
+
+LLSD LLExtendedMeshParams::asLLSD() const
+{
+ LLSD sd;
+
+ sd["flags"] = LLSD::Integer(mFlags);
+
+ return sd;
+}
+
+bool LLExtendedMeshParams::fromLLSD(LLSD& sd)
+{
+ if (sd.has("flags"))
+ {
+ setFlags( sd["flags"].asInteger());
+ return true;
+ }
+
+ return false;
+}
diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h
index 19d9d52817..9216c04229 100644
--- a/indra/llprimitive/llprimitive.h
+++ b/indra/llprimitive/llprimitive.h
@@ -106,6 +106,7 @@ public:
PARAMS_LIGHT_IMAGE = 0x40,
PARAMS_RESERVED = 0x50, // Used on server-side
PARAMS_MESH = 0x60,
+ PARAMS_EXTENDED_MESH = 0x70,
};
public:
@@ -288,6 +289,27 @@ public:
};
+class LLExtendedMeshParams : public LLNetworkData
+{
+protected:
+ U32 mFlags;
+
+public:
+ static const U32 ANIMATED_MESH_ENABLED_FLAG = 0x1 << 0;
+
+ LLExtendedMeshParams();
+ /*virtual*/ BOOL pack(LLDataPacker &dp) const;
+ /*virtual*/ BOOL unpack(LLDataPacker &dp);
+ /*virtual*/ bool operator==(const LLNetworkData& data) const;
+ /*virtual*/ void copy(const LLNetworkData& data);
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ void setFlags(const U32& flags) { mFlags = flags; }
+ U32 getFlags() const { return mFlags; }
+
+};
// This code is not naming-standards compliant. Leaving it like this for
// now to make the connection to code in
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 6b16713add..683c9d6821 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -151,6 +151,7 @@ set(viewer_SOURCE_FILES
llcommunicationchannel.cpp
llcompilequeue.cpp
llconfirmationmanager.cpp
+ llcontrolavatar.cpp
llconversationlog.cpp
llconversationloglist.cpp
llconversationloglistitem.cpp
@@ -602,6 +603,7 @@ set(viewer_SOURCE_FILES
lltransientfloatermgr.cpp
lltranslate.cpp
lltwitterconnect.cpp
+ lluiavatar.cpp
lluilistener.cpp
lluploaddialog.cpp
llurl.cpp
@@ -771,6 +773,7 @@ set(viewer_HEADER_FILES
llcommunicationchannel.h
llcompilequeue.h
llconfirmationmanager.h
+ llcontrolavatar.h
llconversationlog.h
llconversationloglist.h
llconversationloglistitem.h
@@ -1216,6 +1219,7 @@ set(viewer_HEADER_FILES
lltranslate.h
lltwitterconnect.h
lluiconstants.h
+ lluiavatar.h
lluilistener.h
lluploaddialog.h
lluploadfloaterobservers.h
diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml
index ecd7c4bc36..ae57e125bb 100644
--- a/indra/newview/app_settings/logcontrol.xml
+++ b/indra/newview/app_settings/logcontrol.xml
@@ -51,6 +51,7 @@
<key>tags</key>
<array>
<!-- sample entry for debugging specific items
+ <string>AnimatedObjects</string>
<string>Avatar</string>
<string>Inventory</string>
<string>SceneLoadTiming</string>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index dd07972249..7c020063f3 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -2182,6 +2182,39 @@
<key>Value</key>
<string />
</map>
+ <key>DebugAnimatedObjects</key>
+ <map>
+ <key>Comment</key>
+ <string>Show info related to animated objects</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>AnimatedObjectsIgnoreLimits</key>
+ <map>
+ <key>Comment</key>
+ <string>Ignore server-enforced limits on animated objects. This is only useful for server testing.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>AnimatedObjectsAllowLeftClick</key>
+ <map>
+ <key>Comment</key>
+ <string>Allow left-click interaction with animated objects. Uncertain how much performance impact this will have.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>DebugAvatarAppearanceMessage</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 0fb811a386..9f13a944ac 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -880,7 +880,10 @@ void LLWearableHoldingPattern::onAllComplete()
++it)
{
LLViewerObject *objectp = *it;
- gAgentAvatarp->addAttachmentOverridesForObject(objectp);
+ if (!objectp->isAnimatedObject())
+ {
+ gAgentAvatarp->addAttachmentOverridesForObject(objectp);
+ }
}
// Add new attachments to match those requested.
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 464e216cf0..190609bc7a 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1109,7 +1109,10 @@ bool LLAppViewer::init()
// situation to do things the Right Way. Anyone who intentionally
// bypasses this mechanism needs no reminder that s/he's shooting
// him/herself in the foot.
- LLNotificationsUtil::add("RunLauncher");
+ if (!beingDebugged())
+ {
+ LLNotificationsUtil::add("RunLauncher");
+ }
}
#if LL_WINDOWS
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 48b3a1c485..de764ae300 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -646,6 +646,11 @@ bool LLAppViewerWin32::initParseCommandLine(LLCommandLineParser& clp)
return true;
}
+bool LLAppViewerWin32::beingDebugged()
+{
+ return IsDebuggerPresent();
+}
+
bool LLAppViewerWin32::restoreErrorTrap()
{
return true;
diff --git a/indra/newview/llappviewerwin32.h b/indra/newview/llappviewerwin32.h
index 59d1ddaa3d..c5fae6a3a3 100644
--- a/indra/newview/llappviewerwin32.h
+++ b/indra/newview/llappviewerwin32.h
@@ -49,6 +49,7 @@ protected:
virtual bool initHardwareTest(); // Win32 uses DX9 to test hardware.
virtual bool initParseCommandLine(LLCommandLineParser& clp);
+ virtual bool beingDebugged();
virtual bool restoreErrorTrap();
virtual void initCrashReporting(bool reportFreeze);
diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp
new file mode 100644
index 0000000000..e1715d567b
--- /dev/null
+++ b/indra/newview/llcontrolavatar.cpp
@@ -0,0 +1,429 @@
+/**
+ * @file llcontrolavatar.cpp
+ * @brief Implementation for special dummy avatar used to drive rigged meshes.
+ *
+ * $LicenseInfo:firstyear=2017&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2017, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llcontrolavatar.h"
+#include "llagent.h" // Get state values from here
+#include "llviewerobjectlist.h"
+#include "pipeline.h"
+#include "llanimationstates.h"
+#include "llviewercontrol.h"
+#include "llmeshrepository.h"
+#include "llviewerregion.h"
+
+LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) :
+ LLVOAvatar(id, pcode, regionp),
+ mPlaying(false),
+ mGlobalScale(1.0f),
+ mMarkedForDeath(false)
+{
+ mIsDummy = TRUE;
+ mIsControlAvatar = true;
+ mEnableDefaultMotions = false;
+}
+
+// virtual
+LLControlAvatar::~LLControlAvatar()
+{
+}
+
+// virtual
+void LLControlAvatar::initInstance()
+{
+ // Potential optimizations here: avoid creating system
+ // avatar mesh content since it's not used. For now we just clean some
+ // things up after the fact in releaseMeshData().
+ LLVOAvatar::initInstance();
+
+ createDrawable(&gPipeline);
+ updateJointLODs();
+ updateGeometry(mDrawable);
+ hideSkirt();
+}
+
+void LLControlAvatar::matchVolumeTransform()
+{
+ if (mRootVolp)
+ {
+ if (mRootVolp->isAttachment())
+ {
+ LLVOAvatar *attached_av = mRootVolp->getAvatarAncestor();
+ if (attached_av)
+ {
+ LLViewerJointAttachment *attach = attached_av->getTargetAttachmentPoint(mRootVolp);
+ setPositionAgent(mRootVolp->getRenderPosition());
+ attach->updateWorldPRSParent();
+ LLVector3 joint_pos = attach->getWorldPosition();
+ LLQuaternion joint_rot = attach->getWorldRotation();
+ LLVector3 obj_pos = mRootVolp->mDrawable->getPosition();
+ LLQuaternion obj_rot = mRootVolp->mDrawable->getRotation();
+ obj_pos.rotVec(joint_rot);
+ mRoot->setWorldPosition(obj_pos + joint_pos);
+ mRoot->setWorldRotation(obj_rot * joint_rot);
+ setRotation(mRoot->getRotation());
+ }
+ else
+ {
+ LL_WARNS_ONCE() << "can't find attached av!" << LL_ENDL;
+ }
+ }
+ else
+ {
+ setPositionAgent(mRootVolp->getRenderPosition());
+ LLQuaternion obj_rot = mRootVolp->getRotation();
+ LLQuaternion result_rot = obj_rot;
+ setRotation(result_rot);
+ mRoot->setWorldRotation(result_rot);
+ mRoot->setPosition(mRootVolp->getRenderPosition());
+ }
+ }
+}
+
+void LLControlAvatar::setGlobalScale(F32 scale)
+{
+ if (scale <= 0.0)
+ {
+ LL_WARNS() << "invalid global scale " << scale << LL_ENDL;
+ return;
+ }
+ if (scale != mGlobalScale)
+ {
+ F32 adjust_scale = scale/mGlobalScale;
+ LL_INFOS() << "scale " << scale << " adjustment " << adjust_scale << LL_ENDL;
+ // should we be scaling from the pelvis or the root?
+ recursiveScaleJoint(mPelvisp,adjust_scale);
+ mGlobalScale = scale;
+ }
+}
+
+void LLControlAvatar::recursiveScaleJoint(LLJoint* joint, F32 factor)
+{
+ joint->setScale(factor * joint->getScale());
+
+ for (LLJoint::child_list_t::iterator iter = joint->mChildren.begin();
+ iter != joint->mChildren.end(); ++iter)
+ {
+ LLJoint* child = *iter;
+ recursiveScaleJoint(child, factor);
+ }
+}
+
+// Based on LLViewerJointAttachment::setupDrawable(), without the attaching part.
+void LLControlAvatar::updateVolumeGeom()
+{
+ if (!mRootVolp->mDrawable)
+ return;
+ if (mRootVolp->mDrawable->isActive())
+ {
+ mRootVolp->mDrawable->makeStatic(FALSE);
+ }
+ mRootVolp->mDrawable->makeActive();
+ gPipeline.markMoved(mRootVolp->mDrawable);
+ gPipeline.markTextured(mRootVolp->mDrawable); // face may need to change draw pool to/from POOL_HUD
+ mRootVolp->mDrawable->setState(LLDrawable::USE_BACKLIGHT);
+
+ LLViewerObject::const_child_list_t& child_list = mRootVolp->getChildren();
+ for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin();
+ iter != child_list.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mDrawable.notNull())
+ {
+ childp->mDrawable->setState(LLDrawable::USE_BACKLIGHT);
+ gPipeline.markTextured(childp->mDrawable); // face may need to change draw pool to/from POOL_HUD
+ gPipeline.markMoved(childp->mDrawable);
+ }
+ }
+
+ gPipeline.markRebuild(mRootVolp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ mRootVolp->markForUpdate(TRUE);
+
+ // Note that attachment overrides aren't needed here, have already
+ // been applied at the time the mControlAvatar was created, in
+ // llvovolume.cpp.
+
+ matchVolumeTransform();
+
+ // Initial exploration of allowing scaling skeleton to match root
+ // prim bounding box. If enabled, would probably be controlled by
+ // an additional checkbox and default to off. Not enabled for
+ // initial release.
+
+ // What should the scale be? What we really want is the ratio
+ // between the scale at which the object was originally designed
+ // and rigged, and the scale to which it has been subsequently
+ // modified - for example, if the object has been scaled down by a
+ // factor of 2 then we should use 0.5 as the global scale. But we
+ // don't have the original scale stored anywhere, just the current
+ // scale. Possibilities - 1) remember the original scale
+ // somewhere, 2) add another field to let the user specify the
+ // global scale, 3) approximate the original scale by looking at
+ // the proportions of the skeleton after joint positions have
+ // been applied
+
+ //LLVector3 obj_scale = obj->getScale();
+ //F32 obj_scale_z = llmax(obj_scale[2],0.1f);
+ //setGlobalScale(obj_scale_z/2.0f); // roughly fit avatar height range (2m) into object height
+}
+
+LLControlAvatar *LLControlAvatar::createControlAvatar(LLVOVolume *obj)
+{
+ LLControlAvatar *cav = (LLControlAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), CO_FLAG_CONTROL_AVATAR);
+
+ cav->mRootVolp = obj;
+
+ // Sync up position/rotation with object
+ cav->matchVolumeTransform();
+
+ return cav;
+}
+
+void LLControlAvatar::markForDeath()
+{
+ mMarkedForDeath = true;
+}
+
+void LLControlAvatar::idleUpdate(LLAgent &agent, const F64 &time)
+{
+ if (mMarkedForDeath)
+ {
+ markDead();
+ mMarkedForDeath = false;
+ }
+ else
+ {
+ LLVOAvatar::idleUpdate(agent,time);
+ }
+}
+
+BOOL LLControlAvatar::updateCharacter(LLAgent &agent)
+{
+ return LLVOAvatar::updateCharacter(agent);
+}
+
+//virtual
+void LLControlAvatar::updateDebugText()
+{
+ if (gSavedSettings.getBOOL("DebugAnimatedObjects"))
+ {
+ S32 total_linkset_count = 0;
+ if (mRootVolp)
+ {
+ total_linkset_count = 1 + mRootVolp->getChildren().size();
+ }
+ std::vector<LLVOVolume*> volumes;
+ getAnimatedVolumes(volumes);
+ S32 animated_volume_count = volumes.size();
+ std::string active_string;
+ std::string type_string;
+ std::string lod_string;
+ S32 total_tris = 0;
+ S32 total_verts = 0;
+ S32 est_tris = 0;
+ for (std::vector<LLVOVolume*>::iterator it = volumes.begin();
+ it != volumes.end(); ++it)
+ {
+ LLVOVolume *volp = *it;
+ S32 verts = 0;
+ total_tris += volp->getTriangleCount(&verts);
+ total_verts += verts;
+ est_tris += volp->getEstTrianglesMax();
+ lod_string += llformat("%d",volp->getLOD());
+ if (volp && volp->mDrawable)
+ {
+ if (volp->mDrawable->isActive())
+ {
+ active_string += "A";
+ }
+ else
+ {
+ active_string += "S";
+ }
+ if (volp->isRiggedMesh())
+ {
+ // Rigged/animateable mesh
+ type_string += "R";
+ }
+ else if (volp->isMesh())
+ {
+ // Static mesh
+ type_string += "M";
+ }
+ else
+ {
+ // Any other prim
+ type_string += "P";
+ }
+ }
+ else
+ {
+ active_string += "-";
+ type_string += "-";
+ }
+ }
+ addDebugText(llformat("CAV obj %d anim %d active %s impost %d",
+ total_linkset_count, animated_volume_count, active_string.c_str(), (S32) isImpostor()));
+ addDebugText(llformat("types %s lods %s", type_string.c_str(), lod_string.c_str()));
+ addDebugText(llformat("tris %d (est %d), verts %d", total_tris, est_tris, total_verts));
+ addDebugText(llformat("pxarea %s", LLStringOps::getReadableNumber(getPixelArea()).c_str()));
+#if 0
+ std::string region_name = "no region";
+ if (mRootVolp->getRegion())
+ {
+ region_name = mRootVolp->getRegion()->getName();
+ }
+ std::string skel_region_name = "skel no region";
+ if (getRegion())
+ {
+ skel_region_name = getRegion()->getName();
+ }
+ addDebugText(llformat("region %x %s skel %x %s",
+ mRootVolp->getRegion(), region_name.c_str(),
+ getRegion(), skel_region_name.c_str()));
+#endif
+
+ }
+ LLVOAvatar::updateDebugText();
+}
+
+void LLControlAvatar::getAnimatedVolumes(std::vector<LLVOVolume*>& volumes)
+{
+ if (!mRootVolp)
+ {
+ return;
+ }
+
+ volumes.push_back(mRootVolp);
+
+ LLViewerObject::const_child_list_t& child_list = mRootVolp->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin();
+ iter != child_list.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ LLVOVolume *child_volp = dynamic_cast<LLVOVolume*>(childp);
+ if (child_volp && child_volp->isAnimatedObject())
+ {
+ volumes.push_back(child_volp);
+ }
+ }
+}
+
+// This is called after an associated object receives an animation
+// message. Combine the signaled animations for all associated objects
+// and process any resulting state changes.
+void LLControlAvatar::updateAnimations()
+{
+ if (!mRootVolp)
+ {
+ LL_WARNS_ONCE("AnimatedObjects") << "No root vol" << LL_ENDL;
+ return;
+ }
+
+ std::vector<LLVOVolume*> volumes;
+ getAnimatedVolumes(volumes);
+
+ // Rebuild mSignaledAnimations from the associated volumes.
+ std::map<LLUUID, S32> anims;
+ for (std::vector<LLVOVolume*>::iterator vol_it = volumes.begin(); vol_it != volumes.end(); ++vol_it)
+ {
+ LLVOVolume *volp = *vol_it;
+ for (std::map<LLUUID,S32>::iterator anim_it = volp->mObjectSignaledAnimations.begin();
+ anim_it != volp->mObjectSignaledAnimations.end();
+ ++anim_it)
+ {
+ std::map<LLUUID,S32>::iterator found_anim_it = anims.find(anim_it->first);
+ if (found_anim_it != anims.end())
+ {
+ // Animation already present, use the larger sequence id
+ anims[anim_it->first] = llmax(found_anim_it->second, anim_it->second);
+ }
+ else
+ {
+ // Animation not already present, use this sequence id.
+ anims[anim_it->first] = anim_it->second;
+ }
+ }
+ }
+ if (!mPlaying && anims.size()>0)
+ {
+ mPlaying = true;
+ if (!mRootVolp->isAnySelected())
+ {
+ updateVolumeGeom();
+ mRootVolp->recursiveMarkForUpdate(TRUE);
+ }
+ }
+
+ mSignaledAnimations = anims;
+ processAnimationStateChanges();
+}
+
+// virtual
+LLViewerObject* LLControlAvatar::lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end,
+ S32 face,
+ BOOL pick_transparent,
+ BOOL pick_rigged,
+ S32* face_hit,
+ LLVector4a* intersection,
+ LLVector2* tex_coord,
+ LLVector4a* normal,
+ LLVector4a* tangent)
+{
+ LLViewerObject* hit = NULL;
+
+ if (lineSegmentBoundingBox(start, end))
+ {
+ LLVector4a local_end = end;
+ LLVector4a local_intersection;
+
+ if (mRootVolp &&
+ mRootVolp->lineSegmentIntersect(start, local_end, face, pick_transparent, pick_rigged, face_hit, &local_intersection, tex_coord, normal, tangent))
+ {
+ local_end = local_intersection;
+ if (intersection)
+ {
+ *intersection = local_intersection;
+ }
+
+ hit = mRootVolp;
+ }
+ }
+
+ return hit;
+}
+
+// virtual
+std::string LLControlAvatar::getFullname() const
+{
+ if (mRootVolp)
+ {
+ return "AO_" + mRootVolp->getID().getString();
+ }
+ else
+ {
+ return "AO_no_root_vol";
+ }
+}
diff --git a/indra/newview/llcontrolavatar.h b/indra/newview/llcontrolavatar.h
new file mode 100644
index 0000000000..f45de25d81
--- /dev/null
+++ b/indra/newview/llcontrolavatar.h
@@ -0,0 +1,85 @@
+/**
+ * @file llcontrolavatar.h
+ * @brief Special dummy avatar used to drive rigged meshes.
+ *
+ * $LicenseInfo:firstyear=2017&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2017, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_CONTROLAVATAR_H
+#define LL_CONTROLAVATAR_H
+
+#include "llvoavatar.h"
+#include "llvovolume.h"
+
+class LLControlAvatar:
+ public LLVOAvatar
+{
+ LOG_CLASS(LLControlAvatar);
+
+public:
+ LLControlAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual void initInstance(); // Called after construction to initialize the class.
+ virtual ~LLControlAvatar();
+
+ void matchVolumeTransform();
+ void updateVolumeGeom();
+
+ void setGlobalScale(F32 scale);
+ void recursiveScaleJoint(LLJoint *joint, F32 factor);
+ static LLControlAvatar *createControlAvatar(LLVOVolume *obj);
+
+ // Delayed kill so we don't make graphics pipeline unhappy calling
+ // markDead() inside other graphics pipeline operations.
+ void markForDeath();
+
+ virtual void idleUpdate(LLAgent &agent, const F64 &time);
+ virtual BOOL updateCharacter(LLAgent &agent);
+
+ void getAnimatedVolumes(std::vector<LLVOVolume*>& volumes);
+ void updateAnimations();
+
+ virtual LLViewerObject* lineSegmentIntersectRiggedAttachments(
+ const LLVector4a& start, const LLVector4a& end,
+ S32 face = -1, // which face to check, -1 = ALL_SIDES
+ BOOL pick_transparent = FALSE,
+ BOOL pick_rigged = FALSE,
+ S32* face_hit = NULL, // which face was hit
+ LLVector4a* intersection = NULL, // return the intersection point
+ LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
+ LLVector4a* normal = NULL, // return the surface normal at the intersection point
+ LLVector4a* tangent = NULL); // return the surface tangent at the intersection point
+
+ virtual void updateDebugText();
+
+ virtual std::string getFullname() const;
+
+ bool mPlaying;
+
+ F32 mGlobalScale;
+
+ LLVOVolume *mRootVolp;
+
+ bool mMarkedForDeath;
+
+};
+
+#endif //LL_CONTROLAVATAR_H
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
index f956023358..7e51389655 100644
--- a/indra/newview/lldrawable.cpp
+++ b/indra/newview/lldrawable.cpp
@@ -50,6 +50,8 @@
#include "llviewerobjectlist.h"
#include "llviewerwindow.h"
#include "llvocache.h"
+#include "llcontrolavatar.h"
+#include "llcallstack.h"
const F32 MIN_INTERPOLATE_DISTANCE_SQUARED = 0.001f * 0.001f;
const F32 MAX_INTERPOLATE_DISTANCE_SQUARED = 10.f * 10.f;
@@ -550,7 +552,8 @@ void LLDrawable::makeStatic(BOOL warning_enabled)
if (isState(ACTIVE) &&
!isState(ACTIVE_CHILD) &&
!mVObjp->isAttachment() &&
- !mVObjp->isFlexible())
+ !mVObjp->isFlexible() &&
+ !mVObjp->isAnimatedObject())
{
clearState(ACTIVE | ANIMATED_CHILD);
@@ -1084,7 +1087,8 @@ void LLDrawable::setGroup(LLViewerOctreeGroup *groupp)
llassert(!groupp || (LLSpatialGroup*)groupp->hasElement(this));
if (cur_groupp != groupp && getVOVolume())
- { //NULL out vertex buffer references for volumes on spatial group change to maintain
+ {
+ //NULL out vertex buffer references for volumes on spatial group change to maintain
//requirement that every face vertex buffer is either NULL or points to a vertex buffer
//contained by its drawable's spatial group
for (S32 i = 0; i < getNumFaces(); ++i)
diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp
index f74164aea6..1ec8d97f36 100644
--- a/indra/newview/lldrawpool.cpp
+++ b/indra/newview/lldrawpool.cpp
@@ -140,7 +140,7 @@ LLViewerTexture *LLDrawPool::getDebugTexture()
return NULL;
}
-//virtual
+//virtuals
void LLDrawPool::beginRenderPass( S32 pass )
{
}
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index 8128790eb6..375704adff 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -109,6 +109,32 @@ LLDrawPoolAvatar::LLDrawPoolAvatar() :
{
}
+LLDrawPoolAvatar::~LLDrawPoolAvatar()
+{
+ if (!isDead())
+ {
+ LL_WARNS() << "Destroying avatar drawpool that still contains faces" << LL_ENDL;
+ }
+}
+
+// virtual
+BOOL LLDrawPoolAvatar::isDead()
+{
+ if (!LLFacePool::isDead())
+ {
+ return FALSE;
+ }
+
+ for (U32 i = 0; i < NUM_RIGGED_PASSES; ++i)
+ {
+ if (mRiggedFace[i].size() > 0)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
//-----------------------------------------------------------------------------
// instancePool()
//-----------------------------------------------------------------------------
@@ -467,7 +493,7 @@ void LLDrawPoolAvatar::renderShadow(S32 pass)
}
LLVOAvatar *avatarp = (LLVOAvatar *)facep->getDrawable()->getVObj().get();
- if (avatarp->isDead() || avatarp->mIsDummy || avatarp->mDrawable.isNull())
+ if (avatarp->isDead() || avatarp->isUIAvatar() || avatarp->mDrawable.isNull())
{
return;
}
@@ -1707,13 +1733,7 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
continue;
}
- LLUUID mesh_id = volume->getParams().getSculptID();
- if (mesh_id.isNull())
- {
- continue;
- }
-
- const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(mesh_id, vobj);
+ const LLMeshSkinInfo* skin = vobj->getSkinInfo();
if (!skin)
{
continue;
@@ -1927,13 +1947,7 @@ void LLDrawPoolAvatar::updateRiggedVertexBuffers(LLVOAvatar* avatar)
continue;
}
- LLUUID mesh_id = volume->getParams().getSculptID();
- if (mesh_id.isNull())
- {
- continue;
- }
-
- const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(mesh_id, vobj);
+ const LLMeshSkinInfo* skin = vobj->getSkinInfo();
if (!skin)
{
continue;
@@ -2054,11 +2068,16 @@ LLColor3 LLDrawPoolAvatar::getDebugColor() const
void LLDrawPoolAvatar::addRiggedFace(LLFace* facep, U32 type)
{
+ llassert (facep->isState(LLFace::RIGGED));
+ llassert(getType() == LLDrawPool::POOL_AVATAR);
+ if (facep->getPool() && facep->getPool() != this)
+ {
+ LL_ERRS() << "adding rigged face that's already in another pool" << LL_ENDL;
+ }
if (type >= NUM_RIGGED_PASSES)
{
LL_ERRS() << "Invalid rigged face type." << LL_ENDL;
}
-
if (facep->getRiggedIndex(type) != -1)
{
LL_ERRS() << "Tried to add a rigged face that's referenced elsewhere." << LL_ENDL;
@@ -2071,6 +2090,12 @@ void LLDrawPoolAvatar::addRiggedFace(LLFace* facep, U32 type)
void LLDrawPoolAvatar::removeRiggedFace(LLFace* facep)
{
+ llassert (facep->isState(LLFace::RIGGED));
+ llassert(getType() == LLDrawPool::POOL_AVATAR);
+ if (facep->getPool() != this)
+ {
+ LL_ERRS() << "Tried to remove a rigged face from the wrong pool" << LL_ENDL;
+ }
facep->setPool(NULL);
for (U32 i = 0; i < NUM_RIGGED_PASSES; ++i)
diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h
index b9d2204052..45b6d71110 100644
--- a/indra/newview/lldrawpoolavatar.h
+++ b/indra/newview/lldrawpoolavatar.h
@@ -60,6 +60,8 @@ public:
virtual S32 getVertexShaderLevel() const;
LLDrawPoolAvatar();
+ ~LLDrawPoolAvatar();
+ /*virtual*/ BOOL isDead();
static LLMatrix4& getModelView();
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 50a4925c37..efd57ec39f 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -330,11 +330,7 @@ void LLFace::dirtyTexture()
{
vobj->mLODChanged = TRUE;
- LLVOAvatar* avatar = vobj->getAvatar();
- if (avatar)
- { //avatar render cost may have changed
- avatar->updateVisualComplexity();
- }
+ vobj->updateVisualComplexity();
}
gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_VOLUME, FALSE);
}
@@ -835,6 +831,9 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
min = face.mExtents[0];
max = face.mExtents[1];
+ LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " starting extents " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;
+ LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " starting vf extents " << face.mExtents[0] << ", " << face.mExtents[1] << " num verts " << face.mNumVertices << LL_ENDL;
+
llassert(less_than_max_mag(min));
llassert(less_than_max_mag(max));
@@ -863,6 +862,14 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
v[6] = min;
v[7] = max;
+ // MAINT-8264 - stray vertices, especially in low LODs, cause bounding box errors.
+ if (face.mNumVertices < 3)
+ {
+ LL_DEBUGS("RiggedBox") << "skipping face " << f << ", bad num vertices "
+ << face.mNumVertices << " " << face.mNumIndices << " " << face.mWeights << LL_ENDL;
+ return FALSE;
+ }
+
for (U32 i = 0; i < 6; ++i)
{
v[i].setSelectWithMask(mask[i], min, max);
@@ -870,10 +877,13 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
LLVector4a tv[8];
+ LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " mat is " << mat_vert << LL_ENDL;
+
//transform bounding box into drawable space
for (U32 i = 0; i < 8; ++i)
{
mat_vert.affineTransform(v[i], tv[i]);
+ LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " i " << i << " v and tv " << v[i] << ", " << tv[i] << LL_ENDL;
}
//find bounding box
@@ -887,6 +897,7 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
newMin.setMin(newMin, tv[i]);
newMax.setMax(newMax, tv[i]);
}
+ LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " bbox gave extents " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;
if (!mDrawablep->isActive())
{ // Shift position for region
@@ -894,8 +905,10 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
offset.load3(mDrawablep->getRegion()->getOriginAgent().mV);
newMin.add(offset);
newMax.add(offset);
+ LL_DEBUGS("RiggedBox") << "updating extents for face " << f << " not active, added offset " << offset << LL_ENDL;
}
+ LL_DEBUGS("RiggedBox") << "updated extents for face " << f << " to " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;
LLVector4a t;
t.setAdd(newMin,newMax);
t.mul(0.5f);
diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp
index ae7620f2c7..080d0ed8ea 100644
--- a/indra/newview/llfloaterbvhpreview.cpp
+++ b/indra/newview/llfloaterbvhpreview.cpp
@@ -1042,14 +1042,8 @@ LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLViewerDynamicT
mCameraPitch = 0.f;
mCameraZoom = 1.f;
- mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion());
- mDummyAvatar->createDrawable(&gPipeline);
- mDummyAvatar->mIsDummy = TRUE;
+ mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR);
mDummyAvatar->mSpecialRenderMode = 1;
- mDummyAvatar->setPositionAgent(LLVector3::zero);
- mDummyAvatar->slamPosition();
- mDummyAvatar->updateJointLODs();
- mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
mDummyAvatar->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET);
mDummyAvatar->hideSkirt();
diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp
index a9e4d752ac..fbe78475df 100644
--- a/indra/newview/llfloaterimagepreview.cpp
+++ b/indra/newview/llfloaterimagepreview.cpp
@@ -566,15 +566,8 @@ LLImagePreviewAvatar::LLImagePreviewAvatar(S32 width, S32 height) : LLViewerDyna
mCameraPitch = 0.f;
mCameraZoom = 1.f;
- mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion());
- mDummyAvatar->createDrawable(&gPipeline);
- mDummyAvatar->mIsDummy = TRUE;
+ mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR);
mDummyAvatar->mSpecialRenderMode = 2;
- mDummyAvatar->setPositionAgent(LLVector3::zero);
- mDummyAvatar->slamPosition();
- mDummyAvatar->updateJointLODs();
- mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
- // gPipeline.markVisible(mDummyAvatar->mDrawable, *LLViewerCamera::getInstance());
mTextureName = 0;
}
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index da84a6b8f8..168fb13d11 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1755,9 +1755,17 @@ void LLModelPreview::getJointAliases( JointMap& joint_map)
//Joint names and aliases come from avatar_skeleton.xml
joint_map = av->getJointAliases();
- for (S32 i = 0; i < av->mNumCollisionVolumes; i++)
+
+ std::vector<std::string> cv_names, attach_names;
+ av->getSortedJointNames(1, cv_names);
+ av->getSortedJointNames(2, attach_names);
+ for (std::vector<std::string>::iterator it = cv_names.begin(); it != cv_names.end(); ++it)
+ {
+ joint_map[*it] = *it;
+ }
+ for (std::vector<std::string>::iterator it = attach_names.begin(); it != attach_names.end(); ++it)
{
- joint_map[av->mCollisionVolumes[i].getName()] = av->mCollisionVolumes[i].getName();
+ joint_map[*it] = *it;
}
}
@@ -3443,16 +3451,11 @@ void LLModelPreview::update()
//-----------------------------------------------------------------------------
void LLModelPreview::createPreviewAvatar( void )
{
- mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer( LL_PCODE_LEGACY_AVATAR, gAgent.getRegion() );
+ mPreviewAvatar = (LLVOAvatar*)gObjectList.createObjectViewer( LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR );
if ( mPreviewAvatar )
{
mPreviewAvatar->createDrawable( &gPipeline );
- mPreviewAvatar->mIsDummy = TRUE;
mPreviewAvatar->mSpecialRenderMode = 1;
- mPreviewAvatar->setPositionAgent( LLVector3::zero );
- mPreviewAvatar->slamPosition();
- mPreviewAvatar->updateJointLODs();
- mPreviewAvatar->updateGeometry( mPreviewAvatar->mDrawable );
mPreviewAvatar->startMotion( ANIM_AGENT_STAND );
mPreviewAvatar->hideSkirt();
}
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index fdaa28b22b..4f0fb56ba6 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -4062,6 +4062,38 @@ void LLMeshRepository::uploadError(LLSD& args)
mUploadErrorQ.push(args);
}
+F32 LLMeshRepository::getEstTrianglesMax(LLUUID mesh_id)
+{
+ F32 triangles_max = 0.f;
+ if (mThread && mesh_id.notNull())
+ {
+ LLMutexLock lock(mThread->mHeaderMutex);
+ LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
+ if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0)
+ {
+ LLSD& header = iter->second;
+ if (header.has("404")
+ || !header.has("lowest_lod")
+ || (header.has("version") && header["version"].asInteger() > MAX_MESH_VERSION))
+ {
+ return 0.f;
+ }
+
+ S32 bytes_high = header["high_lod"]["size"].asInteger();
+ S32 bytes_med = header["medium_lod"]["size"].asInteger();
+ S32 bytes_low = header["low_lod"]["size"].asInteger();
+ S32 bytes_lowest = header["lowest_lod"]["size"].asInteger();
+ S32 bytes_max = llmax(bytes_high, bytes_med, bytes_low, bytes_lowest);
+
+ F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount"); //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead
+ F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize"); //make sure nothing is "free"
+ F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle");
+ triangles_max = llmax((F32) bytes_max-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
+ }
+ }
+ return triangles_max;
+}
+
F32 LLMeshRepository::getStreamingCost(LLUUID mesh_id, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value)
{
if (mThread && mesh_id.notNull())
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 23af837f6f..d2eac449f7 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -472,6 +472,8 @@ public:
static LLDeadmanTimer sQuiescentTimer; // Time-to-complete-mesh-downloads after significant events
+ // Estimated triangle count of the largest LOD
+ F32 getEstTrianglesMax(LLUUID mesh_id);
F32 getStreamingCost(LLUUID mesh_id, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
index 0bf4d48421..cd00a6f98f 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -342,9 +342,19 @@ void LLPanelObject::getState( )
}
// can move or rotate only linked group with move permissions, or sub-object with move and modify perms
- BOOL enable_move = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && !objectp->isAttachment() && (objectp->permModify() || !gSavedSettings.getBOOL("EditLinkedParts"));
- BOOL enable_scale = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && objectp->permModify();
- BOOL enable_rotate = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && ( (objectp->permModify() && !objectp->isAttachment()) || !gSavedSettings.getBOOL("EditLinkedParts"));
+
+ // AXON REVIEW BEFORE RELEASE, behavior during edit is glitchy.
+ // it's not entirely clear what the motivation is to have 3
+ // different rules for enablement. At least the difference between
+ // move and rotate looks like just a parens error, have updated accordingly.
+ BOOL enable_move = objectp->permMove() && !objectp->isPermanentEnforced() &&
+ ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) &&
+ ((objectp->permModify() && !objectp->isAttachment()) || !gSavedSettings.getBOOL("EditLinkedParts"));
+ BOOL enable_scale = objectp->permMove() && !objectp->isPermanentEnforced() &&
+ ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && objectp->permModify();
+ BOOL enable_rotate = objectp->permMove() && !objectp->isPermanentEnforced() &&
+ ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) &&
+ ((objectp->permModify() && !objectp->isAttachment()) || !gSavedSettings.getBOOL("EditLinkedParts"));
S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount();
BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME ))
diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp
index b1895bfc9b..6c17fb9f8d 100644
--- a/indra/newview/llpanelvolume.cpp
+++ b/indra/newview/llpanelvolume.cpp
@@ -78,6 +78,8 @@
#include "llviewercontrol.h"
#include "llmeshrepository.h"
+#include "llvoavatarself.h"
+
#include <boost/bind.hpp>
// "Features" Tab
@@ -86,6 +88,7 @@ BOOL LLPanelVolume::postBuild()
{
// Flexible Objects Parameters
{
+ childSetCommitCallback("Animated Mesh Checkbox Ctrl", boost::bind(&LLPanelVolume::onCommitAnimatedMeshCheckbox, this, _1, _2), NULL);
childSetCommitCallback("Flexible1D Checkbox Ctrl", boost::bind(&LLPanelVolume::onCommitIsFlexible, this, _1, _2), NULL);
childSetCommitCallback("FlexNumSections",onCommitFlexible,this);
getChild<LLUICtrl>("FlexNumSections")->setValidateBeforeCommit(precommitValidate);
@@ -237,6 +240,11 @@ void LLPanelVolume::getState( )
{
volobjp = (LLVOVolume *)objectp;
}
+ LLVOVolume *root_volobjp = NULL;
+ if (root_objectp && (root_objectp->getPCode() == LL_PCODE_VOLUME))
+ {
+ root_volobjp = (LLVOVolume *)root_objectp;
+ }
if( !objectp )
{
@@ -260,6 +268,8 @@ void LLPanelVolume::getState( )
BOOL editable = root_objectp->permModify() && !root_objectp->isPermanentEnforced();
BOOL single_volume = LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME )
&& LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1;
+ BOOL single_root_volume = LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME ) &&
+ LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() == 1;
// Select Single Message
if (single_volume)
@@ -347,7 +357,23 @@ void LLPanelVolume::getState( )
getChildView("Light Focus")->setEnabled(false);
getChildView("Light Ambiance")->setEnabled(false);
}
-
+
+ // Animated Mesh
+ BOOL is_animated_mesh = single_root_volume && root_volobjp && root_volobjp->isAnimatedObject();
+ getChild<LLUICtrl>("Animated Mesh Checkbox Ctrl")->setValue(is_animated_mesh);
+ BOOL enabled_animated_object_box = FALSE;
+ if (root_volobjp && root_volobjp == volobjp)
+ {
+ enabled_animated_object_box = single_root_volume && root_volobjp && root_volobjp->canBeAnimatedObject() && editable;
+ if (enabled_animated_object_box && !is_animated_mesh &&
+ root_volobjp->isAttachment() && !gAgentAvatarp->canAttachMoreAnimatedObjects())
+ {
+ // Turning this attachment animated would cause us to exceed the limit.
+ enabled_animated_object_box = false;
+ }
+ }
+ getChildView("Animated Mesh Checkbox Ctrl")->setEnabled(enabled_animated_object_box);
+
// Flexible properties
BOOL is_flexible = volobjp && volobjp->isFlexible();
getChild<LLUICtrl>("Flexible1D Checkbox Ctrl")->setValue(is_flexible);
@@ -587,6 +613,7 @@ void LLPanelVolume::clearCtrls()
getChildView("Light Radius")->setEnabled(false);
getChildView("Light Falloff")->setEnabled(false);
+ getChildView("Animated Mesh Checkbox Ctrl")->setEnabled(false);
getChildView("Flexible1D Checkbox Ctrl")->setEnabled(false);
getChildView("FlexNumSections")->setEnabled(false);
getChildView("FlexGravity")->setEnabled(false);
@@ -896,6 +923,31 @@ void LLPanelVolume::onCommitFlexible( LLUICtrl* ctrl, void* userdata )
self->refresh();
}
+void LLPanelVolume::onCommitAnimatedMeshCheckbox(LLUICtrl *, void*)
+{
+ LLViewerObject* objectp = mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+ LLVOVolume *volobjp = (LLVOVolume *)objectp;
+ BOOL animated_mesh = getChild<LLUICtrl>("Animated Mesh Checkbox Ctrl")->getValue();
+ U32 flags = volobjp->getExtendedMeshFlags();
+ U32 new_flags = flags;
+ if (animated_mesh)
+ {
+ new_flags |= LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG;
+ }
+ else
+ {
+ new_flags &= ~LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG;
+ }
+ if (new_flags != flags)
+ {
+ volobjp->setExtendedMeshFlags(new_flags);
+ }
+}
+
void LLPanelVolume::onCommitIsFlexible(LLUICtrl *, void*)
{
if (mObject->flagObjectPermanent())
diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h
index e3453ae99c..66117316cf 100644
--- a/indra/newview/llpanelvolume.h
+++ b/indra/newview/llpanelvolume.h
@@ -63,6 +63,7 @@ public:
static void onCommitLight( LLUICtrl* ctrl, void* userdata);
void onCommitIsFlexible( LLUICtrl* ctrl, void* userdata);
static void onCommitFlexible( LLUICtrl* ctrl, void* userdata);
+ void onCommitAnimatedMeshCheckbox(LLUICtrl* ctrl, void* userdata);
static void onCommitPhysicsParam( LLUICtrl* ctrl, void* userdata);
static void onCommitMaterial( LLUICtrl* ctrl, void* userdata);
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index c44aca6fa5..d29a06f7e5 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -46,6 +46,7 @@
#include "llundo.h"
#include "lluuid.h"
#include "llvolume.h"
+#include "llcontrolavatar.h"
#include "message.h"
#include "object_flags.h"
#include "llquaternion.h"
@@ -645,6 +646,10 @@ void LLSelectMgr::confirmUnlinkObjects(const LLSD& notification, const LLSD& res
// otherwise. this allows the handle_link method to more finely check
// the selection and give an error message when the uer has a
// reasonable expectation for the link to work, but it will fail.
+//
+// For animated objects, there's additional check that if the
+// selection includes at least one animated object, the total mesh
+// triangle count cannot exceed the designated limit.
bool LLSelectMgr::enableLinkObjects()
{
bool new_value = false;
@@ -669,6 +674,10 @@ bool LLSelectMgr::enableLinkObjects()
new_value = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly);
}
}
+ if (!LLSelectMgr::getInstance()->getSelection()->checkAnimatedObjectLinkable())
+ {
+ new_value = false;
+ }
return new_value;
}
@@ -6611,7 +6620,10 @@ S32 get_family_count(LLViewerObject *parent)
//-----------------------------------------------------------------------------
// updateSelectionCenter
-//-----------------------------------------------------------------------------
+//
+// FIXME this is a grab bag of functionality only some of which has to do
+// with the selection center
+// -----------------------------------------------------------------------------
void LLSelectMgr::updateSelectionCenter()
{
const F32 MOVE_SELECTION_THRESHOLD = 1.f; // Movement threshold in meters for updating selection
@@ -6629,23 +6641,12 @@ void LLSelectMgr::updateSelectionCenter()
mSelectionCenterGlobal.clearVec();
mShowSelection = FALSE;
mSelectionBBox = LLBBox();
- mPauseRequest = NULL;
resetAgentHUDZoom();
-
}
else
{
mSelectedObjects->mSelectType = getSelectTypeForObject(object);
- if (mSelectedObjects->mSelectType == SELECT_TYPE_ATTACHMENT && isAgentAvatarValid() && object->getParent() != NULL)
- {
- mPauseRequest = gAgentAvatarp->requestPause();
- }
- else
- {
- mPauseRequest = NULL;
- }
-
if (mSelectedObjects->mSelectType != SELECT_TYPE_HUD && isAgentAvatarValid())
{
// reset hud ZOOM
@@ -6722,6 +6723,59 @@ void LLSelectMgr::updateSelectionCenter()
{
gEditMenuHandler = NULL;
}
+
+ pauseAssociatedAvatars();
+}
+
+//-----------------------------------------------------------------------------
+// pauseAssociatedAvatars
+//
+// If the selection includes an attachment or an animated object, the
+// associated avatars should pause their animations until they are no
+// longer selected.
+//-----------------------------------------------------------------------------
+void LLSelectMgr::pauseAssociatedAvatars()
+{
+ mPauseRequests.clear();
+
+ for (LLObjectSelection::iterator iter = mSelectedObjects->begin();
+ iter != mSelectedObjects->end(); iter++)
+ {
+ LLSelectNode* node = *iter;
+ LLViewerObject* object = node->getObject();
+ if (!object)
+ continue;
+
+ mSelectedObjects->mSelectType = getSelectTypeForObject(object);
+
+ if (mSelectedObjects->mSelectType == SELECT_TYPE_ATTACHMENT &&
+ isAgentAvatarValid() && object->getParent() != NULL)
+ {
+ if (object->isAnimatedObject())
+ {
+ // Is an animated object attachment.
+ // Pause both the control avatar and the avatar it's attached to.
+ if (object->getControlAvatar())
+ {
+ mPauseRequests.push_back(object->getControlAvatar()->requestPause());
+ }
+ mPauseRequests.push_back(gAgentAvatarp->requestPause());
+ }
+ else
+ {
+ // Is a regular attachment. Pause the avatar it's attached to.
+ mPauseRequests.push_back(gAgentAvatarp->requestPause());
+ }
+ }
+ else
+ {
+ if (object && object->isAnimatedObject() && object->getControlAvatar())
+ {
+ // Is a non-attached animated object. Pause the control avatar.
+ mPauseRequests.push_back(object->getControlAvatar()->requestPause());
+ }
+ }
+ }
}
void LLSelectMgr::updatePointAt()
@@ -7358,6 +7412,31 @@ bool LLObjectSelection::applyToObjects(LLSelectedObjectFunctor* func)
return result;
}
+bool LLObjectSelection::checkAnimatedObjectEstTris()
+{
+ F32 est_tris = 0;
+ F32 max_tris = 0;
+ S32 anim_count = 0;
+ for (root_iterator iter = root_begin(); iter != root_end(); ++iter)
+ {
+ LLViewerObject* object = (*iter)->getObject();
+ if (!object)
+ continue;
+ if (object->isAnimatedObject())
+ {
+ anim_count++;
+ }
+ est_tris += object->recursiveGetEstTrianglesMax();
+ max_tris = llmax((F32)max_tris,(F32)object->getAnimatedObjectMaxTris());
+ }
+ return anim_count==0 || est_tris <= max_tris;
+}
+
+bool LLObjectSelection::checkAnimatedObjectLinkable()
+{
+ return checkAnimatedObjectEstTris();
+}
+
bool LLObjectSelection::applyToRootObjects(LLSelectedObjectFunctor* func, bool firstonly)
{
bool result = firstonly ? false : true;
diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h
index e965dd80d5..a7c86e3ad3 100644
--- a/indra/newview/llselectmgr.h
+++ b/indra/newview/llselectmgr.h
@@ -340,6 +340,9 @@ public:
// returns TRUE is any node is currenly worn as an attachment
BOOL isAttachment();
+ bool checkAnimatedObjectEstTris();
+ bool checkAnimatedObjectLinkable();
+
// Apply functors to various subsets of the selected objects
// If firstonly is FALSE, returns the AND of all apply() calls.
// Else returns TRUE immediately if any apply() call succeeds (i.e. OR with early exit)
@@ -743,6 +746,8 @@ public:
LLVector3d getSelectionCenterGlobal() const { return mSelectionCenterGlobal; }
void updateSelectionCenter();
+ void pauseAssociatedAvatars();
+
void resetAgentHUDZoom();
void setAgentHUDZoom(F32 target_zoom, F32 current_zoom);
void getAgentHUDZoom(F32 &target_zoom, F32 &current_zoom) const;
@@ -844,7 +849,7 @@ private:
LLFrameTimer mEffectsTimer;
BOOL mForceSelection;
- LLAnimPauseRequest mPauseRequest;
+ std::vector<LLAnimPauseRequest> mPauseRequests;
};
// *DEPRECATED: For callbacks or observers, use
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index d2a87ee2af..8b1a23fe89 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -29,6 +29,7 @@
#include "llspatialpartition.h"
#include "llappviewer.h"
+#include "llcallstack.h"
#include "lltexturecache.h"
#include "lltexturefetch.h"
#include "llimageworker.h"
@@ -777,6 +778,10 @@ F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
dist = eye.getLength3().getF32();
}
+ LL_DEBUGS("RiggedBox") << "calcDistance, group " << group << " camera " << origin << " obj bounds "
+ << group->mObjectBounds[0] << ", " << group->mObjectBounds[1]
+ << " dist " << dist << " radius " << group->mRadius << LL_ENDL;
+
if (dist < 16.f)
{
dist /= 16.f;
@@ -808,7 +813,8 @@ F32 LLSpatialGroup::getUpdateUrgency() const
BOOL LLSpatialGroup::changeLOD()
{
if (hasState(ALPHA_DIRTY | OBJECT_DIRTY))
- { ///a rebuild is going to happen, update distance and LoD
+ {
+ //a rebuild is going to happen, update distance and LoD
return TRUE;
}
@@ -816,8 +822,28 @@ BOOL LLSpatialGroup::changeLOD()
{
F32 ratio = (mDistance - mLastUpdateDistance)/(llmax(mLastUpdateDistance, mRadius));
+ // MAINT-8264 - this check is not robust if it needs to work
+ // for bounding boxes much larger than the actual enclosed
+ // objects, and using distance to box center is also
+ // problematic. Consider the case that you have a large box
+ // where the enclosed object is in one corner. As you zoom in
+ // on the corner, the object gets much closer to the camera,
+ // but the distance to the box center changes very little, and
+ // an LOD change will not trigger, so object LOD gets "stuck"
+ // at a too-low value. In the case of the above JIRA, the box
+ // was large only due to another error, so this logic did not
+ // need to be changed.
+
if (fabsf(ratio) >= getSpatialPartition()->mSlopRatio)
{
+ LL_DEBUGS("RiggedBox") << "changeLOD true because of ratio compare "
+ << fabsf(ratio) << " " << getSpatialPartition()->mSlopRatio << LL_ENDL;
+ LL_DEBUGS("RiggedBox") << "sg " << this << "\nmDistance " << mDistance
+ << " mLastUpdateDistance " << mLastUpdateDistance
+ << " mRadius " << mRadius
+ << " fab ratio " << fabsf(ratio)
+ << " slop " << getSpatialPartition()->mSlopRatio << LL_ENDL;
+
return TRUE;
}
@@ -869,16 +895,6 @@ void LLSpatialGroup::handleDestruction(const TreeNode* node)
}
}
- //clean up avatar attachment stats
- LLSpatialBridge* bridge = getSpatialPartition()->asBridge();
- if (bridge)
- {
- if (bridge->mAvatar.notNull())
- {
- bridge->mAvatar->subtractAttachmentArea(mSurfaceArea );
- }
- }
-
clearDrawMap();
mVertexBuffer = NULL;
mBufferMap.clear();
@@ -2119,17 +2135,17 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
{
if (drawable->isSpatialBridge())
{
- gGL.diffuseColor4f(1,0.5f,0,1);
+ gGL.diffuseColor4f(1,0.5f,0,1); // orange
}
else if (drawable->getVOVolume())
- {
- if (drawable->isRoot())
+ {
+ if (drawable->isRoot())
{
- gGL.diffuseColor4f(1,1,0,1);
+ gGL.diffuseColor4f(1,1,0,1); // yellow
}
else
{
- gGL.diffuseColor4f(0,1,0,1);
+ gGL.diffuseColor4f(0,1,0,1); // green
}
}
else if (drawable->getVObj())
@@ -2137,24 +2153,24 @@ void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
switch (drawable->getVObj()->getPCode())
{
case LLViewerObject::LL_VO_SURFACE_PATCH:
- gGL.diffuseColor4f(0,1,1,1);
+ gGL.diffuseColor4f(0,1,1,1); // cyan
break;
case LLViewerObject::LL_VO_CLOUDS:
// no longer used
break;
case LLViewerObject::LL_VO_PART_GROUP:
case LLViewerObject::LL_VO_HUD_PART_GROUP:
- gGL.diffuseColor4f(0,0,1,1);
+ gGL.diffuseColor4f(0,0,1,1); // blue
break;
case LLViewerObject::LL_VO_VOID_WATER:
case LLViewerObject::LL_VO_WATER:
- gGL.diffuseColor4f(0,0.5f,1,1);
+ gGL.diffuseColor4f(0,0.5f,1,1); // medium blue
break;
case LL_PCODE_LEGACY_TREE:
- gGL.diffuseColor4f(0,0.5f,0,1);
+ gGL.diffuseColor4f(0,0.5f,0,1); // dark green
break;
default:
- gGL.diffuseColor4f(1,0,1,1);
+ gGL.diffuseColor4f(1,0,1,1); // magenta
break;
}
}
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index 7633e46200..2594e3397c 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -466,8 +466,6 @@ public:
virtual LLCamera transformCamera(LLCamera& camera);
LLDrawable* mDrawable;
- LLPointer<LLVOAvatar> mAvatar;
-
};
class LLCullResult
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 7ea6a14e6b..3097542660 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -2386,6 +2386,7 @@ void register_viewer_callbacks(LLMessageSystem* msg)
msg->setHandlerFuncFast(_PREHASH_NameValuePair, process_name_value);
msg->setHandlerFuncFast(_PREHASH_RemoveNameValuePair, process_remove_name_value);
msg->setHandlerFuncFast(_PREHASH_AvatarAnimation, process_avatar_animation);
+ msg->setHandlerFuncFast(_PREHASH_ObjectAnimation, process_object_animation);
msg->setHandlerFuncFast(_PREHASH_AvatarAppearance, process_avatar_appearance);
msg->setHandlerFuncFast(_PREHASH_CameraConstraint, process_camera_constraint);
msg->setHandlerFuncFast(_PREHASH_AvatarSitResponse, process_avatar_sit_response);
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index bd68d8c999..efbb8c856a 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -785,7 +785,7 @@ void LLToolDragAndDrop::dragOrDrop3D( S32 x, S32 y, MASK mask, BOOL drop, EAccep
mDrop = drop;
if (mDrop)
{
- // don't allow drag and drop onto transparent objects
+ // don't allow drag and drop onto rigged or transparent objects
pick(gViewerWindow->pickImmediate(x, y, FALSE, FALSE));
}
else
diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
index f473000657..238e1d0306 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -111,9 +111,12 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask)
mMouseOutsideSlop = FALSE;
mMouseDownX = x;
mMouseDownY = y;
-
+ LLTimer pick_timer;
+ // AXON experimental feature
+ BOOL pick_rigged = gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick");
//left mouse down always picks transparent (but see handleMouseUp)
- mPick = gViewerWindow->pickImmediate(x, y, TRUE, FALSE);
+ mPick = gViewerWindow->pickImmediate(x, y, TRUE, pick_rigged);
+ LL_INFOS() << "pick_rigged is " << (S32) pick_rigged << " pick time elapsed " << pick_timer.getElapsedTimeF32() << LL_ENDL;
mPick.mKeyMask = mask;
mMouseButtonDown = true;
@@ -128,7 +131,10 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask)
BOOL LLToolPie::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
// don't pick transparent so users can't "pay" transparent objects
- mPick = gViewerWindow->pickImmediate(x, y, /*BOOL pick_transparent*/ FALSE, /*BOOL pick_rigged*/ TRUE, /*BOOL pick_particle*/ TRUE);
+ mPick = gViewerWindow->pickImmediate(x, y,
+ /*BOOL pick_transparent*/ FALSE,
+ /*BOOL pick_rigged*/ TRUE,
+ /*BOOL pick_particle*/ TRUE);
mPick.mKeyMask = mask;
// claim not handled so UI focus stays same
@@ -538,7 +544,9 @@ void LLToolPie::selectionPropertiesReceived()
BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask)
{
- mHoverPick = gViewerWindow->pickImmediate(x, y, FALSE, FALSE);
+ // AXON experimental feature
+ BOOL pick_rigged = gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick");
+ mHoverPick = gViewerWindow->pickImmediate(x, y, FALSE, pick_rigged);
LLViewerObject *parent = NULL;
LLViewerObject *object = mHoverPick.getObject();
LLSelectMgr::getInstance()->setHoverObject(object, mHoverPick.mObjectFace);
@@ -584,7 +592,7 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask)
else
{
// perform a separate pick that detects transparent objects since they respond to 1-click actions
- LLPickInfo click_action_pick = gViewerWindow->pickImmediate(x, y, TRUE, FALSE);
+ LLPickInfo click_action_pick = gViewerWindow->pickImmediate(x, y, TRUE, pick_rigged);
LLViewerObject* click_action_object = click_action_pick.getObject();
@@ -670,6 +678,7 @@ BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask)
LLPickInfo savedPick = mPick;
mPick = gViewerWindow->pickImmediate(savedPick.mMousePt.mX, savedPick.mMousePt.mY,
FALSE /* ignore transparent */,
+ FALSE /* ignore rigged */,
FALSE /* ignore particles */);
if (!mPick.mPosGlobal.isExactlyZero() // valid coordinates for pick
@@ -759,6 +768,7 @@ BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask)
LLPickInfo savedPick = mPick;
mPick = gViewerWindow->pickImmediate(savedPick.mMousePt.mX, savedPick.mMousePt.mY,
FALSE /* ignore transparent */,
+ FALSE /* ignore rigged */,
FALSE /* ignore particles */);
if(mPick.mPickType == LLPickInfo::PICK_OBJECT)
@@ -1751,8 +1761,7 @@ BOOL LLToolPie::handleRightClickPick()
gMenuHolder->setObjectSelection(LLSelectMgr::getInstance()->getSelection());
bool is_other_attachment = (object->isAttachment() && !object->isHUDAttachment() && !object->permYouOwner());
- if (object->isAvatar()
- || is_other_attachment)
+ if (object->isAvatar() || is_other_attachment)
{
// Find the attachment's avatar
while( object && object->isAttachment())
diff --git a/indra/newview/lltoolselect.cpp b/indra/newview/lltoolselect.cpp
index 1fcc9a0711..aa0e9f5c09 100644
--- a/indra/newview/lltoolselect.cpp
+++ b/indra/newview/lltoolselect.cpp
@@ -63,7 +63,9 @@ LLToolSelect::LLToolSelect( LLToolComposite* composite )
BOOL LLToolSelect::handleMouseDown(S32 x, S32 y, MASK mask)
{
// do immediate pick query
- mPick = gViewerWindow->pickImmediate(x, y, TRUE, FALSE);
+ // AXON experimental feature
+ BOOL pick_rigged = gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick");
+ mPick = gViewerWindow->pickImmediate(x, y, TRUE, pick_rigged);
// Pass mousedown to agent
LLTool::handleMouseDown(x, y, mask);
diff --git a/indra/newview/lltoolselectrect.cpp b/indra/newview/lltoolselectrect.cpp
index 71dc8001d4..d655dd881e 100644
--- a/indra/newview/lltoolselectrect.cpp
+++ b/indra/newview/lltoolselectrect.cpp
@@ -71,7 +71,9 @@ void dialog_refresh_all(void);
BOOL LLToolSelectRect::handleMouseDown(S32 x, S32 y, MASK mask)
{
- handlePick(gViewerWindow->pickImmediate(x, y, TRUE, FALSE));
+ // AXON experimental feature
+ BOOL pick_rigged = gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick");
+ handlePick(gViewerWindow->pickImmediate(x, y, TRUE /* pick_transparent */, pick_rigged));
LLTool::handleMouseDown(x, y, mask);
diff --git a/indra/newview/lluiavatar.cpp b/indra/newview/lluiavatar.cpp
new file mode 100644
index 0000000000..6cc14fc49b
--- /dev/null
+++ b/indra/newview/lluiavatar.cpp
@@ -0,0 +1,59 @@
+/**
+ * @file lluiavatar.cpp
+ * @brief Implementation for special dummy avatar used in some UI views
+ *
+ * $LicenseInfo:firstyear=2017&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2017, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "lluiavatar.h"
+#include "llagent.h" // Get state values from here
+#include "llviewerobjectlist.h"
+#include "pipeline.h"
+#include "llanimationstates.h"
+#include "llviewercontrol.h"
+#include "llmeshrepository.h"
+#include "llviewerregion.h"
+
+LLUIAvatar::LLUIAvatar(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) :
+ LLVOAvatar(id, pcode, regionp)
+{
+ mIsDummy = TRUE;
+ mIsUIAvatar = true;
+}
+
+// virtual
+LLUIAvatar::~LLUIAvatar()
+{
+}
+
+// virtual
+void LLUIAvatar::initInstance()
+{
+ LLVOAvatar::initInstance();
+
+ createDrawable( &gPipeline );
+ setPositionAgent(LLVector3::zero);
+ slamPosition();
+ updateJointLODs();
+ updateGeometry(mDrawable);
+}
diff --git a/indra/newview/lluiavatar.h b/indra/newview/lluiavatar.h
new file mode 100644
index 0000000000..bcdffedef2
--- /dev/null
+++ b/indra/newview/lluiavatar.h
@@ -0,0 +1,44 @@
+/**
+ * @file lluiavatar.h
+ * @brief Special dummy avatar used in some UI views
+ *
+ * $LicenseInfo:firstyear=2017&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2017, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_UIAVATAR_H
+#define LL_UIAVATAR_H
+
+#include "llvoavatar.h"
+#include "llvovolume.h"
+
+class LLUIAvatar:
+ public LLVOAvatar
+{
+ LOG_CLASS(LLUIAvatar);
+
+public:
+ LLUIAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual void initInstance(); // Called after construction to initialize the class.
+ virtual ~LLUIAvatar();
+};
+
+#endif //LL_CONTROLAVATAR_H
diff --git a/indra/newview/llviewerjointattachment.cpp b/indra/newview/llviewerjointattachment.cpp
index 66e392ac42..cf9243a871 100644
--- a/indra/newview/llviewerjointattachment.cpp
+++ b/indra/newview/llviewerjointattachment.cpp
@@ -357,6 +357,25 @@ void LLViewerJointAttachment::setOriginalPosition(LLVector3& position)
}
//-----------------------------------------------------------------------------
+// getNumAnimatedObjects()
+//-----------------------------------------------------------------------------
+S32 LLViewerJointAttachment::getNumAnimatedObjects() const
+{
+ S32 count = 0;
+ for (attachedobjs_vec_t::const_iterator iter = mAttachedObjects.begin();
+ iter != mAttachedObjects.end();
+ ++iter)
+ {
+ const LLViewerObject *attached_object = *iter;
+ if (attached_object->isAnimatedObject())
+ {
+ count++;
+ }
+ }
+ return count;
+}
+
+//-----------------------------------------------------------------------------
// clampObjectPosition()
//-----------------------------------------------------------------------------
void LLViewerJointAttachment::clampObjectPosition()
diff --git a/indra/newview/llviewerjointattachment.h b/indra/newview/llviewerjointattachment.h
index 9addafaee1..9641ab4208 100644
--- a/indra/newview/llviewerjointattachment.h
+++ b/indra/newview/llviewerjointattachment.h
@@ -77,6 +77,7 @@ public:
S32 getGroup() const { return mGroup; }
S32 getPieSlice() const { return mPieSlice; }
S32 getNumObjects() const { return mAttachedObjects.size(); }
+ S32 getNumAnimatedObjects() const;
void clampObjectPosition();
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 5bbf5650ad..c2f90cfe14 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -1077,6 +1077,10 @@ U32 info_display_from_string(std::string info_display)
{
return LLPipeline::RENDER_DEBUG_TEXEL_DENSITY;
}
+ else if ("triangle count" == info_display)
+ {
+ return LLPipeline::RENDER_DEBUG_TRIANGLE_COUNT;
+ }
else
{
LL_WARNS() << "unrecognized feature name '" << info_display << "'" << LL_ENDL;
@@ -1616,7 +1620,19 @@ class LLAdvancedEnableAppearanceToXML : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
- return gSavedSettings.getBOOL("DebugAvatarAppearanceMessage");
+ LLViewerObject *obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
+ if (obj && obj->isAnimatedObject() && obj->getControlAvatar())
+ {
+ return gSavedSettings.getBOOL("DebugAnimatedObjects");
+ }
+ else if (obj && obj->isAttachment() && obj->getAvatar())
+ {
+ return gSavedSettings.getBOOL("DebugAvatarAppearanceMessage");
+ }
+ else
+ {
+ return false;
+ }
}
};
@@ -1625,8 +1641,8 @@ class LLAdvancedAppearanceToXML : public view_listener_t
bool handleEvent(const LLSD& userdata)
{
std::string emptyname;
- LLVOAvatar* avatar =
- find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() );
+ LLViewerObject *obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
+ LLVOAvatar* avatar = obj->getAvatar();
if (!avatar)
{
avatar = gAgentAvatarp;
@@ -6050,7 +6066,12 @@ class LLAvatarResetSkeleton: public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
- LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() );
+ LLVOAvatar* avatar = NULL;
+ LLViewerObject *obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
+ if (obj)
+ {
+ avatar = obj->getAvatar();
+ }
if(avatar)
{
avatar->resetSkeleton(false);
@@ -6059,6 +6080,20 @@ class LLAvatarResetSkeleton: public view_listener_t
}
};
+class LLAvatarEnableResetSkeleton: public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ LLViewerObject *obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
+ if (obj && obj->getAvatar())
+ {
+ return true;
+ }
+ return false;
+ }
+};
+
+
class LLAvatarResetSkeletonAndAnimations : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -6892,7 +6927,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)
@@ -9076,6 +9111,7 @@ void initialize_menus()
view_listener_t::addMenu(new LLAvatarReportAbuse(), "Avatar.ReportAbuse");
view_listener_t::addMenu(new LLAvatarToggleMyProfile(), "Avatar.ToggleMyProfile");
view_listener_t::addMenu(new LLAvatarResetSkeleton(), "Avatar.ResetSkeleton");
+ view_listener_t::addMenu(new LLAvatarEnableResetSkeleton(), "Avatar.EnableResetSkeleton");
view_listener_t::addMenu(new LLAvatarResetSkeletonAndAnimations(), "Avatar.ResetSkeletonAndAnimations");
enable.add("Avatar.IsMyProfileOpen", boost::bind(&my_profile_visible));
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index e9085f9327..16d306f997 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -55,6 +55,7 @@
#include "llagentcamera.h"
#include "llcallingcard.h"
#include "llbuycurrencyhtml.h"
+#include "llcontrolavatar.h"
#include "llfirstuse.h"
#include "llfloaterbump.h"
#include "llfloaterbuyland.h"
@@ -103,6 +104,7 @@
#include "llviewerwindow.h"
#include "llvlmanager.h"
#include "llvoavatarself.h"
+#include "llvovolume.h"
#include "llworld.h"
#include "pipeline.h"
#include "llfloaterworldmap.h"
@@ -4982,23 +4984,27 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)
LLUUID animation_id;
LLUUID uuid;
S32 anim_sequence_id;
- LLVOAvatar *avatarp;
+ LLVOAvatar *avatarp = NULL;
mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid);
- //clear animation flags
- avatarp = (LLVOAvatar *)gObjectList.findObject(uuid);
+ LLViewerObject *objp = gObjectList.findObject(uuid);
+ if (objp)
+ {
+ avatarp = objp->asAvatar();
+ }
if (!avatarp)
{
// no agent by this ID...error?
- LL_WARNS("Messaging") << "Received animation state for unknown avatar" << uuid << LL_ENDL;
+ LL_WARNS("Messaging") << "Received animation state for unknown avatar " << uuid << LL_ENDL;
return;
}
S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList);
S32 num_source_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationSourceList);
+ //clear animation flags
avatarp->mSignaledAnimations.clear();
if (avatarp->isSelf())
@@ -5069,6 +5075,72 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)
}
}
+void process_object_animation(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLUUID animation_id;
+ LLUUID uuid;
+ S32 anim_sequence_id;
+
+ mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid);
+
+ LL_DEBUGS("AnimatedObjects") << "Received animation state for object " << uuid << LL_ENDL;
+
+ LLViewerObject *objp = gObjectList.findObject(uuid);
+ if (!objp)
+ {
+ LL_WARNS("Messaging") << "Received animation state for unknown object " << uuid << LL_ENDL;
+ return;
+ }
+
+ LLVOVolume *volp = dynamic_cast<LLVOVolume*>(objp);
+ if (!volp)
+ {
+ LL_WARNS("Messaging") << "Received animation state for non-volume object " << uuid << LL_ENDL;
+ return;
+ }
+
+ if (!volp->isAnimatedObject())
+ {
+ LL_WARNS("Messaging") << "Received animation state for non-animated object " << uuid << LL_ENDL;
+ return;
+ }
+
+ volp->updateControlAvatar();
+ LLControlAvatar *avatarp = volp->getControlAvatar();
+ if (!avatarp)
+ {
+ LL_WARNS("Messaging") << "Received animation request for object with no control avatar, ignoring" << LL_ENDL;
+ return;
+ }
+
+ S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList);
+ LL_DEBUGS("AnimatedObjects") << "processing object animation requests, num_blocks " << num_blocks << LL_ENDL;
+
+ if (!avatarp->mPlaying)
+ {
+ avatarp->mPlaying = true;
+ if (!avatarp->mRootVolp->isAnySelected())
+ {
+ avatarp->updateVolumeGeom();
+ avatarp->mRootVolp->recursiveMarkForUpdate(TRUE);
+ }
+ }
+
+ volp->mObjectSignaledAnimations.clear();
+
+ for( S32 i = 0; i < num_blocks; i++ )
+ {
+ mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i);
+ mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i);
+ volp->mObjectSignaledAnimations[animation_id] = anim_sequence_id;
+ LL_DEBUGS("AnimatedObjects") << "got object animation request for object "
+ << uuid << " animation id " << animation_id << LL_ENDL;
+ }
+
+ avatarp->updateAnimations();
+}
+
+
void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data)
{
LLUUID uuid;
diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h
index b0eaa37541..cef6f79812 100644
--- a/indra/newview/llviewermessage.h
+++ b/indra/newview/llviewermessage.h
@@ -95,6 +95,7 @@ void process_health_message(LLMessageSystem *mesgsys, void **user_data);
void process_sim_stats(LLMessageSystem *mesgsys, void **user_data);
void process_shooter_agent_hit(LLMessageSystem* msg, void** user_data);
void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data);
+void process_object_animation(LLMessageSystem *mesgsys, void **user_data);
void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data);
void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data);
void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data);
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 5d49c888cf..531f9a9527 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -60,6 +60,7 @@
#include "llbbox.h"
#include "llbox.h"
#include "llcylinder.h"
+#include "llcontrolavatar.h"
#include "lldrawable.h"
#include "llface.h"
#include "llfloaterproperties.h"
@@ -69,6 +70,7 @@
#include "llselectmgr.h"
#include "llrendersphere.h"
#include "lltooldraganddrop.h"
+#include "lluiavatar.h"
#include "llviewercamera.h"
#include "llviewertexturelist.h"
#include "llviewerinventory.h"
@@ -103,6 +105,7 @@
#include "llfloaterperms.h"
#include "llvocache.h"
#include "llcleanup.h"
+#include "llcallstack.h"
//#define DEBUG_UPDATE_TYPE
@@ -138,8 +141,11 @@ 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)
{
+ LL_DEBUGS("ObjectUpdate") << "creating " << id << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
+
LLViewerObject *res = NULL;
LL_RECORD_BLOCK_TIME(FTM_CREATE_OBJECT);
@@ -166,6 +172,18 @@ LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pco
}
res = gAgentAvatarp;
}
+ else if (flags & CO_FLAG_CONTROL_AVATAR)
+ {
+ LLControlAvatar *control_avatar = new LLControlAvatar(id, pcode, regionp);
+ control_avatar->initInstance();
+ res = control_avatar;
+ }
+ else if (flags & CO_FLAG_UI_AVATAR)
+ {
+ LLUIAvatar *ui_avatar = new LLUIAvatar(id, pcode, regionp);
+ ui_avatar->initInstance();
+ res = ui_avatar;
+ }
else
{
LLVOAvatar *avatar = new LLVOAvatar(id, pcode, regionp);
@@ -235,6 +253,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 +278,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),
@@ -365,17 +384,23 @@ void LLViewerObject::markDead()
//LL_INFOS() << "Marking self " << mLocalID << " as dead." << LL_ENDL;
// Root object of this hierarchy unlinks itself.
- LLVOAvatar *av = getAvatarAncestor();
if (getParent())
{
((LLViewerObject *)getParent())->removeChild(this);
}
LLUUID mesh_id;
- if (av && LLVOAvatar::getRiggedMeshID(this,mesh_id))
- {
- // This case is needed for indirectly attached mesh objects.
- av->resetJointsOnDetach(mesh_id);
- }
+ {
+ LLVOAvatar *av = getAvatar();
+ if (av && LLVOAvatar::getRiggedMeshID(this,mesh_id))
+ {
+ // This case is needed for indirectly attached mesh objects.
+ av->rebuildAttachmentOverrides();
+ }
+ }
+ if (getControlAvatar())
+ {
+ unlinkControlAvatar();
+ }
// Mark itself as dead
mDead = TRUE;
@@ -676,6 +701,18 @@ void LLViewerObject::setNameValueList(const std::string& name_value_list)
}
}
+BOOL LLViewerObject::isAnySelected() const
+{
+ bool any_selected = isSelected();
+ for (child_list_t::const_iterator iter = mChildList.begin();
+ iter != mChildList.end(); iter++)
+ {
+ const LLViewerObject* child = *iter;
+ any_selected = any_selected || child->isSelected();
+ }
+ return any_selected;
+}
+
void LLViewerObject::setSelected(BOOL sel)
{
mUserSelected = sel;
@@ -848,9 +885,18 @@ void LLViewerObject::addChild(LLViewerObject *childp)
if(childp->setParent(this))
{
mChildList.push_back(childp);
+ childp->afterReparent();
}
}
+void LLViewerObject::onReparent(LLViewerObject *old_parent, LLViewerObject *new_parent)
+{
+}
+
+void LLViewerObject::afterReparent()
+{
+}
+
void LLViewerObject::removeChild(LLViewerObject *childp)
{
for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i)
@@ -1067,6 +1113,9 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
{
LL_DEBUGS_ONCE("SceneLoadTiming") << "Received viewer object data" << LL_ENDL;
+ LL_DEBUGS("ObjectUpdate") << " mesgsys " << mesgsys << " dp " << dp << " id " << getID() << " update_type " << (S32) update_type << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
+
U32 retval = 0x0;
// If region is removed from the list it is also deleted.
@@ -1121,10 +1170,10 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
F32 time_dilation = 1.f;
if(mesgsys != NULL)
{
- U16 time_dilation16;
- mesgsys->getU16Fast(_PREHASH_RegionData, _PREHASH_TimeDilation, time_dilation16);
- time_dilation = ((F32) time_dilation16) / 65535.f;
- mRegionp->setTimeDilation(time_dilation);
+ U16 time_dilation16;
+ mesgsys->getU16Fast(_PREHASH_RegionData, _PREHASH_TimeDilation, time_dilation16);
+ time_dilation = ((F32) time_dilation16) / 65535.f;
+ mRegionp->setTimeDilation(time_dilation);
}
// this will be used to determine if we've really changed position
@@ -1380,7 +1429,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);
@@ -1650,7 +1699,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
U8 state;
mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num );
- mState = state;
+ mAttachmentState = state;
break;
}
@@ -1673,7 +1722,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
U8 state;
dp->unpackU8(state, "State");
- mState = state;
+ mAttachmentState = state;
switch(update_type)
{
@@ -2906,6 +2955,95 @@ void LLViewerObject::fetchInventoryFromServer()
}
}
+LLControlAvatar *LLViewerObject::getControlAvatar()
+{
+ return getRootEdit()->mControlAvatar.get();
+}
+
+LLControlAvatar *LLViewerObject::getControlAvatar() const
+{
+ return getRootEdit()->mControlAvatar.get();
+}
+
+void LLViewerObject::updateControlAvatar()
+{
+ LLViewerObject *root = getRootEdit();
+ if (root->isAnimatedObject() && !root->getControlAvatar())
+ {
+ bool any_mesh = root->isMesh();
+ LLViewerObject::const_child_list_t& child_list = root->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin();
+ iter != child_list.end(); ++iter)
+ {
+ const LLViewerObject* child = *iter;
+ any_mesh = any_mesh || child->isMesh();
+ }
+ if (any_mesh)
+ {
+ std::string vobj_name = llformat("Vol%p", root);
+ LL_DEBUGS("AnimatedObjects") << vobj_name << " calling linkControlAvatar()" << LL_ENDL;
+ root->linkControlAvatar();
+ }
+ }
+ if (!root->isAnimatedObject() && root->getControlAvatar())
+ {
+ std::string vobj_name = llformat("Vol%p", root);
+ LL_DEBUGS("AnimatedObjects") << vobj_name << " calling unlinkControlAvatar()" << LL_ENDL;
+ root->unlinkControlAvatar();
+ }
+}
+
+void LLViewerObject::linkControlAvatar()
+{
+ if (!getControlAvatar() && isRootEdit())
+ {
+ LLVOVolume *volp = dynamic_cast<LLVOVolume*>(this);
+ if (!volp)
+ {
+ LL_WARNS() << "called with null or non-volume object" << LL_ENDL;
+ return;
+ }
+ mControlAvatar = LLControlAvatar::createControlAvatar(volp);
+ LL_DEBUGS("AnimatedObjects") << volp->getID()
+ << " created control av for "
+ << (S32) (1+volp->numChildren()) << " prims" << LL_ENDL;
+ }
+ if (getControlAvatar())
+ {
+ getControlAvatar()->rebuildAttachmentOverrides();
+ getControlAvatar()->updateAnimations();
+ }
+ else
+ {
+ LL_WARNS() << "no control avatar found!" << LL_ENDL;
+ }
+}
+
+void LLViewerObject::unlinkControlAvatar()
+{
+ if (getControlAvatar())
+ {
+ getControlAvatar()->rebuildAttachmentOverrides();
+ }
+ if (isRootEdit())
+ {
+ // This will remove the entire linkset from the control avatar
+ if (mControlAvatar)
+ {
+ mControlAvatar->markForDeath();
+ mControlAvatar = NULL;
+ }
+ }
+ // For non-root prims, removing from the linkset will
+ // automatically remove the control avatar connection.
+}
+
+// virtual
+bool LLViewerObject::isAnimatedObject() const
+{
+ return false;
+}
+
struct LLFilenameAndTask
{
LLUUID mTaskID;
@@ -3515,6 +3653,45 @@ F32 LLViewerObject::getLinksetPhysicsCost()
return mLinksetPhysicsCost;
}
+F32 LLViewerObject::recursiveGetEstTrianglesMax() const
+{
+ F32 est_tris = getEstTrianglesMax();
+ for (child_list_t::const_iterator iter = mChildList.begin();
+ iter != mChildList.end(); iter++)
+ {
+ const LLViewerObject* child = *iter;
+ est_tris += child->recursiveGetEstTrianglesMax();
+ }
+ return est_tris;
+}
+
+S32 LLViewerObject::getAnimatedObjectMaxTris() const
+{
+ S32 max_tris = 0;
+ if (gSavedSettings.getBOOL("AnimatedObjectsIgnoreLimits")) // AXON REMOVE AFTER SERVER TESTING DONE
+ {
+ max_tris = S32_MAX;
+ }
+ else
+ {
+ if (gAgent.getRegion())
+ {
+ LLSD features;
+ gAgent.getRegion()->getSimulatorFeatures(features);
+ if (features.has("AnimatedObjects"))
+ {
+ max_tris = features["AnimatedObjects"]["AnimatedObjectMaxTris"].asInteger();
+ }
+ }
+ }
+ return max_tris;
+}
+
+F32 LLViewerObject::getEstTrianglesMax() const
+{
+ return 0.f;
+}
+
F32 LLViewerObject::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const
{
return 0.f;
@@ -3530,6 +3707,58 @@ U32 LLViewerObject::getHighLODTriangleCount()
return 0;
}
+U32 LLViewerObject::recursiveGetTriangleCount(S32* vcount) const
+{
+ S32 total_tris = getTriangleCount(vcount);
+ LLViewerObject::const_child_list_t& child_list = getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin();
+ iter != child_list.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp)
+ {
+ total_tris += childp->getTriangleCount(vcount);
+ }
+ }
+ return total_tris;
+}
+
+// This is using the stored surface area for each volume (which
+// defaults to 1.0 for the case of everything except a sculpt) and
+// then scaling it linearly based on the largest dimension in the
+// prim's scale. Should revisit at some point.
+F32 LLViewerObject::recursiveGetScaledSurfaceArea() const
+{
+ F32 area = 0.f;
+ const LLDrawable* drawable = mDrawable;
+ if (drawable)
+ {
+ const LLVOVolume* volume = drawable->getVOVolume();
+ if (volume)
+ {
+ if (volume->getVolume())
+ {
+ const LLVector3& scale = volume->getScale();
+ area += volume->getVolume()->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]);
+ }
+ LLViewerObject::const_child_list_t children = volume->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator child_iter = children.begin();
+ child_iter != children.end();
+ ++child_iter)
+ {
+ LLViewerObject* child_obj = *child_iter;
+ LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj );
+ if (child && child->getVolume())
+ {
+ const LLVector3& scale = child->getScale();
+ area += child->getVolume()->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]);
+ }
+ }
+ }
+ }
+ return area;
+}
+
void LLViewerObject::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)
{
LLVector4a center;
@@ -3633,7 +3862,6 @@ void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */)
}
}
-
void LLViewerObject::setLineWidthForWindowSize(S32 window_width)
{
if (window_width < 700)
@@ -3861,7 +4089,7 @@ const LLVector3 LLViewerObject::getRenderPosition() const
if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED))
{
LLVOAvatar* avatar = getAvatar();
- if (avatar)
+ if (avatar && !getControlAvatar())
{
return avatar->getPositionAgent();
}
@@ -3885,7 +4113,7 @@ const LLVector3 LLViewerObject::getPivotPositionAgent() const
const LLQuaternion LLViewerObject::getRenderRotation() const
{
LLQuaternion ret;
- if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED))
+ if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED) && !isAnimatedObject())
{
return ret;
}
@@ -5147,7 +5375,13 @@ LLVOAvatar* LLViewerObject::asAvatar()
return NULL;
}
-// If this object is directly or indirectly parented by an avatar, return it.
+// If this object is directly or indirectly parented by an avatar,
+// return it. Normally getAvatar() is the correct function to call;
+// it will give the avatar used for skinning. The exception is with
+// animated objects that are also attachments; in that case,
+// getAvatar() will return the control avatar, used for skinning, and
+// getAvatarAncestor will return the avatar to which the object is
+// attached.
LLVOAvatar* LLViewerObject::getAvatarAncestor()
{
LLViewerObject *pobj = (LLViewerObject*) getParent();
@@ -5486,6 +5720,11 @@ LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 para
new_block = new LLLightImageParams();
break;
}
+ case LLNetworkData::PARAMS_EXTENDED_MESH:
+ {
+ new_block = new LLExtendedMeshParams();
+ break;
+ }
default:
{
LL_INFOS() << "Unknown param type." << LL_ENDL;
@@ -5885,6 +6124,17 @@ void LLViewerObject::updateVolume(const LLVolumeParams& volume_params)
}
}
+void LLViewerObject::recursiveMarkForUpdate(BOOL priority)
+{
+ for (LLViewerObject::child_list_t::iterator iter = mChildList.begin();
+ iter != mChildList.end(); iter++)
+ {
+ LLViewerObject* child = *iter;
+ child->markForUpdate(priority);
+ }
+ markForUpdate(priority);
+}
+
void LLViewerObject::markForUpdate(BOOL priority)
{
if (mDrawable.notNull())
@@ -5936,6 +6186,11 @@ void LLViewerObject::setRegion(LLViewerRegion *regionp)
child->setRegion(regionp);
}
+ if (mControlAvatar)
+ {
+ mControlAvatar->setRegion(regionp);
+ }
+
setChanged(MOVED | SILHOUETTE);
updateDrawable(FALSE);
}
@@ -6384,6 +6639,10 @@ const std::string& LLViewerObject::getAttachmentItemName() const
//virtual
LLVOAvatar* LLViewerObject::getAvatar() const
{
+ if (getControlAvatar())
+ {
+ return getControlAvatar();
+ }
if (isAttachment())
{
LLViewerObject* vobj = (LLViewerObject*) getParent();
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 21c95d5533..ab0e74bc97 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -47,6 +47,7 @@ class LLAgent; // TODO: Get rid of this.
class LLAudioSource;
class LLAudioSourceVO;
class LLColor4;
+class LLControlAvatar;
class LLDataPacker;
class LLDataPackerBinaryBuffer;
class LLDrawable;
@@ -220,6 +221,8 @@ public:
LLViewerRegion* getRegion() const { return mRegionp; }
BOOL isSelected() const { return mUserSelected; }
+ // Check whole linkset
+ BOOL isAnySelected() const;
virtual void setSelected(BOOL sel);
const LLUUID &getID() const { return mID; }
@@ -231,6 +234,7 @@ public:
virtual BOOL isFlexible() const { return FALSE; }
virtual BOOL isSculpted() const { return FALSE; }
virtual BOOL isMesh() const { return FALSE; }
+ virtual BOOL isRiggedMesh() const { return FALSE; }
virtual BOOL hasLightTexture() const { return FALSE; }
// This method returns true if the object is over land owned by
@@ -255,6 +259,8 @@ public:
*/
virtual BOOL setParent(LLViewerObject* parent);
+ virtual void onReparent(LLViewerObject *old_parent, LLViewerObject *new_parent);
+ virtual void afterReparent();
virtual void addChild(LLViewerObject *childp);
virtual void removeChild(LLViewerObject *childp);
const_child_list_t& getChildren() const { return mChildList; }
@@ -356,9 +362,15 @@ public:
virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE);
+ S32 getAnimatedObjectMaxTris() const;
+ F32 recursiveGetEstTrianglesMax() const;
+ virtual F32 getEstTrianglesMax() const;
virtual F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL, F32* unscaled_value = NULL) const;
virtual U32 getTriangleCount(S32* vcount = NULL) const;
virtual U32 getHighLODTriangleCount();
+ F32 recursiveGetScaledSurfaceArea() const;
+
+ U32 recursiveGetTriangleCount(S32* vcount = NULL) const;
void setObjectCost(F32 cost);
F32 getObjectCost();
@@ -374,7 +386,7 @@ public:
void sendShapeUpdate();
- U8 getState() { return mState; }
+ U8 getAttachmentState() { return mAttachmentState; }
F32 getAppAngle() const { return mAppAngle; }
F32 getPixelArea() const { return mPixelArea; }
@@ -411,7 +423,8 @@ public:
void setIcon(LLViewerTexture* icon_image);
void clearIcon();
- void markForUpdate(BOOL priority);
+ void recursiveMarkForUpdate(BOOL priority);
+ virtual void markForUpdate(BOOL priority);
void updateVolume(const LLVolumeParams& volume_params);
virtual void updateSpatialExtents(LLVector4a& min, LLVector4a& max);
virtual F32 getBinRadius();
@@ -685,6 +698,27 @@ public:
static BOOL sUseSharedDrawables;
+public:
+ // Returns mControlAvatar for the edit root prim of this linkset
+ LLControlAvatar *getControlAvatar();
+ LLControlAvatar *getControlAvatar() const;
+
+ // Create or connect to an existing control av as applicable
+ void linkControlAvatar();
+ // Remove any reference to control av for this prim
+ void unlinkControlAvatar();
+ // Link or unlink as needed
+ void updateControlAvatar();
+
+ virtual bool isAnimatedObject() const;
+
+ // Flags for createObject
+ static const S32 CO_FLAG_CONTROL_AVATAR = 1 << 0;
+ static const S32 CO_FLAG_UI_AVATAR = 1 << 1;
+
+protected:
+ LLPointer<LLControlAvatar> mControlAvatar;
+
protected:
// delete an item in the inventory, but don't tell the
// server. This is used internally by remove, update, and
@@ -695,8 +729,7 @@ protected:
// updateInventory.
void doUpdateInventory(LLPointer<LLViewerInventoryItem>& item, U8 key, bool is_new);
-
- static LLViewerObject *createObject(const LLUUID &id, LLPCode pcode, LLViewerRegion *regionp);
+ static LLViewerObject *createObject(const LLUUID &id, LLPCode pcode, LLViewerRegion *regionp, S32 flags = 0);
BOOL setData(const U8 *datap, const U32 data_size);
@@ -784,7 +817,7 @@ protected:
LLQuaternion mAngularVelocityRot; // accumulated rotation from the angular velocity computations
LLQuaternion mPreviousRotation;
- U8 mState; // legacy
+ U8 mAttachmentState; // this encodes the attachment id in a somewhat complex way. 0 if not an attachment.
LLViewerObjectMedia* mMedia; // NULL if no media associated
U8 mClickAction;
F32 mObjectCost; //resource cost of this object or -1 if unknown
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index dc54346d59..46d92a079e 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -68,6 +68,7 @@
#include "u64.h"
#include "llviewertexturelist.h"
#include "lldatapacker.h"
+#include "llcallstack.h"
#ifdef LL_USESYSTEMLIBS
#include <zlib.h>
#else
@@ -241,6 +242,10 @@ void LLViewerObjectList::processUpdateCore(LLViewerObject* objectp,
}
// ignore returned flags
+ LL_DEBUGS("ObjectUpdate") << "uuid " << objectp->mID << " calling processUpdateMessage "
+ << objectp << " just_created " << just_created << " from_cache " << from_cache << " msg " << msg << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
+
objectp->processUpdateMessage(msg, user_data, i, update_type, dpp);
if (objectp->isDead())
@@ -352,7 +357,10 @@ LLViewerObject* LLViewerObjectList::processObjectUpdateFromCache(LLVOCacheEntry*
if (!objectp)
{
objectp = createObjectFromCache(pcode, regionp, fullid, entry->getLocalID());
-
+
+ LL_DEBUGS("ObjectUpdate") << "uuid " << fullid << " created objectp " << objectp << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
+
if (!objectp)
{
LL_INFOS() << "createObject failure for object: " << fullid << LL_ENDL;
@@ -471,6 +479,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
compressed_dp.reset();
uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);
+ LL_DEBUGS("ObjectUpdate") << "got binary data from message to compressed_dpbuffer" << LL_ENDL;
mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compressed_dpbuffer, 0, i);
compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length);
@@ -530,6 +539,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
// LL_WARNS() << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << LL_ENDL;
mNumUnknownUpdates++;
}
+ else
+ {
+ LL_DEBUGS("ObjectUpdate") << "Non-full, non-compressed update, obj " << local_id << ", global ID " << fullid << " from " << mesgsys->getSender() << LL_ENDL;
+ }
}
else // OUT_FULL only?
{
@@ -538,10 +551,19 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
msg_size += sizeof(LLUUID);
msg_size += sizeof(U32);
- // LL_INFOS() << "Full Update, obj " << local_id << ", global ID" << fullid << "from " << mesgsys->getSender() << LL_ENDL;
+ LL_DEBUGS("ObjectUpdate") << "Full Update, obj " << local_id << ", global ID " << fullid << " from " << mesgsys->getSender() << LL_ENDL;
}
objectp = findObject(fullid);
+ if (compressed)
+ {
+ LL_DEBUGS("ObjectUpdate") << "uuid " << fullid << " received compressed data from message (earlier in function)" << LL_ENDL;
+ }
+ LL_DEBUGS("ObjectUpdate") << "uuid " << fullid << " objectp " << objectp
+ << " update_cache " << (S32) update_cache << " compressed " << compressed
+ << " update_type " << update_type << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
+
if(update_cache)
{
objectp = regionp->updateCacheEntry(local_id, objectp, update_type);
@@ -616,6 +638,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
#endif
objectp = createObject(pcode, regionp, fullid, local_id, gMessageSystem->getSender());
+
+ LL_DEBUGS("ObjectUpdate") << "creating object " << fullid << " result " << objectp << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
+
if (!objectp)
{
LL_INFOS() << "createObject failure for object: " << fullid << LL_ENDL;
@@ -710,12 +736,17 @@ void LLViewerObjectList::processCachedObjectUpdate(LLMessageSystem *mesgsys,
mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_CRC, crc, i);
mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i);
msg_size += sizeof(U32) * 2;
-
+
+ LL_DEBUGS("ObjectUpdate") << "got probe for id " << id << " crc " << crc << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
+
// Lookup data packer and add this id to cache miss lists if necessary.
U8 cache_miss_type = LLViewerRegion::CACHE_MISS_TYPE_NONE;
if(!regionp->probeCache(id, crc, flags, cache_miss_type))
{
// Cache Miss.
+ LL_DEBUGS("ObjectUpdate") << "cache miss for id " << id << " crc " << crc << " miss type " << (S32) cache_miss_type << LL_ENDL;
+
recorder.cacheMissEvent(id, update_type, cache_miss_type, msg_size);
continue; // no data packer, skip this object
@@ -876,8 +907,8 @@ void LLViewerObjectList::update(LLAgent &agent)
{
if (idle_count >= idle_list.size())
{
- idle_list.push_back( objectp );
- }
+ idle_list.push_back( objectp );
+ }
else
{
idle_list[idle_count] = objectp;
@@ -914,7 +945,7 @@ void LLViewerObjectList::update(LLAgent &agent)
{
objectp = *idle_iter;
llassert(objectp->isActive());
- objectp->idleUpdate(agent, frame_time);
+ objectp->idleUpdate(agent, frame_time);
}
//update flexible objects
@@ -1275,6 +1306,9 @@ void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp)
// Cleanup any references we have to this object
// Remove from object map so noone can look it up.
+ LL_DEBUGS("ObjectUpdate") << " dereferencing id " << objectp->mID << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
+
mUUIDObjectMap.erase(objectp->mID);
//if (objectp->getRegion())
@@ -1961,12 +1995,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;
@@ -1986,6 +2020,9 @@ LLViewerObject *LLViewerObjectList::createObjectFromCache(const LLPCode pcode, L
{
llassert_always(uuid.notNull());
+ LL_DEBUGS("ObjectUpdate") << "creating " << uuid << " local_id " << local_id << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
+
LLViewerObject *objectp = LLViewerObject::createObject(uuid, pcode, regionp);
if (!objectp)
{
@@ -2020,6 +2057,9 @@ LLViewerObject *LLViewerObjectList::createObject(const LLPCode pcode, LLViewerRe
fullid = uuid;
}
+ LL_DEBUGS("ObjectUpdate") << "createObject creating " << fullid << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
+
LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp);
if (!objectp)
{
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
index 94c751acc6..72b2b99004 100644
--- a/indra/newview/llviewerobjectlist.h
+++ b/indra/newview/llviewerobjectlist.h
@@ -67,7 +67,7 @@ public:
inline LLViewerObject *getObject(const S32 index);
inline LLViewerObject *findObject(const LLUUID &id);
- LLViewerObject *createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp); // Create a viewer-side object
+ LLViewerObject *createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp, S32 flags = 0); // Create a viewer-side object
LLViewerObject *createObjectFromCache(const LLPCode pcode, LLViewerRegion *regionp, const LLUUID &uuid, const U32 local_id);
LLViewerObject *createObject(const LLPCode pcode, LLViewerRegion *regionp,
const LLUUID &uuid, const U32 local_id, const LLHost &sender);
diff --git a/indra/newview/llvieweroctree.cpp b/indra/newview/llvieweroctree.cpp
index 023f1b92ba..e7916ebd3b 100644
--- a/indra/newview/llvieweroctree.cpp
+++ b/indra/newview/llvieweroctree.cpp
@@ -731,7 +731,7 @@ bool LLViewerOctreeGroup::boundObjects(BOOL empty, LLVector4a& minOut, LLVector4
update_min_max(newMin, newMax, minMax[0]);
update_min_max(newMin, newMax, minMax[1]);
}
-
+
mObjectBounds[0].setAdd(newMin, newMax);
mObjectBounds[0].mul(0.5f);
mObjectBounds[1].setSub(newMax, newMin);
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 5b61eab5f7..efaa327af6 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -78,6 +78,7 @@
#include "llcoros.h"
#include "lleventcoro.h"
#include "llcorehttputil.h"
+#include "llcallstack.h"
#ifdef LL_WINDOWS
#pragma warning(disable:4355)
@@ -1252,7 +1253,7 @@ void LLViewerRegion::updateVisibleEntries(F32 max_time)
LLPointer<LLViewerOctreeGroup> group = *group_iter;
if(group->getNumRefs() < 3 || //group to be deleted
!group->getOctreeNode() || group->isEmpty()) //group empty
-{
+ {
continue;
}
@@ -2136,6 +2137,24 @@ void LLViewerRegion::getInfo(LLSD& info)
info["Region"]["Handle"]["y"] = (LLSD::Integer)y;
}
+void LLViewerRegion::requestSimulatorFeatures()
+{
+ // kick off a request for simulator features
+ std::string url = getCapability("SimulatorFeatures");
+ if (!url.empty())
+ {
+ std::string coroname =
+ LLCoros::instance().launch("LLViewerRegionImpl::requestSimulatorFeatureCoro",
+ boost::bind(&LLViewerRegionImpl::requestSimulatorFeatureCoro, mImpl, url, getHandle()));
+
+ LL_INFOS("AppInit", "SimulatorFeatures") << "Launching " << coroname << " requesting simulator features from " << url << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("AppInit", "SimulatorFeatures") << "SimulatorFeatures cap not set" << LL_ENDL;
+ }
+}
+
boost::signals2::connection LLViewerRegion::setSimulatorFeaturesReceivedCallback(const caps_received_signal_t::slot_type& cb)
{
return mSimulatorFeaturesReceivedSignal.connect(cb);
@@ -2213,7 +2232,7 @@ void LLViewerRegion::decodeBoundingInfo(LLVOCacheEntry* entry)
{
LLViewerRegion* old_regionp = ((LLDrawable*)entry->getEntry()->getDrawable())->getRegion();
if(old_regionp != this && old_regionp)
-{
+ {
LLViewerObject* obj = ((LLDrawable*)entry->getEntry()->getDrawable())->getVObj();
if(obj)
{
@@ -2376,12 +2395,18 @@ LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLDataPackerB
// we've seen this object before
if (entry->getCRC() == crc)
{
+ LL_DEBUGS("AnimatedObjects") << " got dupe for local_id " << local_id << LL_ENDL;
+ dumpStack("AnimatedObjectsStack");
+
// Record a hit
entry->recordDupe();
result = CACHE_UPDATE_DUPE;
}
else //CRC changed
{
+ LL_DEBUGS("AnimatedObjects") << " got update for local_id " << local_id << LL_ENDL;
+ dumpStack("AnimatedObjectsStack");
+
// Update the cache entry
entry->updateEntry(crc, dp);
@@ -2392,6 +2417,9 @@ LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLDataPackerB
}
else
{
+ LL_DEBUGS("AnimatedObjects") << " got first notification for local_id " << local_id << LL_ENDL;
+ dumpStack("AnimatedObjectsStack");
+
// we haven't seen this object before
// Create new entry and add to map
result = CACHE_UPDATE_ADDED;
@@ -2496,7 +2524,7 @@ bool LLViewerRegion::probeCache(U32 local_id, U32 crc, U32 flags, U8 &cache_miss
// Record a hit
mRegionCacheHitCount++;
entry->recordHit();
- cache_miss_type = CACHE_MISS_TYPE_NONE;
+ cache_miss_type = CACHE_MISS_TYPE_NONE;
entry->setUpdateFlags(flags);
if(entry->isState(LLVOCacheEntry::ACTIVE))
@@ -2519,12 +2547,14 @@ bool LLViewerRegion::probeCache(U32 local_id, U32 crc, U32 flags, U8 &cache_miss
// LL_INFOS() << "CRC miss for " << local_id << LL_ENDL;
addCacheMiss(local_id, CACHE_MISS_TYPE_CRC);
+ cache_miss_type = CACHE_MISS_TYPE_CRC;
}
}
else
{
// LL_INFOS() << "Cache miss for " << local_id << LL_ENDL;
addCacheMiss(local_id, CACHE_MISS_TYPE_FULL);
+ cache_miss_type = CACHE_MISS_TYPE_FULL;
}
return false;
@@ -2561,6 +2591,9 @@ void LLViewerRegion::requestCacheMisses()
msg->nextBlockFast(_PREHASH_ObjectData);
msg->addU8Fast(_PREHASH_CacheMissType, (*iter).mType);
msg->addU32Fast(_PREHASH_ID, (*iter).mID);
+
+ LL_DEBUGS("AnimatedObjects") << "Requesting cache missed object " << (*iter).mID << LL_ENDL;
+
blocks++;
if (blocks >= 255)
@@ -2861,6 +2894,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("MeshUploadFlag");
capabilityNames.append("NavMeshGenerationStatus");
capabilityNames.append("NewFileAgentInventory");
+ capabilityNames.append("ObjectAnimation");
capabilityNames.append("ObjectMedia");
capabilityNames.append("ObjectMediaNavigate");
capabilityNames.append("ObjectNavMeshProperties");
@@ -2954,12 +2988,8 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u
}
else if (name == "SimulatorFeatures")
{
- // kick off a request for simulator features
- std::string coroname =
- LLCoros::instance().launch("LLViewerRegionImpl::requestSimulatorFeatureCoro",
- boost::bind(&LLViewerRegionImpl::requestSimulatorFeatureCoro, mImpl, url, getHandle()));
-
- LL_INFOS("AppInit", "SimulatorFeatures") << "Launching " << coroname << " requesting simulator features from " << url << LL_ENDL;
+ mImpl->mCapabilities["SimulatorFeatures"] = url;
+ requestSimulatorFeatures();
}
else
{
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index 69fb9c4d4e..69fa42c1c8 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -305,6 +305,7 @@ public:
bool meshUploadEnabled() const;
// has region received its simulator features list? Requires an additional query after caps received.
+ void requestSimulatorFeatures();
void setSimulatorFeaturesReceived(bool);
bool simulatorFeaturesReceived() const;
boost::signals2::connection setSimulatorFeaturesReceivedCallback(const caps_received_signal_t::slot_type& cb);
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 74deaffe16..b87a6d14e5 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1158,7 +1158,9 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi
if (prim_media_dnd_enabled)
{
- LLPickInfo pick_info = pickImmediate( pos.mX, pos.mY, TRUE /*BOOL pick_transparent*/, FALSE );
+ LLPickInfo pick_info = pickImmediate( pos.mX, pos.mY,
+ TRUE /* pick_transparent */,
+ FALSE /* pick_rigged */);
LLUUID object_id = pick_info.getObjectID();
S32 object_face = pick_info.mObjectFace;
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index eae8f2cc56..40fb05abc4 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -44,6 +44,7 @@
#include "llavatarnamecache.h"
#include "llavatarpropertiesprocessor.h"
#include "llavatarrendernotifier.h"
+#include "llcontrolavatar.h"
#include "llexperiencecache.h"
#include "llphysicsmotion.h"
#include "llviewercontrol.h"
@@ -109,6 +110,8 @@
#include "llcallstack.h"
#include "llrendersphere.h"
+#include <boost/lexical_cast.hpp>
+
extern F32 SPEED_ADJUST_MAX;
extern F32 SPEED_ADJUST_MAX_SEC;
extern F32 ANIM_SPEED_MAX;
@@ -142,7 +145,7 @@ const LLUUID ANIM_AGENT_PHYSICS_MOTION = LLUUID("7360e029-3cb8-ebc4-863e-212df44
//-----------------------------------------------------------------------------
// Constants
//-----------------------------------------------------------------------------
-const F32 DELTA_TIME_MIN = 0.01f; // we clamp measured deltaTime to this
+const F32 DELTA_TIME_MIN = 0.01f; // we clamp measured delta_time to this
const F32 DELTA_TIME_MAX = 0.2f; // range to insure stability of computations.
const F32 PELVIS_LAG_FLYING = 0.22f;// pelvis follow half life while flying
@@ -617,6 +620,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
LLViewerObject(id, pcode, regionp),
mSpecialRenderMode(0),
mAttachmentSurfaceArea(0.f),
+ mAttachmentVisibleTriangleCount(0),
+ mAttachmentEstTriangleCount(0.f),
mReportedVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN),
mTurning(FALSE),
mLastSkeletonSerialNum( 0 ),
@@ -663,7 +668,10 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mLastUpdateRequestCOFVersion(-1),
mLastUpdateReceivedCOFVersion(-1),
mCachedMuteListUpdateTime(0),
- mCachedInMuteList(false)
+ mCachedInMuteList(false),
+ mIsControlAvatar(false),
+ mIsUIAvatar(false),
+ mEnableDefaultMotions(true)
{
LL_DEBUGS("AvatarRender") << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << LL_ENDL;
@@ -718,6 +726,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mCurrentGesticulationLevel = 0;
+
mRuthTimer.reset();
mRuthDebugTimer.reset();
mDebugExistenceTimer.reset();
@@ -1099,7 +1108,7 @@ void LLVOAvatar::cleanupClass()
}
// virtual
-void LLVOAvatar::initInstance(void)
+void LLVOAvatar::initInstance()
{
//-------------------------------------------------------------------------
// register motions
@@ -1222,8 +1231,6 @@ const LLVector3 LLVOAvatar::getRenderPosition() const
{
return getPosition() * mDrawable->getParent()->getRenderMatrix();
}
-
-
}
void LLVOAvatar::updateDrawable(BOOL force_damped)
@@ -1240,6 +1247,10 @@ void LLVOAvatar::onShift(const LLVector4a& shift_vector)
void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)
{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
if (isImpostor() && !needsImpostorUpdate())
{
LLVector3 delta = getRenderPosition() -
@@ -1404,13 +1415,29 @@ void LLVOAvatar::renderCollisionVolumes()
static F32 sphere_scale = 1.0f;
static F32 center_dot_scale = 0.05f;
- static LLVector3 CV_COLOR_OCCLUDED(0.0f, 0.0f, 1.0f);
- static LLVector3 CV_COLOR_VISIBLE(0.5f, 0.5f, 1.0f);
- static LLVector3 DOT_COLOR_OCCLUDED(1.0f, 1.0f, 1.0f);
- static LLVector3 DOT_COLOR_VISIBLE(1.0f, 1.0f, 1.0f);
+ static LLVector3 BLUE(0.0f, 0.0f, 1.0f);
+ static LLVector3 PASTEL_BLUE(0.5f, 0.5f, 1.0f);
+ static LLVector3 RED(1.0f, 0.0f, 0.0f);
+ static LLVector3 PASTEL_RED(1.0f, 0.5f, 0.5f);
+ static LLVector3 WHITE(1.0f, 1.0f, 1.0f);
+
- render_sphere_and_line(begin_pos, end_pos, sphere_scale, CV_COLOR_OCCLUDED, CV_COLOR_VISIBLE);
- render_sphere_and_line(begin_pos, end_pos, center_dot_scale, DOT_COLOR_OCCLUDED, DOT_COLOR_VISIBLE);
+ LLVector3 cv_color_occluded;
+ LLVector3 cv_color_visible;
+ LLVector3 dot_color_occluded(WHITE);
+ LLVector3 dot_color_visible(WHITE);
+ if (isControlAvatar())
+ {
+ cv_color_occluded = RED;
+ cv_color_visible = PASTEL_RED;
+ }
+ else
+ {
+ cv_color_occluded = BLUE;
+ cv_color_visible = PASTEL_BLUE;
+ }
+ render_sphere_and_line(begin_pos, end_pos, sphere_scale, cv_color_occluded, cv_color_visible);
+ render_sphere_and_line(begin_pos, end_pos, center_dot_scale, dot_color_occluded, dot_color_visible);
gGL.popMatrix();
}
@@ -1422,9 +1449,6 @@ void LLVOAvatar::renderCollisionVolumes()
mNameText->lineSegmentIntersect(unused, unused, unused, TRUE);
}
-
- mDebugText.clear();
- addDebugText(ostr.str());
}
void LLVOAvatar::renderBones()
@@ -1595,6 +1619,11 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&
return FALSE;
}
+ if (isControlAvatar())
+ {
+ return FALSE;
+ }
+
if (lineSegmentBoundingBox(start, end))
{
for (S32 i = 0; i < mNumCollisionVolumes; ++i)
@@ -1680,6 +1709,7 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&
return FALSE;
}
+// virtual
LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end,
S32 face,
BOOL pick_transparent,
@@ -1796,7 +1826,11 @@ void LLVOAvatar::buildCharacter()
mAahMorph = getVisualParam( "Express_Open_Mouth" );
}
- startDefaultMotions();
+ // Currently disabled for control avatars (animated objects), enabled for all others.
+ if (mEnableDefaultMotions)
+ {
+ startDefaultMotions();
+ }
//-------------------------------------------------------------------------
// restart any currently active motions
@@ -1856,7 +1890,7 @@ void LLVOAvatar::resetVisualParams()
void LLVOAvatar::resetSkeleton(bool reset_animations)
{
LL_DEBUGS("Avatar") << avString() << " reset starts" << LL_ENDL;
- if (!mLastProcessedAppearance)
+ if (!isControlAvatar() && !mLastProcessedAppearance)
{
LL_WARNS() << "Can't reset avatar; no appearance message has been received yet." << LL_ENDL;
return;
@@ -1910,8 +1944,11 @@ void LLVOAvatar::resetSkeleton(bool reset_animations)
}
// Reset tweakable params to preserved state
- bool slam_params = true;
- applyParsedAppearanceMessage(*mLastProcessedAppearance, slam_params);
+ if (mLastProcessedAppearance)
+ {
+ bool slam_params = true;
+ applyParsedAppearanceMessage(*mLastProcessedAppearance, slam_params);
+ }
updateVisualParams();
// Restore attachment pos overrides
@@ -1942,7 +1979,7 @@ void LLVOAvatar::resetSkeleton(bool reset_animations)
//-----------------------------------------------------------------------------
void LLVOAvatar::releaseMeshData()
{
- if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || mIsDummy)
+ if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || isUIAvatar())
{
return;
}
@@ -1962,15 +1999,15 @@ void LLVOAvatar::releaseMeshData()
LLFace* facep = mDrawable->getFace(0);
if (facep)
{
- facep->setSize(0, 0);
- for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++)
- {
- facep = mDrawable->getFace(i);
+ facep->setSize(0, 0);
+ for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++)
+ {
+ facep = mDrawable->getFace(i);
if (facep)
{
- facep->setSize(0, 0);
- }
- }
+ facep->setSize(0, 0);
+ }
+ }
}
}
@@ -1994,6 +2031,10 @@ void LLVOAvatar::releaseMeshData()
void LLVOAvatar::restoreMeshData()
{
llassert(!isSelf());
+ if (mDrawable.isNull())
+ {
+ return;
+ }
//LL_INFOS() << "Restoring" << LL_ENDL;
mMeshValid = TRUE;
@@ -2303,7 +2344,7 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time)
{
LL_RECORD_BLOCK_TIME(FTM_JOINT_UPDATE);
- if (mIsSitting && getParent())
+ if (isSitting() && getParent())
{
LLViewerObject *root_object = (LLViewerObject*)getRoot();
LLDrawable* drawablep = root_object->mDrawable;
@@ -2469,7 +2510,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)
// (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing)
//--------------------------------------------------------------------------------------------
- if ( mIsSitting )
+ if ( isSitting() )
{
LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] );
mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot->getWorldPosition() + headOffset );
@@ -2593,13 +2634,16 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update)
}
}
- mDrawable->movePartition();
-
- //force a move if sitting on an active object
- if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive())
- {
- gPipeline.markMoved(mDrawable, TRUE);
- }
+ if (mDrawable.notNull())
+ {
+ mDrawable->movePartition();
+
+ //force a move if sitting on an active object
+ if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive())
+ {
+ gPipeline.markMoved(mDrawable, TRUE);
+ }
+ }
}
void LLVOAvatar::idleUpdateAppearanceAnimation()
@@ -2760,7 +2804,8 @@ void LLVOAvatar::idleUpdateLoadingEffect()
LLPartData::LL_PART_EMISSIVE_MASK | // LLPartData::LL_PART_FOLLOW_SRC_MASK |
LLPartData::LL_PART_TARGET_POS_MASK );
- if (!isTooComplex()) // do not generate particles for overly-complex avatars
+ // do not generate particles for dummy or overly-complex avatars
+ if (!mIsDummy && !isTooComplex())
{
setParticleSource(particle_parameters, getID());
}
@@ -3358,154 +3403,218 @@ bool LLVOAvatar::isInMuteList()
return muted;
}
-void LLVOAvatar::updateDebugText()
+void LLVOAvatar::updateAppearanceMessageDebugText()
{
- // clear debug text
- mDebugText.clear();
+ S32 central_bake_version = -1;
+ if (getRegion())
+ {
+ central_bake_version = getRegion()->getCentralBakeVersion();
+ }
+ bool all_baked_downloaded = allBakedTexturesCompletelyDownloaded();
+ bool all_local_downloaded = allLocalTexturesCompletelyDownloaded();
+ std::string debug_line = llformat("%s%s - mLocal: %d, mEdit: %d, mUSB: %d, CBV: %d",
+ isSelf() ? (all_local_downloaded ? "L" : "l") : "-",
+ all_baked_downloaded ? "B" : "b",
+ mUseLocalAppearance, mIsEditingAppearance,
+ 1, central_bake_version);
+ std::string origin_string = bakedTextureOriginInfo();
+ debug_line += " [" + origin_string + "]";
+ S32 curr_cof_version = LLAppearanceMgr::instance().getCOFVersion();
+ S32 last_request_cof_version = mLastUpdateRequestCOFVersion;
+ S32 last_received_cof_version = mLastUpdateReceivedCOFVersion;
+ if (isSelf())
+ {
+ debug_line += llformat(" - cof: %d req: %d rcv:%d",
+ curr_cof_version, last_request_cof_version, last_received_cof_version);
+ if (gSavedSettings.getBOOL("DebugForceAppearanceRequestFailure"))
+ {
+ debug_line += " FORCING ERRS";
+ }
+ }
+ else
+ {
+ debug_line += llformat(" - cof rcv:%d", last_received_cof_version);
+ }
+ debug_line += llformat(" bsz-z: %.3f", mBodySize[2]);
+ if (mAvatarOffset[2] != 0.0f)
+ {
+ debug_line += llformat("avofs-z: %.3f", mAvatarOffset[2]);
+ }
+ bool hover_enabled = getRegion() && getRegion()->avatarHoverHeightEnabled();
+ debug_line += hover_enabled ? " H" : " h";
+ const LLVector3& hover_offset = getHoverOffset();
+ if (hover_offset[2] != 0.0)
+ {
+ debug_line += llformat(" hov_z: %.3f", hover_offset[2]);
+ debug_line += llformat(" %s", (isSitting() ? "S" : "T"));
+ debug_line += llformat("%s", (isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED) ? "G" : "-"));
+ }
+ LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition();
+ LLVector3 normal;
+ LLVector3 ankle_right_ground_agent = ankle_right_pos_agent;
+ resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal);
+ F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]);
+ debug_line += llformat(" relev %.3f", rightElev);
- if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
- {
- S32 central_bake_version = -1;
- if (getRegion())
- {
- central_bake_version = getRegion()->getCentralBakeVersion();
- }
- bool all_baked_downloaded = allBakedTexturesCompletelyDownloaded();
- bool all_local_downloaded = allLocalTexturesCompletelyDownloaded();
- std::string debug_line = llformat("%s%s - mLocal: %d, mEdit: %d, mUSB: %d, CBV: %d",
- isSelf() ? (all_local_downloaded ? "L" : "l") : "-",
- all_baked_downloaded ? "B" : "b",
- mUseLocalAppearance, mIsEditingAppearance,
- 1, central_bake_version);
- std::string origin_string = bakedTextureOriginInfo();
- debug_line += " [" + origin_string + "]";
- S32 curr_cof_version = LLAppearanceMgr::instance().getCOFVersion();
- S32 last_request_cof_version = mLastUpdateRequestCOFVersion;
- S32 last_received_cof_version = mLastUpdateReceivedCOFVersion;
- if (isSelf())
- {
- debug_line += llformat(" - cof: %d req: %d rcv:%d",
- curr_cof_version, last_request_cof_version, last_received_cof_version);
- if (gSavedSettings.getBOOL("DebugForceAppearanceRequestFailure"))
- {
- debug_line += " FORCING ERRS";
- }
- }
- else
- {
- debug_line += llformat(" - cof rcv:%d", last_received_cof_version);
- }
- debug_line += llformat(" bsz-z: %.3f", mBodySize[2]);
- if (mAvatarOffset[2] != 0.0f)
+ LLVector3 root_pos = mRoot->getPosition();
+ LLVector3 pelvis_pos = mPelvisp->getPosition();
+ debug_line += llformat(" rp %.3f pp %.3f", root_pos[2], pelvis_pos[2]);
+
+ S32 is_visible = (S32) isVisible();
+ S32 is_m_visible = (S32) mVisible;
+ debug_line += llformat(" v %d/%d", is_visible, is_m_visible);
+
+ addDebugText(debug_line);
+}
+
+LLViewerInventoryItem* getObjectInventoryItem(LLViewerObject *vobj, LLUUID asset_id)
+{
+ LLViewerInventoryItem *item = NULL;
+
+ if (vobj)
+ {
+ if (vobj->getInventorySerial()<=0)
{
- debug_line += llformat("avofs-z: %.3f", mAvatarOffset[2]);
+ vobj->requestInventory();
}
- bool hover_enabled = getRegion() && getRegion()->avatarHoverHeightEnabled();
- debug_line += hover_enabled ? " H" : " h";
- const LLVector3& hover_offset = getHoverOffset();
- if (hover_offset[2] != 0.0)
- {
- debug_line += llformat(" hov_z: %.3f", hover_offset[2]);
- debug_line += llformat(" %s", (mIsSitting ? "S" : "T"));
- debug_line += llformat("%s", (isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED) ? "G" : "-"));
- }
- LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition();
- LLVector3 normal;
- LLVector3 ankle_right_ground_agent = ankle_right_pos_agent;
- resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal);
- F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]);
- debug_line += llformat(" relev %.3f", rightElev);
+ item = vobj->getInventoryItemByAsset(asset_id);
+ }
+ return item;
+}
- LLVector3 root_pos = mRoot->getPosition();
- LLVector3 pelvis_pos = mPelvisp->getPosition();
- debug_line += llformat(" rp %.3f pp %.3f", root_pos[2], pelvis_pos[2]);
+LLViewerInventoryItem* recursiveGetObjectInventoryItem(LLViewerObject *vobj, LLUUID asset_id)
+{
+ LLViewerInventoryItem *item = getObjectInventoryItem(vobj, asset_id);
+ if (!item)
+ {
+ LLViewerObject::const_child_list_t& children = vobj->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator it = children.begin();
+ it != children.end(); ++it)
+ {
+ LLViewerObject *childp = *it;
+ item = getObjectInventoryItem(childp, asset_id);
+ if (item)
+ {
+ break;
+ }
+ }
+ }
+ return item;
+}
- addDebugText(debug_line);
+void LLVOAvatar::updateAnimationDebugText()
+{
+ for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin();
+ iter != mMotionController.getActiveMotions().end(); ++iter)
+ {
+ LLMotion* motionp = *iter;
+ if (motionp->getMinPixelArea() < getPixelArea())
+ {
+ std::string output;
+ std::string motion_name = motionp->getName();
+ if (motion_name.empty())
+ {
+ if (isControlAvatar())
+ {
+ LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this);
+ // Try to get name from inventory of associated object
+ LLVOVolume *volp = control_av->mRootVolp;
+ LLViewerInventoryItem *item = recursiveGetObjectInventoryItem(volp,motionp->getID());
+ if (item)
+ {
+ motion_name = item->getName();
+ }
+ }
+ }
+ if (motion_name.empty())
+ {
+ std::string name;
+ if (gAgent.isGodlikeWithoutAdminMenuFakery() || isSelf())
+ {
+ name = motionp->getID().asString();
+ LLVOAvatar::AnimSourceIterator anim_it = mAnimationSources.begin();
+ for (; anim_it != mAnimationSources.end(); ++anim_it)
+ {
+ if (anim_it->second == motionp->getID())
+ {
+ LLViewerObject* object = gObjectList.findObject(anim_it->first);
+ if (!object)
+ {
+ break;
+ }
+ if (object->isAvatar())
+ {
+ if (mMotionController.mIsSelf)
+ {
+ // Searching inventory by asset id is really long
+ // so just mark as inventory
+ // Also item is likely to be named by LLPreviewAnim
+ name += "(inventory)";
+ }
+ }
+ else
+ {
+ LLViewerInventoryItem* item = NULL;
+ if (!object->isInventoryDirty())
+ {
+ item = object->getInventoryItemByAsset(motionp->getID());
+ }
+ if (item)
+ {
+ name = item->getName();
+ }
+ else if (object->isAttachment())
+ {
+ name += "(" + getAttachmentItemName() + ")";
+ }
+ else
+ {
+ // in-world object, name or content unknown
+ name += "(in-world)";
+ }
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ name = LLUUID::null.asString();
+ }
+ output = llformat("%s - %d",
+ name.c_str(),
+ (U32)motionp->getPriority());
+ }
+ else
+ {
+ output = llformat("%s - %d",
+ motion_name.c_str(),
+ (U32)motionp->getPriority());
+ }
+ addDebugText(output);
+ }
+ }
+}
+
+void LLVOAvatar::updateDebugText()
+{
+ // Leave mDebugText uncleared here, in case a derived class has added some state first
+
+ if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
+ {
+ updateAppearanceMessageDebugText();
}
+
if (gSavedSettings.getBOOL("DebugAvatarCompositeBaked"))
{
if (!mBakedTextureDebugText.empty())
addDebugText(mBakedTextureDebugText);
}
+ // Develop -> Avatar -> Animation Info
if (LLVOAvatar::sShowAnimationDebug)
{
- for (LLMotionController::motion_list_t::iterator iter = mMotionController.getActiveMotions().begin();
- iter != mMotionController.getActiveMotions().end(); ++iter)
- {
- LLMotion* motionp = *iter;
- if (motionp->getMinPixelArea() < getPixelArea())
- {
- std::string output;
- if (motionp->getName().empty())
- {
- std::string name;
- if (gAgent.isGodlikeWithoutAdminMenuFakery() || isSelf())
- {
- name = motionp->getID().asString();
- LLVOAvatar::AnimSourceIterator anim_it = mAnimationSources.begin();
- for (; anim_it != mAnimationSources.end(); ++anim_it)
- {
- if (anim_it->second == motionp->getID())
- {
- LLViewerObject* object = gObjectList.findObject(anim_it->first);
- if (!object)
- {
- break;
- }
- if (object->isAvatar())
- {
- if (mMotionController.mIsSelf)
- {
- // Searching inventory by asset id is really long
- // so just mark as inventory
- // Also item is likely to be named by LLPreviewAnim
- name += "(inventory)";
- }
- }
- else
- {
- LLViewerInventoryItem* item = NULL;
- if (!object->isInventoryDirty())
- {
- item = object->getInventoryItemByAsset(motionp->getID());
- }
- if (item)
- {
- name = item->getName();
- }
- else if (object->isAttachment())
- {
- name += "(" + getAttachmentItemName() + ")";
- }
- else
- {
- // in-world object, name or content unknown
- name += "(in-world)";
- }
- }
- break;
- }
- }
- }
- else
- {
- name = LLUUID::null.asString();
- }
-
- output = llformat("%s - %d",
- name.c_str(),
- (U32)motionp->getPriority());
- }
- else
- {
- output = llformat("%s - %d",
- motionp->getName().c_str(),
- (U32)motionp->getPriority());
- }
- addDebugText(output);
- }
- }
+ updateAnimationDebugText();
}
if (!mDebugText.size() && mText.notNull())
@@ -3517,51 +3626,125 @@ void LLVOAvatar::updateDebugText()
{
setDebugText(mDebugText);
}
- mDebugText.clear();
-
+ mDebugText.clear();
}
//------------------------------------------------------------------------
-// updateCharacter()
-// called on both your avatar and other avatars
+// updateFootstepSounds
+// Factored out from updateCharacter()
+// Generate footstep sounds when feet hit the ground
//------------------------------------------------------------------------
-BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
-{
- updateDebugText();
-
- if (!mIsBuilt)
- {
- return FALSE;
- }
+void LLVOAvatar::updateFootstepSounds()
+{
+ if (mIsDummy)
+ {
+ return;
+ }
+
+ //-------------------------------------------------------------------------
+ // Find the ground under each foot, these are used for a variety
+ // of things that follow
+ //-------------------------------------------------------------------------
+ LLVector3 ankle_left_pos_agent = mFootLeftp->getWorldPosition();
+ LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition();
- BOOL visible = isVisible();
+ LLVector3 ankle_left_ground_agent = ankle_left_pos_agent;
+ LLVector3 ankle_right_ground_agent = ankle_right_pos_agent;
+ LLVector3 normal;
+ resolveHeightAgent(ankle_left_pos_agent, ankle_left_ground_agent, normal);
+ resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal);
- // For fading out the names above heads, only let the timer
- // run if we're visible.
- if (mDrawable.notNull() && !visible)
+ F32 leftElev = llmax(-0.2f, ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]);
+ F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]);
+
+ if (!isSitting())
{
- mTimeVisible.reset();
+ //-------------------------------------------------------------------------
+ // Figure out which foot is on ground
+ //-------------------------------------------------------------------------
+ if (!mInAir)
+ {
+ if ((leftElev < 0.0f) || (rightElev < 0.0f))
+ {
+ ankle_left_pos_agent = mFootLeftp->getWorldPosition();
+ ankle_right_pos_agent = mFootRightp->getWorldPosition();
+ leftElev = ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ];
+ rightElev = ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ];
+ }
+ }
}
+
+ const LLUUID AGENT_FOOTSTEP_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_LAND};
+ const S32 NUM_AGENT_FOOTSTEP_ANIMS = LL_ARRAY_SIZE(AGENT_FOOTSTEP_ANIMS);
- //--------------------------------------------------------------------
- // the rest should only be done occasionally for far away avatars
- //--------------------------------------------------------------------
+ if ( gAudiop && isAnyAnimationSignaled(AGENT_FOOTSTEP_ANIMS, NUM_AGENT_FOOTSTEP_ANIMS) )
+ {
+ BOOL playSound = FALSE;
+ LLVector3 foot_pos_agent;
+
+ BOOL onGroundLeft = (leftElev <= 0.05f);
+ BOOL onGroundRight = (rightElev <= 0.05f);
+ // did left foot hit the ground?
+ if ( onGroundLeft && !mWasOnGroundLeft )
+ {
+ foot_pos_agent = ankle_left_pos_agent;
+ playSound = TRUE;
+ }
+
+ // did right foot hit the ground?
+ if ( onGroundRight && !mWasOnGroundRight )
+ {
+ foot_pos_agent = ankle_right_pos_agent;
+ playSound = TRUE;
+ }
+
+ mWasOnGroundLeft = onGroundLeft;
+ mWasOnGroundRight = onGroundRight;
+
+ if ( playSound )
+ {
+ const F32 STEP_VOLUME = 0.1f;
+ const LLUUID& step_sound_id = getStepSound();
+
+ LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent);
+
+ if (LLViewerParcelMgr::getInstance()->canHearSound(foot_pos_global)
+ && !LLMuteList::getInstance()->isMuted(getID(), LLMute::flagObjectSounds))
+ {
+ gAudiop->triggerSound(step_sound_id, getID(), STEP_VOLUME, LLAudioEngine::AUDIO_TYPE_AMBIENT, foot_pos_global);
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// computeUpdatePeriod()
+// Factored out from updateCharacter()
+// Set new value for mUpdatePeriod based on distance and various other factors.
+//------------------------------------------------------------------------
+void LLVOAvatar::computeUpdatePeriod()
+{
bool visually_muted = isVisuallyMuted();
- if (visible && (!isSelf() || visually_muted) && !mIsDummy && sUseImpostors && !mNeedsAnimUpdate && !sFreezeCounter)
+ if (mDrawable.notNull()
+ && isVisible()
+ && (!isSelf() || visually_muted)
+ && !isUIAvatar()
+ && sUseImpostors
+ && !mNeedsAnimUpdate
+ && !sFreezeCounter)
{
const LLVector4a* ext = mDrawable->getSpatialExtents();
LLVector4a size;
size.setSub(ext[1],ext[0]);
F32 mag = size.getLength3().getF32()*0.5f;
-
F32 impostor_area = 256.f*512.f*(8.125f - LLVOAvatar::sLODFactor*8.f);
if (visually_muted)
{ // visually muted avatars update at 16 hz
mUpdatePeriod = 16;
}
- else if ( ! shouldImpostor()
+ else if (! shouldImpostor()
|| mDrawable->mDistanceWRTCamera < 1.f + mag)
{ // first 25% of max visible avatars are not impostored
// also, don't impostor avatars whose bounding box may be penetrating the
@@ -3585,63 +3768,214 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
//nearby avatars, update the impostors more frequently.
mUpdatePeriod = 4;
}
-
- visible = (LLDrawable::getCurrentFrame()+mID.mData[0])%mUpdatePeriod == 0 ? TRUE : FALSE;
}
else
{
mUpdatePeriod = 1;
}
+}
- // don't early out for your own avatar, as we rely on your animations playing reliably
- // for example, the "turn around" animation when entering customize avatar needs to trigger
- // even when your avatar is offscreen
- if (!visible && !isSelf())
- {
- updateMotions(LLCharacter::HIDDEN_UPDATE);
- return FALSE;
- }
+//------------------------------------------------------------------------
+// updateOrientation()
+// Factored out from updateCharacter()
+// This is used by updateCharacter() to update the avatar's orientation:
+// - updates mTurning state
+// - updates rotation of the mRoot joint in the skeleton
+// - for self, calls setControlFlags() to notify the simulator about any turns
+//------------------------------------------------------------------------
+void LLVOAvatar::updateOrientation(LLAgent& agent, F32 speed, F32 delta_time)
+{
+ LLQuaternion iQ;
+ LLVector3 upDir( 0.0f, 0.0f, 1.0f );
+
+ // Compute a forward direction vector derived from the primitive rotation
+ // and the velocity vector. When walking or jumping, don't let body deviate
+ // more than 90 from the view, if necessary, flip the velocity vector.
- // change animation time quanta based on avatar render load
- if (!isSelf() && !mIsDummy)
+ LLVector3 primDir;
+ if (isSelf())
+ {
+ primDir = agent.getAtAxis() - projected_vec(agent.getAtAxis(), agent.getReferenceUpVector());
+ primDir.normalize();
+ }
+ else
+ {
+ primDir = getRotation().getMatrix3().getFwdRow();
+ }
+ LLVector3 velDir = getVelocity();
+ velDir.normalize();
+ if ( mSignaledAnimations.find(ANIM_AGENT_WALK) != mSignaledAnimations.end())
+ {
+ F32 vpD = velDir * primDir;
+ if (vpD < -0.5f)
+ {
+ velDir *= -1.0f;
+ }
+ }
+ LLVector3 fwdDir = lerp(primDir, velDir, clamp_rescale(speed, 0.5f, 2.0f, 0.0f, 1.0f));
+ if (isSelf() && gAgentCamera.cameraMouselook())
+ {
+ // make sure fwdDir stays in same general direction as primdir
+ if (gAgent.getFlying())
+ {
+ fwdDir = LLViewerCamera::getInstance()->getAtAxis();
+ }
+ else
+ {
+ LLVector3 at_axis = LLViewerCamera::getInstance()->getAtAxis();
+ LLVector3 up_vector = gAgent.getReferenceUpVector();
+ at_axis -= up_vector * (at_axis * up_vector);
+ at_axis.normalize();
+
+ F32 dot = fwdDir * at_axis;
+ if (dot < 0.f)
+ {
+ fwdDir -= 2.f * at_axis * dot;
+ fwdDir.normalize();
+ }
+ }
+ }
+
+ LLQuaternion root_rotation = mRoot->getWorldMatrix().quaternion();
+ F32 root_roll, root_pitch, root_yaw;
+ root_rotation.getEulerAngles(&root_roll, &root_pitch, &root_yaw);
+
+ // When moving very slow, the pelvis is allowed to deviate from the
+ // forward direction to allow it to hold its position while the torso
+ // and head turn. Once in motion, it must conform however.
+ BOOL self_in_mouselook = isSelf() && gAgentCamera.cameraMouselook();
+
+ LLVector3 pelvisDir( mRoot->getWorldMatrix().getFwdRow4().mV );
+
+ static LLCachedControl<F32> s_pelvis_rot_threshold_slow(gSavedSettings, "AvatarRotateThresholdSlow", 60.0);
+ static LLCachedControl<F32> s_pelvis_rot_threshold_fast(gSavedSettings, "AvatarRotateThresholdFast", 2.0);
+
+ F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, s_pelvis_rot_threshold_slow, s_pelvis_rot_threshold_fast);
+
+ if (self_in_mouselook)
+ {
+ pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR;
+ }
+ pelvis_rot_threshold *= DEG_TO_RAD;
+
+ F32 angle = angle_between( pelvisDir, fwdDir );
+
+ // The avatar's root is allowed to have a yaw that deviates widely
+ // from the forward direction, but if roll or pitch are off even
+ // a little bit we need to correct the rotation.
+ if(root_roll < 1.f * DEG_TO_RAD
+ && root_pitch < 5.f * DEG_TO_RAD)
+ {
+ // smaller correction vector means pelvis follows prim direction more closely
+ if (!mTurning && angle > pelvis_rot_threshold*0.75f)
+ {
+ mTurning = TRUE;
+ }
+
+ // use tighter threshold when turning
+ if (mTurning)
+ {
+ pelvis_rot_threshold *= 0.4f;
+ }
+
+ // am I done turning?
+ if (angle < pelvis_rot_threshold)
+ {
+ mTurning = FALSE;
+ }
+
+ LLVector3 correction_vector = (pelvisDir - fwdDir) * clamp_rescale(angle, pelvis_rot_threshold*0.75f, pelvis_rot_threshold, 1.0f, 0.0f);
+ fwdDir += correction_vector;
+ }
+ else
+ {
+ mTurning = FALSE;
+ }
+
+ // Now compute the full world space rotation for the whole body (wQv)
+ LLVector3 leftDir = upDir % fwdDir;
+ leftDir.normalize();
+ fwdDir = leftDir % upDir;
+ LLQuaternion wQv( fwdDir, leftDir, upDir );
+
+ if (isSelf() && mTurning)
+ {
+ if ((fwdDir % pelvisDir) * upDir > 0.f)
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_TURN_RIGHT);
+ }
+ else
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_TURN_LEFT);
+ }
+ }
+
+ // Set the root rotation, but do so incrementally so that it
+ // lags in time by some fixed amount.
+ //F32 u = LLSmoothInterpolation::getInterpolant(PELVIS_LAG);
+ F32 pelvis_lag_time = 0.f;
+ if (self_in_mouselook)
+ {
+ pelvis_lag_time = PELVIS_LAG_MOUSELOOK;
+ }
+ else if (mInAir)
+ {
+ pelvis_lag_time = PELVIS_LAG_FLYING;
+ // increase pelvis lag time when moving slowly
+ pelvis_lag_time *= clamp_rescale(mSpeedAccum, 0.f, 15.f, 3.f, 1.f);
+ }
+ else
+ {
+ pelvis_lag_time = PELVIS_LAG_WALKING;
+ }
+
+ F32 u = llclamp((delta_time / pelvis_lag_time), 0.0f, 1.0f);
+
+ mRoot->setWorldRotation( slerp(u, mRoot->getWorldRotation(), wQv) );
+}
+
+//------------------------------------------------------------------------
+// updateTimeStep()
+// Factored out from updateCharacter().
+//
+// Updates the time step used by the motion controller, based on area
+// and avatar count criteria. This will also stop the
+// ANIM_AGENT_WALK_ADJUST animation under some circumstances.
+// ------------------------------------------------------------------------
+void LLVOAvatar::updateTimeStep()
+{
+ if (!isSelf() && !isUIAvatar()) // ie, non-self avatars, and animated objects will be affected.
{
+ // Note that sInstances counts animated objects and
+ // standard avatars in the same bucket. Is this desirable?
F32 time_quantum = clamp_rescale((F32)sInstances.size(), 10.f, 35.f, 0.f, 0.25f);
F32 pixel_area_scale = clamp_rescale(mPixelArea, 100, 5000, 1.f, 0.f);
F32 time_step = time_quantum * pixel_area_scale;
+ // Extrema:
+ // If number of avs is 10 or less, time_step is unmodified (flagged with 0.0).
+ // If area of av is 5000 or greater, time_step is unmodified (flagged with 0.0).
+ // If number of avs is 35 or greater, and area of av is 100 or less,
+ // time_step takes the maximum possible value of 0.25.
+ // Other situations will give values within the (0, 0.25) range.
if (time_step != 0.f)
{
// disable walk motion servo controller as it doesn't work with motion timesteps
stopMotion(ANIM_AGENT_WALK_ADJUST);
removeAnimationData("Walk Speed");
}
+ // See SL-763 - playback with altered time step does not
+ // appear to work correctly, odd behavior for distant avatars.
+ // As of 11-2017, LLMotionController::updateMotions() will
+ // ignore the value here. Need to re-enable if it's every
+ // fixed.
mMotionController.setTimeStep(time_step);
- // LL_INFOS() << "Setting timestep to " << time_quantum * pixel_area_scale << LL_ENDL;
- }
-
- if (getParent() && !mIsSitting)
- {
- sitOnObject((LLViewerObject*)getParent());
- }
- else if (!getParent() && mIsSitting && !isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED))
- {
- getOffObject();
}
+}
- //--------------------------------------------------------------------
- // create local variables in world coords for region position values
- //--------------------------------------------------------------------
- F32 speed;
- LLVector3 normal;
-
- LLVector3 xyVel = getVelocity();
- xyVel.mV[VZ] = 0.0f;
- speed = xyVel.length();
- // remembering the value here prevents a display glitch if the
- // animation gets toggled during this update.
- bool was_sit_ground_constrained = isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED);
-
- if (!(mIsSitting && getParent()))
+void LLVOAvatar::updateRootPositionAndRotation(LLAgent& agent, F32 speed, bool was_sit_ground_constrained)
+{
+ if (!(isSitting() && getParent()))
{
// This case includes all configurations except sitting on an
// object, so does include ground sit.
@@ -3655,7 +3989,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
{
mTimeLast = animation_time;
- // put the pelvis at slaved position/mRotation
+ // Initially put the pelvis at slaved position/mRotation
// SL-315
mRoot->setWorldPosition( getPositionAgent() ); // first frame
mRoot->setWorldRotation( getRotation() );
@@ -3664,9 +3998,9 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
//--------------------------------------------------------------------
// dont' let dT get larger than 1/5th of a second
//--------------------------------------------------------------------
- F32 deltaTime = animation_time - mTimeLast;
+ F32 delta_time = animation_time - mTimeLast;
- deltaTime = llclamp( deltaTime, DELTA_TIME_MIN, DELTA_TIME_MAX );
+ delta_time = llclamp( delta_time, DELTA_TIME_MIN, DELTA_TIME_MAX );
mTimeLast = animation_time;
mSpeedAccum = (mSpeedAccum * 0.95f) + (speed * 0.05f);
@@ -3685,7 +4019,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
root_pos = gAgent.getPosGlobalFromAgent(getRenderPosition());
root_pos.mdV[VZ] += getVisualParamWeight(AVATAR_HOVER);
-
+ LLVector3 normal;
resolveHeightGlobal(root_pos, ground_under_pelvis, normal);
F32 foot_to_ground = (F32) (root_pos.mdV[VZ] - mPelvisToFoot - ground_under_pelvis.mdV[VZ]);
BOOL in_air = ((!LLWorld::getInstance()->getRegionFromPosGlobal(ground_under_pelvis)) ||
@@ -3707,185 +4041,154 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
// correct for the fact that the pelvis is not necessarily the center
// of the agent's physical representation
root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot;
- if (!mIsSitting && !was_sit_ground_constrained)
+ if (!isSitting() && !was_sit_ground_constrained)
{
root_pos += LLVector3d(getHoverOffset());
}
-
- LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos);
-
-
- if (newPosition != mRoot->getXform()->getWorldPosition())
- {
- mRoot->touch();
- // SL-315
- mRoot->setWorldPosition( newPosition ); // regular update
- }
+ LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this);
+ if (cav)
+ {
+ cav->matchVolumeTransform();
+ }
+ else
+ {
+ LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos);
+ if (newPosition != mRoot->getXform()->getWorldPosition())
+ {
+ mRoot->touch();
+ // SL-315
+ mRoot->setWorldPosition( newPosition ); // regular update
+ }
+ }
//--------------------------------------------------------------------
// Propagate viewer object rotation to root of avatar
//--------------------------------------------------------------------
- if (!isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS))
+ if (!isControlAvatar() && !isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS))
{
- LLQuaternion iQ;
- LLVector3 upDir( 0.0f, 0.0f, 1.0f );
-
- // Compute a forward direction vector derived from the primitive rotation
- // and the velocity vector. When walking or jumping, don't let body deviate
- // more than 90 from the view, if necessary, flip the velocity vector.
-
- LLVector3 primDir;
- if (isSelf())
- {
- primDir = agent.getAtAxis() - projected_vec(agent.getAtAxis(), agent.getReferenceUpVector());
- primDir.normalize();
- }
- else
- {
- primDir = getRotation().getMatrix3().getFwdRow();
- }
- LLVector3 velDir = getVelocity();
- velDir.normalize();
- if ( mSignaledAnimations.find(ANIM_AGENT_WALK) != mSignaledAnimations.end())
- {
- F32 vpD = velDir * primDir;
- if (vpD < -0.5f)
- {
- velDir *= -1.0f;
- }
- }
- LLVector3 fwdDir = lerp(primDir, velDir, clamp_rescale(speed, 0.5f, 2.0f, 0.0f, 1.0f));
- if (isSelf() && gAgentCamera.cameraMouselook())
- {
- // make sure fwdDir stays in same general direction as primdir
- if (gAgent.getFlying())
- {
- fwdDir = LLViewerCamera::getInstance()->getAtAxis();
- }
- else
- {
- LLVector3 at_axis = LLViewerCamera::getInstance()->getAtAxis();
- LLVector3 up_vector = gAgent.getReferenceUpVector();
- at_axis -= up_vector * (at_axis * up_vector);
- at_axis.normalize();
-
- F32 dot = fwdDir * at_axis;
- if (dot < 0.f)
- {
- fwdDir -= 2.f * at_axis * dot;
- fwdDir.normalize();
- }
- }
- }
-
- LLQuaternion root_rotation = mRoot->getWorldMatrix().quaternion();
- F32 root_roll, root_pitch, root_yaw;
- root_rotation.getEulerAngles(&root_roll, &root_pitch, &root_yaw);
-
- // When moving very slow, the pelvis is allowed to deviate from the
- // forward direction to allow it to hold it's position while the torso
- // and head turn. Once in motion, it must conform however.
- BOOL self_in_mouselook = isSelf() && gAgentCamera.cameraMouselook();
-
- LLVector3 pelvisDir( mRoot->getWorldMatrix().getFwdRow4().mV );
-
- static LLCachedControl<F32> s_pelvis_rot_threshold_slow(gSavedSettings, "AvatarRotateThresholdSlow", 60.0);
- static LLCachedControl<F32> s_pelvis_rot_threshold_fast(gSavedSettings, "AvatarRotateThresholdFast", 2.0);
-
- F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, s_pelvis_rot_threshold_slow, s_pelvis_rot_threshold_fast);
-
- if (self_in_mouselook)
- {
- pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR;
- }
- pelvis_rot_threshold *= DEG_TO_RAD;
-
- F32 angle = angle_between( pelvisDir, fwdDir );
-
- // The avatar's root is allowed to have a yaw that deviates widely
- // from the forward direction, but if roll or pitch are off even
- // a little bit we need to correct the rotation.
- if(root_roll < 1.f * DEG_TO_RAD
- && root_pitch < 5.f * DEG_TO_RAD)
- {
- // smaller correction vector means pelvis follows prim direction more closely
- if (!mTurning && angle > pelvis_rot_threshold*0.75f)
- {
- mTurning = TRUE;
- }
-
- // use tighter threshold when turning
- if (mTurning)
- {
- pelvis_rot_threshold *= 0.4f;
- }
-
- // am I done turning?
- if (angle < pelvis_rot_threshold)
- {
- mTurning = FALSE;
- }
-
- LLVector3 correction_vector = (pelvisDir - fwdDir) * clamp_rescale(angle, pelvis_rot_threshold*0.75f, pelvis_rot_threshold, 1.0f, 0.0f);
- fwdDir += correction_vector;
- }
- else
- {
- mTurning = FALSE;
- }
-
- // Now compute the full world space rotation for the whole body (wQv)
- LLVector3 leftDir = upDir % fwdDir;
- leftDir.normalize();
- fwdDir = leftDir % upDir;
- LLQuaternion wQv( fwdDir, leftDir, upDir );
-
- if (isSelf() && mTurning)
- {
- if ((fwdDir % pelvisDir) * upDir > 0.f)
- {
- gAgent.setControlFlags(AGENT_CONTROL_TURN_RIGHT);
- }
- else
- {
- gAgent.setControlFlags(AGENT_CONTROL_TURN_LEFT);
- }
- }
-
- // Set the root rotation, but do so incrementally so that it
- // lags in time by some fixed amount.
- //F32 u = LLSmoothInterpolation::getInterpolant(PELVIS_LAG);
- F32 pelvis_lag_time = 0.f;
- if (self_in_mouselook)
- {
- pelvis_lag_time = PELVIS_LAG_MOUSELOOK;
- }
- else if (mInAir)
- {
- pelvis_lag_time = PELVIS_LAG_FLYING;
- // increase pelvis lag time when moving slowly
- pelvis_lag_time *= clamp_rescale(mSpeedAccum, 0.f, 15.f, 3.f, 1.f);
- }
- else
- {
- pelvis_lag_time = PELVIS_LAG_WALKING;
- }
-
- F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f);
-
- mRoot->setWorldRotation( slerp(u, mRoot->getWorldRotation(), wQv) );
-
+ // Rotation fixups for avatars in motion.
+ // Skip for animated objects.
+ updateOrientation(agent, speed, delta_time);
}
}
else if (mDrawable.notNull())
{
+ // Sitting on an object - mRoot is slaved to mDrawable orientation.
LLVector3 pos = mDrawable->getPosition();
pos += getHoverOffset() * mDrawable->getRotation();
// SL-315
mRoot->setPosition(pos);
mRoot->setRotation(mDrawable->getRotation());
}
+}
+
+//------------------------------------------------------------------------
+// updateCharacter()
+//
+// This is called for all avatars, so there are 4 possible situations:
+//
+// 1) Avatar is your own. In this case the class is LLVOAvatarSelf,
+// isSelf() is true, and agent specifies the corresponding agent
+// information for you. In all the other cases, agent is irrelevant
+// and it would be less confusing if it were null or something.
+//
+// 2) Avatar is controlled by another resident. Class is LLVOAvatar,
+// and isSelf() is false.
+//
+// 3) Avatar is the controller for an animated object. Class is
+// LLControlAvatar and mIsDummy is true. Avatar is a purely
+// viewer-side entity with no representation on the simulator.
+//
+// 4) Avatar is a UI avatar used in some areas of the UI, such as when
+// previewing uploaded animations. Class is LLUIAvatar, and mIsDummy
+// is true. Avatar is purely viewer-side with no representation on the
+// simulator.
+//
+//------------------------------------------------------------------------
+BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
+{
+ updateDebugText();
+
+ if (!mIsBuilt)
+ {
+ return FALSE;
+ }
+
+ BOOL visible = isVisible();
+ bool is_control_avatar = isControlAvatar(); // capture state to simplify tracing
+ bool is_attachment = false;
+ if (is_control_avatar)
+ {
+ LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this);
+ is_attachment = cav && cav->mRootVolp && cav->mRootVolp->isAttachment(); // For attached animated objects
+ }
+
+ LLScopedContextString str("updateCharacter " + getFullname() + " is_control_avatar "
+ + boost::lexical_cast<std::string>(is_control_avatar)
+ + " is_attachment " + boost::lexical_cast<std::string>(is_attachment));
+
+ // For fading out the names above heads, only let the timer
+ // run if we're visible.
+ if (mDrawable.notNull() && !visible)
+ {
+ mTimeVisible.reset();
+ }
+
+ //--------------------------------------------------------------------
+ // The rest should only be done occasionally for far away avatars.
+ // Set mUpdatePeriod and visible based on distance and other criteria.
+ //--------------------------------------------------------------------
+ computeUpdatePeriod();
+ visible = (LLDrawable::getCurrentFrame()+mID.mData[0])%mUpdatePeriod == 0 ? TRUE : FALSE;
+
+ //--------------------------------------------------------------------
+ // Early out if not visible and not self
+ // don't early out for your own avatar, as we rely on your animations playing reliably
+ // for example, the "turn around" animation when entering customize avatar needs to trigger
+ // even when your avatar is offscreen
+ //--------------------------------------------------------------------
+ if (!visible && !isSelf())
+ {
+ updateMotions(LLCharacter::HIDDEN_UPDATE);
+ return FALSE;
+ }
+
+ //--------------------------------------------------------------------
+ // change animation time quanta based on avatar render load
+ //--------------------------------------------------------------------
+ // SL-763 the time step quantization does not currently work.
+ //updateTimeStep();
+
+ //--------------------------------------------------------------------
+ // Update sitting state based on parent and active animation info.
+ //--------------------------------------------------------------------
+ if (getParent() && !isSitting())
+ {
+ sitOnObject((LLViewerObject*)getParent());
+ }
+ else if (!getParent() && isSitting() && !isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED))
+ {
+ getOffObject();
+ }
+
+ //--------------------------------------------------------------------
+ // create local variables in world coords for region position values
+ //--------------------------------------------------------------------
+ LLVector3 xyVel = getVelocity();
+ xyVel.mV[VZ] = 0.0f;
+ F32 speed = xyVel.length();
+ // remembering the value here prevents a display glitch if the
+ // animation gets toggled during this update.
+ bool was_sit_ground_constrained = isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED);
+
+ //--------------------------------------------------------------------
+ // This does a bunch of state updating, including figuring out
+ // whether av is in the air, setting mRoot position and rotation
+ // In some cases, calls updateOrientation() for a lot of the
+ // work
+ // --------------------------------------------------------------------
+ updateRootPositionAndRotation(agent, speed, was_sit_ground_constrained);
//-------------------------------------------------------------------------
// Update character motions
@@ -3904,7 +4207,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
}
// Special handling for sitting on ground.
- if (!getParent() && (mIsSitting || was_sit_ground_constrained))
+ if (!getParent() && (isSitting() || was_sit_ground_constrained))
{
F32 off_z = LLVector3d(getHoverOffset()).mdV[VZ];
@@ -3921,90 +4224,18 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
// update head position
updateHeadOffset();
- //-------------------------------------------------------------------------
- // Find the ground under each foot, these are used for a variety
- // of things that follow
- //-------------------------------------------------------------------------
- LLVector3 ankle_left_pos_agent = mFootLeftp->getWorldPosition();
- LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition();
-
- LLVector3 ankle_left_ground_agent = ankle_left_pos_agent;
- LLVector3 ankle_right_ground_agent = ankle_right_pos_agent;
- resolveHeightAgent(ankle_left_pos_agent, ankle_left_ground_agent, normal);
- resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal);
-
- F32 leftElev = llmax(-0.2f, ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]);
- F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]);
-
- if (!mIsSitting)
- {
- //-------------------------------------------------------------------------
- // Figure out which foot is on ground
- //-------------------------------------------------------------------------
- if (!mInAir)
- {
- if ((leftElev < 0.0f) || (rightElev < 0.0f))
- {
- ankle_left_pos_agent = mFootLeftp->getWorldPosition();
- ankle_right_pos_agent = mFootRightp->getWorldPosition();
- leftElev = ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ];
- rightElev = ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ];
- }
- }
- }
-
- //-------------------------------------------------------------------------
// Generate footstep sounds when feet hit the ground
- //-------------------------------------------------------------------------
- const LLUUID AGENT_FOOTSTEP_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_LAND};
- const S32 NUM_AGENT_FOOTSTEP_ANIMS = LL_ARRAY_SIZE(AGENT_FOOTSTEP_ANIMS);
-
- if ( gAudiop && isAnyAnimationSignaled(AGENT_FOOTSTEP_ANIMS, NUM_AGENT_FOOTSTEP_ANIMS) )
- {
- BOOL playSound = FALSE;
- LLVector3 foot_pos_agent;
-
- BOOL onGroundLeft = (leftElev <= 0.05f);
- BOOL onGroundRight = (rightElev <= 0.05f);
-
- // did left foot hit the ground?
- if ( onGroundLeft && !mWasOnGroundLeft )
- {
- foot_pos_agent = ankle_left_pos_agent;
- playSound = TRUE;
- }
-
- // did right foot hit the ground?
- if ( onGroundRight && !mWasOnGroundRight )
- {
- foot_pos_agent = ankle_right_pos_agent;
- playSound = TRUE;
- }
-
- mWasOnGroundLeft = onGroundLeft;
- mWasOnGroundRight = onGroundRight;
-
- if ( playSound )
- {
- const F32 STEP_VOLUME = 0.1f;
- const LLUUID& step_sound_id = getStepSound();
-
- LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent);
-
- if (LLViewerParcelMgr::getInstance()->canHearSound(foot_pos_global)
- && !LLMuteList::getInstance()->isMuted(getID(), LLMute::flagObjectSounds))
- {
- gAudiop->triggerSound(step_sound_id, getID(), STEP_VOLUME, LLAudioEngine::AUDIO_TYPE_AMBIENT, foot_pos_global);
- }
- }
- }
+ updateFootstepSounds();
+ // Update child joints as needed.
mRoot->updateWorldMatrixChildren();
- //mesh vertices need to be reskinned
- mNeedsSkin = TRUE;
+ // System avatar mesh vertices need to be reskinned.
+ mNeedsSkin = TRUE;
+
return TRUE;
}
+
//-----------------------------------------------------------------------------
// updateHeadOffset()
//-----------------------------------------------------------------------------
@@ -4019,7 +4250,7 @@ void LLVOAvatar::updateHeadOffset()
{
midEyePt = midEyePt * ~mDrawable->getWorldRotation();
}
- if (mIsSitting)
+ if (isSitting())
{
mHeadOffset = midEyePt;
}
@@ -4115,7 +4346,7 @@ void LLVOAvatar::updateVisibility()
if (mIsDummy)
{
- visible = TRUE;
+ visible = FALSE;
}
else if (mDrawable.isNull())
{
@@ -4229,7 +4460,8 @@ void LLVOAvatar::updateVisibility()
}
else
{
- if (mMeshValid && mMeshInvisibleTime.getElapsedTimeF32() > TIME_BEFORE_MESH_CLEANUP)
+ if (mMeshValid &&
+ (isControlAvatar() || mMeshInvisibleTime.getElapsedTimeF32() > TIME_BEFORE_MESH_CLEANUP))
{
releaseMeshData();
}
@@ -4264,6 +4496,11 @@ U32 LLVOAvatar::renderSkinned()
return num_indices;
}
+ if (mDrawable.isNull())
+ {
+ return num_indices;
+ }
+
LLFace* face = mDrawable->getFace(0);
bool needs_rebuild = !face || !face->getVertexBuffer() || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY);
@@ -4430,7 +4667,7 @@ U32 LLVOAvatar::renderSkinned()
{
if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender)
{
- if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy)
+ if (isTextureVisible(TEX_HEAD_BAKED) || isUIAvatar())
{
LLViewerJoint* head_mesh = getViewerJoint(MESH_ID_HEAD);
if (head_mesh)
@@ -4440,7 +4677,7 @@ U32 LLVOAvatar::renderSkinned()
first_pass = FALSE;
}
}
- if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy)
+ if (isTextureVisible(TEX_UPPER_BAKED) || isUIAvatar())
{
LLViewerJoint* upper_mesh = getViewerJoint(MESH_ID_UPPER_BODY);
if (upper_mesh)
@@ -4450,7 +4687,7 @@ U32 LLVOAvatar::renderSkinned()
first_pass = FALSE;
}
- if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy)
+ if (isTextureVisible(TEX_LOWER_BAKED) || isUIAvatar())
{
LLViewerJoint* lower_mesh = getViewerJoint(MESH_ID_LOWER_BODY);
if (lower_mesh)
@@ -4479,7 +4716,7 @@ U32 LLVOAvatar::renderSkinned()
U32 LLVOAvatar::renderTransparent(BOOL first_pass)
{
U32 num_indices = 0;
- if( isWearingWearableType( LLWearableType::WT_SKIRT ) && (mIsDummy || isTextureVisible(TEX_SKIRT_BAKED)) )
+ if( isWearingWearableType( LLWearableType::WT_SKIRT ) && (isUIAvatar() || isTextureVisible(TEX_SKIRT_BAKED)) )
{
gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.25f);
LLViewerJoint* skirt_mesh = getViewerJoint(MESH_ID_SKIRT);
@@ -4507,18 +4744,15 @@ U32 LLVOAvatar::renderTransparent(BOOL first_pass)
}
first_pass = FALSE;
}
- // Can't test for baked hair being defined, since that won't always be the case (not all viewers send baked hair)
- // TODO: 1.25 will be able to switch this logic back to calling isTextureVisible();
- if ( (getImage(TEX_HAIR_BAKED, 0) && getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE)
- || LLDrawPoolAlpha::sShowDebugAlpha)
- {
- LLViewerJoint* hair_mesh = getViewerJoint(MESH_ID_HAIR);
- if (hair_mesh)
- {
- num_indices += hair_mesh->render(mAdjustedPixelArea, first_pass, mIsDummy);
- }
- first_pass = FALSE;
- }
+ if (isTextureVisible(TEX_HAIR_BAKED))
+ {
+ LLViewerJoint* hair_mesh = getViewerJoint(MESH_ID_HAIR);
+ if (hair_mesh)
+ {
+ num_indices += hair_mesh->render(mAdjustedPixelArea, first_pass, mIsDummy);
+ }
+ first_pass = FALSE;
+ }
if (LLPipeline::sImpostorRender)
{
gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
@@ -4558,7 +4792,7 @@ U32 LLVOAvatar::renderRigid()
gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f);
}
- if (isTextureVisible(TEX_EYES_BAKED) || mIsDummy)
+ if (isTextureVisible(TEX_EYES_BAKED) || isUIAvatar())
{
LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT);
LLViewerJoint* eyeball_right = getViewerJoint(MESH_ID_EYEBALL_RIGHT);
@@ -5105,10 +5339,13 @@ void LLVOAvatar::processAnimationStateChanges()
startMotion(ANIM_AGENT_WALK_ADJUST);
stopMotion(ANIM_AGENT_FLY_ADJUST);
}
- else if (mInAir && !mIsSitting)
+ else if (mInAir && !isSitting())
{
stopMotion(ANIM_AGENT_WALK_ADJUST);
- startMotion(ANIM_AGENT_FLY_ADJUST);
+ if (mEnableDefaultMotions)
+ {
+ startMotion(ANIM_AGENT_FLY_ADJUST);
+ }
}
else
{
@@ -5118,13 +5355,19 @@ void LLVOAvatar::processAnimationStateChanges()
if ( isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) )
{
- startMotion(ANIM_AGENT_TARGET);
+ if (mEnableDefaultMotions)
+ {
+ startMotion(ANIM_AGENT_TARGET);
+ }
stopMotion(ANIM_AGENT_BODY_NOISE);
}
else
{
stopMotion(ANIM_AGENT_TARGET);
- startMotion(ANIM_AGENT_BODY_NOISE);
+ if (mEnableDefaultMotions)
+ {
+ startMotion(ANIM_AGENT_BODY_NOISE);
+ }
}
// clear all current animations
@@ -5479,14 +5722,14 @@ bool LLVOAvatar::getRiggedMeshID(LLViewerObject* pVO, LLUUID& mesh_id)
LLVOVolume* pVObj = pVO->mDrawable->getVOVolume();
if ( pVObj )
{
- const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID(), pVObj );
+ const LLMeshSkinInfo* pSkinData = pVObj->getSkinInfo();
if (pSkinData
&& pSkinData->mJointNames.size() > JOINT_COUNT_REQUIRED_FOR_FULLRIG // full rig
&& pSkinData->mAlternateBindMatrix.size() > 0 )
- {
- mesh_id = pSkinData->mMeshID;
- return true;
- }
+ {
+ mesh_id = pSkinData->mMeshID;
+ return true;
+ }
}
}
return false;
@@ -5533,8 +5776,7 @@ bool LLVOAvatar::jointIsRiggedTo(const std::string& joint_name, const LLViewerOb
return false;
}
- LLUUID currentId = vobj->getVolume()->getParams().getSculptID();
- const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId, vobj );
+ const LLMeshSkinInfo* pSkinData = vobj->getSkinInfo();
if ( vobj && vobj->isAttachment() && vobj->isMesh() && pSkinData )
{
@@ -5561,6 +5803,17 @@ void LLVOAvatar::clearAttachmentOverrides()
pJoint->clearAttachmentScaleOverrides();
}
}
+
+ if (mPelvisFixups.count()>0)
+ {
+ mPelvisFixups.clear();
+ LLJoint* pJointPelvis = getJoint("mPelvis");
+ if (pJointPelvis)
+ {
+ pJointPelvis->setPosition( LLVector3( 0.0f, 0.0f, 0.0f) );
+ }
+ postPelvisSetRecalc();
+ }
}
//-----------------------------------------------------------------------------
@@ -5570,7 +5823,25 @@ void LLVOAvatar::rebuildAttachmentOverrides()
{
LLScopedContextString str("rebuildAttachmentOverrides " + getFullname());
- // Attachment points
+ LL_DEBUGS("AnimatedObjects") << "rebuilding" << LL_ENDL;
+ dumpStack("AnimatedObjectsStack");
+
+ clearAttachmentOverrides();
+
+ // Handle the case that we're resetting the skeleton of an animated object.
+ LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this);
+ if (control_av)
+ {
+ LLVOVolume *volp = control_av->mRootVolp;
+ if (volp)
+ {
+ LL_DEBUGS("Avatar") << volp->getID() << " adding attachment overrides for root vol, prim count "
+ << (S32) (1+volp->numChildren()) << LL_ENDL;
+ addAttachmentOverridesForObject(volp);
+ }
+ }
+
+ // Attached objects
for (attachment_map_t::iterator iter = mAttachmentPoints.begin();
iter != mAttachmentPoints.end();
++iter)
@@ -5581,24 +5852,33 @@ void LLVOAvatar::rebuildAttachmentOverrides()
for (LLViewerJointAttachment::attachedobjs_vec_t::iterator at_it = attachment_pt->mAttachedObjects.begin();
at_it != attachment_pt->mAttachedObjects.end(); ++at_it)
{
- addAttachmentOverridesForObject(*at_it);
+ LLViewerObject *vo = *at_it;
+ // Attached animated objects affect joints in their control
+ // avs, not the avs to which they are attached.
+ if (!vo->isAnimatedObject())
+ {
+ addAttachmentOverridesForObject(vo);
+ }
}
}
}
}
+
//-----------------------------------------------------------------------------
-// addAttachmentPosOverridesForObject
+// addAttachmentOverridesForObject
//-----------------------------------------------------------------------------
void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)
{
- LLVOAvatar *av = vo->getAvatarAncestor();
- if (!av || (av != this))
- {
+ if (vo->getAvatar() != this && vo->getAvatarAncestor() != this)
+ {
LL_WARNS("Avatar") << "called with invalid avatar" << LL_ENDL;
return;
- }
+ }
- LLScopedContextString str("addAttachmentOverridesForObject " + av->getFullname());
+ LLScopedContextString str("addAttachmentOverridesForObject " + vo->getAvatar()->getFullname());
+
+ LL_DEBUGS("AnimatedObjects") << "adding" << LL_ENDL;
+ dumpStack("AnimatedObjectsStack");
// Process all children
LLViewerObject::const_child_list_t& children = vo->getChildren();
@@ -5616,15 +5896,18 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)
{
return;
}
+
+ LLViewerObject *root_object = (LLViewerObject*)vobj->getRoot();
+ LL_DEBUGS("AnimatedObjects") << "trying to add attachment overrides for root object " << root_object->getID() << " prim is " << vobj << LL_ENDL;
if (vobj->isMesh() &&
((vobj->getVolume() && !vobj->getVolume()->isMeshAssetLoaded()) || !gMeshRepo.meshRezEnabled()))
{
+ LL_DEBUGS("AnimatedObjects") << "failed to add attachment overrides for root object " << root_object->getID() << " mesh asset not loaded" << LL_ENDL;
return;
}
- LLUUID currentId = vobj->getVolume()->getParams().getSculptID();
- const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId, vobj );
+ const LLMeshSkinInfo* pSkinData = vobj->getSkinInfo();
- if ( vobj && vobj->isAttachment() && vobj->isMesh() && pSkinData )
+ if ( vobj && vobj->isMesh() && pSkinData )
{
const int bindCnt = pSkinData->mAlternateBindMatrix.size();
const int jointCnt = pSkinData->mJointNames.size();
@@ -5636,6 +5919,8 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)
{
const F32 pelvisZOffset = pSkinData->mPelvisOffset;
const LLUUID& mesh_id = pSkinData->mMeshID;
+
+ LL_DEBUGS("AnimatedObjects") << "adding attachment overrides for " << mesh_id << " to root object " << root_object->getID() << LL_ENDL;
bool fullRig = (jointCnt>=JOINT_COUNT_REQUIRED_FOR_FULLRIG) ? true : false;
if ( fullRig )
{
@@ -5684,6 +5969,10 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)
}
}
}
+ else
+ {
+ LL_DEBUGS("AnimatedObjects") << "failed to add attachment overrides for root object " << root_object->getID() << " not mesh or no pSkinData" << LL_ENDL;
+ }
//Rebuild body data if we altered joints/pelvis
if ( pelvisGotSet )
@@ -5806,14 +6095,14 @@ void LLVOAvatar::showAttachmentOverrides(bool verbose) const
}
//-----------------------------------------------------------------------------
-// resetJointsOnDetach
+// removeAttachmentOverridesForObject
//-----------------------------------------------------------------------------
-void LLVOAvatar::resetJointsOnDetach(LLViewerObject *vo)
+void LLVOAvatar::removeAttachmentOverridesForObject(LLViewerObject *vo)
{
- LLVOAvatar *av = vo->getAvatarAncestor();
- if (!av || (av != this))
+ if (vo->getAvatar() != this && vo->getAvatarAncestor() != this)
{
LL_WARNS("Avatar") << "called with invalid avatar" << LL_ENDL;
+ return;
}
// Process all children
@@ -5822,21 +6111,21 @@ void LLVOAvatar::resetJointsOnDetach(LLViewerObject *vo)
it != children.end(); ++it)
{
LLViewerObject *childp = *it;
- resetJointsOnDetach(childp);
+ removeAttachmentOverridesForObject(childp);
}
// Process self.
LLUUID mesh_id;
if (getRiggedMeshID(vo,mesh_id))
{
- resetJointsOnDetach(mesh_id);
+ removeAttachmentOverridesForObject(mesh_id);
}
}
//-----------------------------------------------------------------------------
-// resetJointsOnDetach
+// removeAttachmentOverridesForObject
//-----------------------------------------------------------------------------
-void LLVOAvatar::resetJointsOnDetach(const LLUUID& mesh_id)
+void LLVOAvatar::removeAttachmentOverridesForObject(const LLUUID& mesh_id)
{
//Subsequent joints are relative to pelvis
avatar_joint_list_t::iterator iter = mSkeleton.begin();
@@ -5914,7 +6203,7 @@ void LLVOAvatar::getGround(const LLVector3 &in_pos_agent, LLVector3 &out_pos_age
LLVector3d z_vec(0.0f, 0.0f, 1.0f);
LLVector3d p0_global, p1_global;
- if (mIsDummy)
+ if (isUIAvatar())
{
outNorm.setVec(z_vec);
out_pos_agent = in_pos_agent;
@@ -5943,7 +6232,7 @@ F32 LLVOAvatar::getTimeDilation()
//-----------------------------------------------------------------------------
F32 LLVOAvatar::getPixelArea() const
{
- if (mIsDummy)
+ if (isUIAvatar())
{
return 100000.f;
}
@@ -6358,7 +6647,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.
@@ -6421,6 +6710,11 @@ const LLViewerJointAttachment *LLVOAvatar::attachObject(LLViewerObject *viewer_o
return 0;
}
+ if (!viewer_object->isAnimatedObject())
+ {
+ rebuildAttachmentOverrides();
+ }
+
updateVisualComplexity();
if (viewer_object->isSelected())
@@ -6450,19 +6744,63 @@ U32 LLVOAvatar::getNumAttachments() const
//-----------------------------------------------------------------------------
// canAttachMoreObjects()
+// Returns true if we can attach <n> more objects.
//-----------------------------------------------------------------------------
-BOOL LLVOAvatar::canAttachMoreObjects() const
+BOOL LLVOAvatar::canAttachMoreObjects(U32 n) const
{
- return (getNumAttachments() < MAX_AGENT_ATTACHMENTS);
+ return (getNumAttachments() + n) <= MAX_AGENT_ATTACHMENTS;
}
//-----------------------------------------------------------------------------
-// canAttachMoreObjects()
-// Returns true if we can attach <n> more objects.
+// getNumAnimatedObjectAttachments()
//-----------------------------------------------------------------------------
-BOOL LLVOAvatar::canAttachMoreObjects(U32 n) const
+U32 LLVOAvatar::getNumAnimatedObjectAttachments() const
{
- return (getNumAttachments() + n) <= MAX_AGENT_ATTACHMENTS;
+ U32 num_attachments = 0;
+ for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin();
+ iter != mAttachmentPoints.end();
+ ++iter)
+ {
+ const LLViewerJointAttachment *attachment_pt = (*iter).second;
+ num_attachments += attachment_pt->getNumAnimatedObjects();
+ }
+ return num_attachments;
+}
+
+//-----------------------------------------------------------------------------
+// getMaxAnimatedObjectAttachments()
+// Gets from simulator feature if available, otherwise 0.
+//-----------------------------------------------------------------------------
+S32 LLVOAvatar::getMaxAnimatedObjectAttachments() const
+{
+ S32 max_attach = 0;
+ // AXON REMOVE AFTER SERVER TESTING DONE
+ if (gSavedSettings.getBOOL("AnimatedObjectsIgnoreLimits"))
+ {
+ max_attach = MAX_AGENT_ATTACHMENTS;
+ }
+ else
+ {
+ if (gAgent.getRegion())
+ {
+ LLSD features;
+ gAgent.getRegion()->getSimulatorFeatures(features);
+ if (features.has("AnimatedObjects"))
+ {
+ max_attach = features["AnimatedObjects"]["MaxAgentAnimatedObjectAttachments"].asInteger();
+ }
+ }
+ }
+ return max_attach;
+}
+
+//-----------------------------------------------------------------------------
+// canAttachMoreAnimatedObjects()
+// Returns true if we can attach <n> more animated objects.
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::canAttachMoreAnimatedObjects(U32 n) const
+{
+ return (getNumAnimatedObjectAttachments() + n) <= getMaxAnimatedObjectAttachments();
}
//-----------------------------------------------------------------------------
@@ -6553,7 +6891,7 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )
LLUUID mesh_id;
if (getRiggedMeshID(pVO, mesh_id))
{
- resetJointsOnDetach(mesh_id);
+ // FIXME this seems like an odd place for this code.
if ( gAgentCamera.cameraCustomizeAvatar() )
{
gAgent.unpauseAnimation();
@@ -6578,9 +6916,13 @@ BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object)
if (attachment->isObjectAttached(viewer_object))
{
updateVisualComplexity();
+ bool is_animated_object = viewer_object->isAnimatedObject();
cleanupAttachedMesh( viewer_object );
-
attachment->removeObject(viewer_object);
+ if (!is_animated_object)
+ {
+ rebuildAttachmentOverrides();
+ }
LL_DEBUGS() << "Detaching object " << viewer_object->mID << " from " << attachment->getName() << LL_ENDL;
return TRUE;
}
@@ -6714,7 +7056,10 @@ void LLVOAvatar::getOffObject()
mRoot->setRotation(cur_rotation_world);
mRoot->getXform()->update();
- startMotion(ANIM_AGENT_BODY_NOISE);
+ if (mEnableDefaultMotions)
+ {
+ startMotion(ANIM_AGENT_BODY_NOISE);
+ }
if (isSelf())
{
@@ -6872,6 +7217,12 @@ void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color)
updateMeshTextures();
}
+// FIXME: We have an mVisible member, set in updateVisibility(), but this
+// function doesn't return it! isVisible() and mVisible are used
+// different places for different purposes. mVisible seems to be more
+// related to whether the actual avatar mesh is shown, and isVisible()
+// to whether anything about the avatar is displayed in the scene.
+// Maybe better naming could make this clearer?
BOOL LLVOAvatar::isVisible() const
{
return mDrawable.notNull()
@@ -6882,6 +7233,11 @@ BOOL LLVOAvatar::isVisible() const
// Determine if we have enough avatar data to render
bool LLVOAvatar::getIsCloud() const
{
+ if (mIsDummy)
+ {
+ return false;
+ }
+
return ( ((const_cast<LLVOAvatar*>(this))->visualParamWeightsAreDefault())// Do we have a shape?
|| ( !isTextureDefined(TEX_LOWER_BAKED)
|| !isTextureDefined(TEX_UPPER_BAKED)
@@ -7161,9 +7517,9 @@ bool LLVOAvatar::isTooComplex() const
// so that unlimited will completely disable the overly complex impostor rendering
// yes, this leaves them vulnerable to griefing objects... their choice
too_complex = ( max_render_cost > 0
- && ( mVisualComplexity > max_render_cost
- || (max_attachment_area > 0.0f && mAttachmentSurfaceArea > max_attachment_area)
- ));
+ && (mVisualComplexity > max_render_cost
+ || (max_attachment_area > 0.0f && mAttachmentSurfaceArea > max_attachment_area)
+ ));
}
return too_complex;
@@ -8800,6 +9156,7 @@ void LLVOAvatar::updateRegion(LLViewerRegion *regionp)
LLViewerObject::updateRegion(regionp);
}
+// virtual
std::string LLVOAvatar::getFullname() const
{
std::string name;
@@ -8846,6 +9203,11 @@ void LLVOAvatar::updateFreezeCounter(S32 counter)
BOOL LLVOAvatar::updateLOD()
{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+
if (isImpostor() && 0 != mDrawable->getNumFaces() && mDrawable->getFace(0)->hasGeometry())
{
return TRUE;
@@ -8886,10 +9248,10 @@ U32 LLVOAvatar::getPartitionType() const
void LLVOAvatar::updateImpostors()
{
LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD;
- LLCharacter::sAllowInstancesChange = FALSE;
- for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
- iter != LLCharacter::sInstances.end(); ++iter)
+ std::vector<LLCharacter*> instances_copy = LLCharacter::sInstances;
+ for (std::vector<LLCharacter*>::iterator iter = instances_copy.begin();
+ iter != instances_copy.end(); ++iter)
{
LLVOAvatar* avatar = (LLVOAvatar*) *iter;
if (!avatar->isDead() && avatar->isVisible()
@@ -8986,6 +9348,17 @@ void LLVOAvatar::updateImpostorRendering(U32 newMaxNonImpostorsValue)
void LLVOAvatar::idleUpdateRenderComplexity()
{
+ if (isControlAvatar())
+ {
+ LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this);
+ bool is_attachment = cav && cav->mRootVolp && cav->mRootVolp->isAttachment(); // For attached animated objects
+ if (is_attachment)
+ {
+ // ARC for animated object attachments is accounted with the avatar they're attached to.
+ return;
+ }
+ }
+
// Render Complexity
calculateUpdateRenderComplexity(); // Update mVisualComplexity if needed
@@ -9033,10 +9406,16 @@ void LLVOAvatar::idleUpdateRenderComplexity()
// Visual rank
info_line = llformat("%d rank", mVisibilityRank);
// Use grey for imposters, white for normal rendering or no impostors
- info_color.set(isImpostor() ? LLColor4::grey : LLColor4::white);
+ info_color.set(isImpostor() ? LLColor4::grey : (isControlAvatar() ? LLColor4::yellow : LLColor4::white));
info_style = LLFontGL::NORMAL;
mText->addLine(info_line, info_color, info_style);
+ // Triangle count
+ mText->addLine(std::string("VisTris ") + LLStringOps::getReadableNumber(mAttachmentVisibleTriangleCount),
+ info_color, info_style);
+ mText->addLine(std::string("EstMaxTris ") + LLStringOps::getReadableNumber(mAttachmentEstTriangleCount),
+ info_color, info_style);
+
// Attachment Surface Area
static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit", 1000.0f);
info_line = llformat("%.0f m^2", mAttachmentSurfaceArea);
@@ -9055,22 +9434,13 @@ void LLVOAvatar::idleUpdateRenderComplexity()
info_color.set(LLColor4::grey);
info_style = LLFontGL::NORMAL;
}
+
mText->addLine(info_line, info_color, info_style);
updateText(); // corrects position
}
}
-void LLVOAvatar::addAttachmentArea(F32 delta_area)
-{
- mAttachmentSurfaceArea += delta_area;
-}
-
-void LLVOAvatar::subtractAttachmentArea(F32 delta_area)
-{
- mAttachmentSurfaceArea = delta_area > mAttachmentSurfaceArea ? 0.0 : mAttachmentSurfaceArea - delta_area;
-}
-
void LLVOAvatar::updateVisualComplexity()
{
LL_DEBUGS("AvatarRender") << "avatar " << getID() << " appearance changed" << LL_ENDL;
@@ -9078,6 +9448,136 @@ void LLVOAvatar::updateVisualComplexity()
mVisualComplexityStale = true;
}
+// Account for the complexity of a single top-level object associated
+// with an avatar. This will be either an attached object or an animated
+// object.
+void LLVOAvatar::accountRenderComplexityForObject(
+ const LLViewerObject *attached_object,
+ const F32 max_attachment_complexity,
+ LLVOVolume::texture_cost_t& textures,
+ U32& cost,
+ hud_complexity_list_t& hud_complexity_list)
+{
+ if (attached_object && !attached_object->isHUDAttachment())
+ {
+ mAttachmentVisibleTriangleCount += attached_object->recursiveGetTriangleCount();
+ mAttachmentEstTriangleCount += attached_object->recursiveGetEstTrianglesMax();
+ mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea();
+
+ textures.clear();
+ const LLDrawable* drawable = attached_object->mDrawable;
+ if (drawable)
+ {
+ const LLVOVolume* volume = drawable->getVOVolume();
+ if (volume)
+ {
+ F32 attachment_total_cost = 0;
+ F32 attachment_volume_cost = 0;
+ F32 attachment_texture_cost = 0;
+ F32 attachment_children_cost = 0;
+ // AXON placeholder value, will revisit in testing.
+ const F32 animated_object_attachment_surcharge = 20000;
+
+ if (attached_object->isAnimatedObject())
+ {
+ attachment_volume_cost += animated_object_attachment_surcharge;
+ }
+ attachment_volume_cost += volume->getRenderCost(textures);
+
+ const_child_list_t children = volume->getChildren();
+ for (const_child_list_t::const_iterator child_iter = children.begin();
+ child_iter != children.end();
+ ++child_iter)
+ {
+ LLViewerObject* child_obj = *child_iter;
+ LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj );
+ if (child)
+ {
+ attachment_children_cost += child->getRenderCost(textures);
+ }
+ }
+
+ for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin();
+ volume_texture != textures.end();
+ ++volume_texture)
+ {
+ // add the cost of each individual texture in the linkset
+ attachment_texture_cost += volume_texture->second;
+ }
+ attachment_total_cost = attachment_volume_cost + attachment_texture_cost + attachment_children_cost;
+ LL_DEBUGS("ARCdetail") << "Attachment costs " << attached_object->getAttachmentItemID()
+ << " total: " << attachment_total_cost
+ << ", volume: " << attachment_volume_cost
+ << ", textures: " << attachment_texture_cost
+ << ", " << volume->numChildren()
+ << " children: " << attachment_children_cost
+ << LL_ENDL;
+ // Limit attachment complexity to avoid signed integer flipping of the wearer's ACI
+ cost += (U32)llclamp(attachment_total_cost, MIN_ATTACHMENT_COMPLEXITY, max_attachment_complexity);
+ }
+ }
+ }
+ if (isSelf()
+ && attached_object
+ && attached_object->isHUDAttachment()
+ && !attached_object->isTempAttachment()
+ && attached_object->mDrawable)
+ {
+ textures.clear();
+
+ mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea();
+
+ const LLVOVolume* volume = attached_object->mDrawable->getVOVolume();
+ if (volume)
+ {
+ LLHUDComplexity hud_object_complexity;
+ hud_object_complexity.objectName = attached_object->getAttachmentItemName();
+ hud_object_complexity.objectId = attached_object->getAttachmentItemID();
+ std::string joint_name;
+ gAgentAvatarp->getAttachedPointName(attached_object->getAttachmentItemID(), joint_name);
+ hud_object_complexity.jointName = joint_name;
+ // get cost and individual textures
+ hud_object_complexity.objectsCost += volume->getRenderCost(textures);
+ hud_object_complexity.objectsCount++;
+
+ LLViewerObject::const_child_list_t& child_list = attached_object->getChildren();
+ for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin();
+ iter != child_list.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ const LLVOVolume* chld_volume = dynamic_cast<LLVOVolume*>(childp);
+ if (chld_volume)
+ {
+ // get cost and individual textures
+ hud_object_complexity.objectsCost += chld_volume->getRenderCost(textures);
+ hud_object_complexity.objectsCount++;
+ }
+ }
+
+ hud_object_complexity.texturesCount += textures.size();
+
+ for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin();
+ volume_texture != textures.end();
+ ++volume_texture)
+ {
+ // add the cost of each individual texture (ignores duplicates)
+ hud_object_complexity.texturesCost += volume_texture->second;
+ LLViewerFetchedTexture *tex = LLViewerTextureManager::getFetchedTexture(volume_texture->first);
+ if (tex)
+ {
+ // Note: Texture memory might be incorect since texture might be still loading.
+ hud_object_complexity.texturesMemoryTotal += tex->getTextureMemory();
+ if (tex->getOriginalHeight() * tex->getOriginalWidth() >= HUD_OVERSIZED_TEXTURE_DATA_SIZE)
+ {
+ hud_object_complexity.largeTexturesCount++;
+ }
+ }
+ }
+ hud_complexity_list.push_back(hud_object_complexity);
+ }
+ }
+}
+
// Calculations for mVisualComplexity value
void LLVOAvatar::calculateUpdateRenderComplexity()
{
@@ -9116,7 +9616,25 @@ void LLVOAvatar::calculateUpdateRenderComplexity()
}
LL_DEBUGS("ARCdetail") << "Avatar body parts complexity: " << cost << LL_ENDL;
+ mAttachmentVisibleTriangleCount = 0;
+ mAttachmentEstTriangleCount = 0.f;
+ mAttachmentSurfaceArea = 0.f;
+
+ // A standalone animated object needs to be accounted for
+ // using its associated volume. Attached animated objects
+ // will be covered by the subsequent loop over attachments.
+ LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this);
+ if (control_av)
+ {
+ LLVOVolume *volp = control_av->mRootVolp;
+ if (volp && !volp->isAttachment())
+ {
+ accountRenderComplexityForObject(volp, max_attachment_complexity,
+ textures, cost, hud_complexity_list);
+ }
+ }
+ // Account for complexity of all attachments.
for (attachment_map_t::const_iterator attachment_point = mAttachmentPoints.begin();
attachment_point != mAttachmentPoints.end();
++attachment_point)
@@ -9127,112 +9645,8 @@ void LLVOAvatar::calculateUpdateRenderComplexity()
++attachment_iter)
{
const LLViewerObject* attached_object = (*attachment_iter);
- if (attached_object && !attached_object->isHUDAttachment())
- {
- textures.clear();
- const LLDrawable* drawable = attached_object->mDrawable;
- if (drawable)
- {
- const LLVOVolume* volume = drawable->getVOVolume();
- if (volume)
- {
- F32 attachment_total_cost = 0;
- F32 attachment_volume_cost = 0;
- F32 attachment_texture_cost = 0;
- F32 attachment_children_cost = 0;
-
- attachment_volume_cost += volume->getRenderCost(textures);
-
- const_child_list_t children = volume->getChildren();
- for (const_child_list_t::const_iterator child_iter = children.begin();
- child_iter != children.end();
- ++child_iter)
- {
- LLViewerObject* child_obj = *child_iter;
- LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj );
- if (child)
- {
- attachment_children_cost += child->getRenderCost(textures);
- }
- }
-
- for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin();
- volume_texture != textures.end();
- ++volume_texture)
- {
- // add the cost of each individual texture in the linkset
- attachment_texture_cost += volume_texture->second;
- }
- attachment_total_cost = attachment_volume_cost + attachment_texture_cost + attachment_children_cost;
- LL_DEBUGS("ARCdetail") << "Attachment costs " << attached_object->getAttachmentItemID()
- << " total: " << attachment_total_cost
- << ", volume: " << attachment_volume_cost
- << ", textures: " << attachment_texture_cost
- << ", " << volume->numChildren()
- << " children: " << attachment_children_cost
- << LL_ENDL;
- // Limit attachment complexity to avoid signed integer flipping of the wearer's ACI
- cost += (U32)llclamp(attachment_total_cost, MIN_ATTACHMENT_COMPLEXITY, max_attachment_complexity);
- }
- }
- }
- if (isSelf()
- && attached_object
- && attached_object->isHUDAttachment()
- && !attached_object->isTempAttachment()
- && attached_object->mDrawable)
- {
- textures.clear();
-
- const LLVOVolume* volume = attached_object->mDrawable->getVOVolume();
- if (volume)
- {
- LLHUDComplexity hud_object_complexity;
- hud_object_complexity.objectName = attached_object->getAttachmentItemName();
- hud_object_complexity.objectId = attached_object->getAttachmentItemID();
- std::string joint_name;
- gAgentAvatarp->getAttachedPointName(attached_object->getAttachmentItemID(), joint_name);
- hud_object_complexity.jointName = joint_name;
- // get cost and individual textures
- hud_object_complexity.objectsCost += volume->getRenderCost(textures);
- hud_object_complexity.objectsCount++;
-
- LLViewerObject::const_child_list_t& child_list = attached_object->getChildren();
- for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin();
- iter != child_list.end(); ++iter)
- {
- LLViewerObject* childp = *iter;
- const LLVOVolume* chld_volume = dynamic_cast<LLVOVolume*>(childp);
- if (chld_volume)
- {
- // get cost and individual textures
- hud_object_complexity.objectsCost += chld_volume->getRenderCost(textures);
- hud_object_complexity.objectsCount++;
- }
- }
-
- hud_object_complexity.texturesCount += textures.size();
-
- for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin();
- volume_texture != textures.end();
- ++volume_texture)
- {
- // add the cost of each individual texture (ignores duplicates)
- hud_object_complexity.texturesCost += volume_texture->second;
- LLViewerFetchedTexture *tex = LLViewerTextureManager::getFetchedTexture(volume_texture->first);
- if (tex)
- {
- // Note: Texture memory might be incorect since texture might be still loading.
- hud_object_complexity.texturesMemoryTotal += tex->getTextureMemory();
- if (tex->getOriginalHeight() * tex->getOriginalWidth() >= HUD_OVERSIZED_TEXTURE_DATA_SIZE)
- {
- hud_object_complexity.largeTexturesCount++;
- }
- }
- }
- hud_complexity_list.push_back(hud_object_complexity);
- }
- }
+ accountRenderComplexityForObject(attached_object, max_attachment_complexity,
+ textures, cost, hud_complexity_list);
}
}
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index bd89d4ef23..d7b67de2ad 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -50,6 +50,8 @@
#include "llviewertexlayer.h"
#include "material_codes.h" // LL_MCODE_END
#include "llviewerstats.h"
+#include "llvovolume.h"
+#include "llavatarrendernotifier.h"
extern const LLUUID ANIM_AGENT_BODY_NOISE;
extern const LLUUID ANIM_AGENT_BREATHE_ROT;
@@ -169,7 +171,8 @@ public:
LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point
LLVector4a* normal = NULL, // return the surface normal at the intersection point
LLVector4a* tangent = NULL); // return the surface tangent at the intersection point
- LLViewerObject* lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end,
+ virtual LLViewerObject* lineSegmentIntersectRiggedAttachments(
+ const LLVector4a& start, const LLVector4a& end,
S32 face = -1, // which face to check, -1 = ALL_SIDES
BOOL pick_transparent = FALSE,
BOOL pick_rigged = FALSE,
@@ -202,8 +205,8 @@ public:
LLJoint* getJoint(S32 num);
void addAttachmentOverridesForObject(LLViewerObject *vo);
- void resetJointsOnDetach(const LLUUID& mesh_id);
- void resetJointsOnDetach(LLViewerObject *vo);
+ void removeAttachmentOverridesForObject(const LLUUID& mesh_id);
+ void removeAttachmentOverridesForObject(LLViewerObject *vo);
bool jointIsRiggedTo(const std::string& joint_name);
bool jointIsRiggedTo(const std::string& joint_name, const LLViewerObject *vo);
void clearAttachmentOverrides();
@@ -233,6 +236,9 @@ public:
public:
virtual bool isSelf() const { return false; } // True if this avatar is for this viewer's agent
+ virtual bool isControlAvatar() const { return mIsControlAvatar; } // True if this avatar is a control av (no associated user)
+ virtual bool isUIAvatar() const { return mIsUIAvatar; } // True if this avatar is a supplemental av used in some UI views (no associated user)
+
private: //aligned members
LL_ALIGN_16(LLVector4a mImpostorExtents[2]);
@@ -240,8 +246,16 @@ private: //aligned members
// Updates
//--------------------------------------------------------------------
public:
- void updateDebugText();
+ void updateAppearanceMessageDebugText();
+ void updateAnimationDebugText();
+ virtual void updateDebugText();
virtual BOOL updateCharacter(LLAgent &agent);
+ void updateFootstepSounds();
+ void computeUpdatePeriod();
+ void updateOrientation(LLAgent &agent, F32 speed, F32 delta_time);
+ void updateTimeStep();
+ void updateRootPositionAndRotation(LLAgent &agent, F32 speed, bool was_sit_ground_constrained);
+
void idleUpdateVoiceVisualizer(bool voice_enabled);
void idleUpdateMisc(bool detailed_update);
virtual void idleUpdateAppearanceAnimation();
@@ -259,14 +273,17 @@ public:
static void invalidateNameTags();
void addNameTagLine(const std::string& line, const LLColor4& color, S32 style, const LLFontGL* font);
void idleUpdateRenderComplexity();
+ void accountRenderComplexityForObject(const LLViewerObject *attached_object,
+ const F32 max_attachment_complexity,
+ LLVOVolume::texture_cost_t& textures,
+ U32& cost,
+ hud_complexity_list_t& hud_complexity_list);
void calculateUpdateRenderComplexity();
static const U32 VISUAL_COMPLEXITY_UNKNOWN;
void updateVisualComplexity();
U32 getVisualComplexity() { return mVisualComplexity; }; // Numbers calculated here by rendering AV
F32 getAttachmentSurfaceArea() { return mAttachmentSurfaceArea; }; // estimated surface area of attachments
- void addAttachmentArea(F32 delta_area);
- void subtractAttachmentArea(F32 delta_area);
U32 getReportedVisualComplexity() { return mReportedVisualComplexity; }; // Numbers as reported by the SL server
void setReportedVisualComplexity(U32 value) { mReportedVisualComplexity = value; };
@@ -422,6 +439,8 @@ public:
private:
F32 mAttachmentSurfaceArea; //estimated surface area of attachments
+ U32 mAttachmentVisibleTriangleCount;
+ F32 mAttachmentEstTriangleCount;
bool shouldAlphaMask();
BOOL mNeedsSkin; // avatar has been animated and verts have not been updated
@@ -441,6 +460,14 @@ public:
VisualMuteSettings mVisuallyMuteSetting; // Always or never visually mute this AV
//--------------------------------------------------------------------
+ // animated object status
+ //--------------------------------------------------------------------
+public:
+ bool mIsControlAvatar;
+ bool mIsUIAvatar;
+ bool mEnableDefaultMotions;
+
+ //--------------------------------------------------------------------
// Morph masks
//--------------------------------------------------------------------
public:
@@ -739,9 +766,9 @@ public:
static LLVOAvatar* findAvatarFromAttachment(LLViewerObject* obj);
/*virtual*/ BOOL isWearingWearableType(LLWearableType::EType type ) const;
LLViewerObject * findAttachmentByID( const LLUUID & target_id ) const;
+ LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object);
protected:
- LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object);
void lazyAttach();
void rebuildRiggedAttachments( void );
@@ -761,10 +788,12 @@ public:
BOOL hasHUDAttachment() const;
LLBBox getHUDBBox() const;
void resetHUDAttachments();
- BOOL canAttachMoreObjects() const;
- BOOL canAttachMoreObjects(U32 n) const;
+ BOOL canAttachMoreObjects(U32 n=1) const;
+ S32 getMaxAnimatedObjectAttachments() const;
+ BOOL canAttachMoreAnimatedObjects(U32 n=1) const;
protected:
U32 getNumAttachments() const; // O(N), not O(1)
+ U32 getNumAnimatedObjectAttachments() const; // O(N), not O(1)
/** Wearables
** **
@@ -897,7 +926,7 @@ private:
**/
public:
- std::string getFullname() const; // Returns "FirstName LastName"
+ virtual std::string getFullname() const; // Returns "FirstName LastName"
std::string avString() const; // Frequently used string in log messages "Avatar '<full name'"
protected:
static void getAnimLabels(std::vector<std::string>* labels);
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index 10af524ee7..a2144d6466 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -2791,7 +2791,7 @@ BOOL LLVOAvatarSelf::needsRenderBeam()
// don't render selection beam on hud objects
is_touching_or_grabbing = FALSE;
}
- return is_touching_or_grabbing || (mState & AGENT_STATE_EDITING && LLSelectMgr::getInstance()->shouldShowSelection());
+ return is_touching_or_grabbing || (getAttachmentState() & AGENT_STATE_EDITING && LLSelectMgr::getInstance()->shouldShowSelection());
}
// static
diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp
index de63a3963c..5def0f0c0f 100644
--- a/indra/newview/llvograss.cpp
+++ b/indra/newview/llvograss.cpp
@@ -92,7 +92,7 @@ LLVOGrass::~LLVOGrass()
void LLVOGrass::updateSpecies()
{
- mSpecies = mState;
+ mSpecies = getAttachmentState();
if (!sSpeciesTable.count(mSpecies))
{
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 7b4d8ef329..69c2d2cccd 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -76,8 +76,14 @@
#include "lldatapacker.h"
#include "llviewershadermgr.h"
#include "llvoavatar.h"
+#include "llcontrolavatar.h"
+#include "llvoavatarself.h"
#include "llvocache.h"
#include "llmaterialmgr.h"
+#include "llanimationstates.h"
+#include "llinventorytype.h"
+#include "llviewerinventory.h"
+#include "llcallstack.h"
const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;
const F32 FORCE_CULL_AREA = 8.f;
@@ -302,6 +308,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
U32 block_num, EObjectUpdateType update_type,
LLDataPacker *dp)
{
+
LLColor4U color;
const S32 teDirtyBits = (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR|TEM_CHANGE_MEDIA);
@@ -315,6 +322,9 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
sculpt_id = sculpt_params->getSculptTexture();
sculpt_type = sculpt_params->getSculptType();
+
+ LL_DEBUGS("ObjectUpdate") << "uuid " << mID << " set sculpt_id " << sculpt_id << LL_ENDL;
+ dumpStack("ObjectUpdateStack");
}
if (!dp)
@@ -1096,16 +1106,34 @@ void LLVOVolume::updateSculptTexture()
}
+void LLVOVolume::updateVisualComplexity()
+{
+ LLVOAvatar* avatar = getAvatarAncestor();
+ if (avatar)
+ {
+ avatar->updateVisualComplexity();
+ }
+ LLVOAvatar* rigged_avatar = getAvatar();
+ if(rigged_avatar && (rigged_avatar != avatar))
+ {
+ rigged_avatar->updateVisualComplexity();
+ }
+}
+
void LLVOVolume::notifyMeshLoaded()
{
mSculptChanged = TRUE;
gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
- LLVOAvatar* avatar = getAvatar();
- if (avatar)
- {
- avatar->updateVisualComplexity();
- }
+ if (getAvatar() && !isAnimatedObject())
+ {
+ getAvatar()->addAttachmentOverridesForObject(this);
+ }
+ if (getControlAvatar() && isAnimatedObject())
+ {
+ getControlAvatar()->addAttachmentOverridesForObject(this);
+ }
+ updateVisualComplexity();
}
// sculpt replaces generate() for sculpted surfaces
@@ -1217,16 +1245,16 @@ void LLVOVolume::sculpt()
S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius)
{
S32 cur_detail;
- if (LLPipeline::sDynamicLOD)
- {
- // We've got LOD in the profile, and in the twist. Use radius.
- F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance;
- cur_detail = LLVolumeLODGroup::getDetailFromTan(ll_round(tan_angle, 0.01f));
- }
- else
- {
- cur_detail = llclamp((S32) (sqrtf(radius)*LLVOVolume::sLODFactor*4.f), 0, 3);
- }
+ if (LLPipeline::sDynamicLOD)
+ {
+ // We've got LOD in the profile, and in the twist. Use radius.
+ F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance;
+ cur_detail = LLVolumeLODGroup::getDetailFromTan(ll_round(tan_angle, 0.01f));
+ }
+ else
+ {
+ cur_detail = llclamp((S32) (sqrtf(radius)*LLVOVolume::sLODFactor*4.f), 0, 3);
+ }
return cur_detail;
}
@@ -1254,11 +1282,21 @@ BOOL LLVOVolume::calcLOD()
distance = avatar->mDrawable->mDistanceWRTCamera;
radius = avatar->getBinRadius();
+ if (distance <= 0.f || radius <= 0.f)
+ {
+ LL_DEBUGS("CalcLOD") << "avatar distance/radius uninitialized, skipping" << LL_ENDL;
+ return FALSE;
+ }
}
else
{
distance = mDrawable->mDistanceWRTCamera;
radius = getVolume() ? getVolume()->mLODScaleBias.scaledVec(getScale()).length() : getScale().length();
+ if (distance <= 0.f || radius <= 0.f)
+ {
+ LL_DEBUGS("CalcLOD") << "non-avatar distance/radius uninitialized, skipping" << LL_ENDL;
+ return FALSE;
+ }
}
//hold onto unmodified distance for debugging
@@ -1280,22 +1318,37 @@ BOOL LLVOVolume::calcLOD()
distance *= F_PI/3.f;
cur_detail = computeLODDetail(ll_round(distance, 0.01f),
- ll_round(radius, 0.01f));
-
+ ll_round(radius, 0.01f));
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TRIANGLE_COUNT) && mDrawable->getFace(0))
+ {
+ if (isRootEdit())
+ {
+ S32 total_tris = recursiveGetTriangleCount();
+ S32 est_max_tris = recursiveGetEstTrianglesMax();
+ setDebugText(llformat("TRIS SHOWN %d EST %d", total_tris, est_max_tris));
+ }
+ }
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LOD_INFO) &&
mDrawable->getFace(0))
{
- //setDebugText(llformat("%.2f:%.2f, %d", mDrawable->mDistanceWRTCamera, radius, cur_detail));
-
- setDebugText(llformat("%d", mDrawable->getFace(0)->getTextureIndex()));
+ // This is a debug display for LODs. Please don't put the texture index here.
+ setDebugText(llformat("%d", cur_detail));
}
if (cur_detail != mLOD)
{
+ LL_DEBUGS("CalcLOD") << "new LOD " << cur_detail << " change from " << mLOD
+ << " distance " << distance << " radius " << radius << " rampDist " << rampDist
+ << " drawable rigged? " << (mDrawable ? (S32) mDrawable->isState(LLDrawable::RIGGED) : (S32) -1)
+ << " mRiggedVolume " << (void*)getRiggedVolume()
+ << " distanceWRTCamera " << (mDrawable ? mDrawable->mDistanceWRTCamera : -1.f)
+ << LL_ENDL;
+
mAppAngle = ll_round((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f);
mLOD = cur_detail;
- return TRUE;
+
+ return TRUE;
}
return FALSE;
@@ -1312,6 +1365,16 @@ BOOL LLVOVolume::updateLOD()
if (lod_changed)
{
+ if (debugLoggingEnabled("AnimatedObjectsLinkset"))
+ {
+ if (isAnimatedObject() && isRiggedMesh())
+ {
+ std::string vobj_name = llformat("Vol%p", this);
+ F32 est_tris = getEstTrianglesMax();
+ LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " updateLOD to " << getLOD() << ", tris " << est_tris << LL_ENDL;
+ }
+ }
+
gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
mLODChanged = TRUE;
}
@@ -1386,7 +1449,8 @@ void LLVOVolume::updateFaceFlags()
BOOL LLVOVolume::setParent(LLViewerObject* parent)
{
BOOL ret = FALSE ;
- if (parent != getParent())
+ LLViewerObject *old_parent = (LLViewerObject*) getParent();
+ if (parent != old_parent)
{
ret = LLViewerObject::setParent(parent);
if (ret && mDrawable)
@@ -1394,6 +1458,7 @@ BOOL LLVOVolume::setParent(LLViewerObject* parent)
gPipeline.markMoved(mDrawable);
gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
}
+ onReparent(old_parent, parent);
}
return ret ;
@@ -1458,14 +1523,29 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED);
- // bool rigged = false;
+ if (getRiggedVolume())
+ {
+ // MAINT-8264 - better to use the existing call in calling
+ // func LLVOVolume::updateGeometry() if we can detect when
+ // updates needed, set REBUILD_RIGGED accordingly.
+
+ // Without the flag, this will remove unused rigged volumes, which we are not currently very aggressive about.
+ updateRiggedVolume();
+ }
+
LLVolume* volume = mRiggedVolume;
if (!volume)
{
volume = getVolume();
}
- // There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces()
+ bool any_valid_boxes = false;
+
+ if (getRiggedVolume())
+ {
+ LL_DEBUGS("RiggedBox") << "rebuilding box, volume face count " << getVolume()->getNumVolumeFaces() << " drawable face count " << mDrawable->getNumFaces() << LL_ENDL;
+ }
+ // There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces()
for (S32 i = 0;
i < getVolume()->getNumVolumeFaces() && i < mDrawable->getNumFaces() && i < getNumTEs();
i++)
@@ -1475,16 +1555,28 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
{
continue;
}
- res &= face->genVolumeBBoxes(*volume, i,
- mRelativeXform,
- (mVolumeImpl && mVolumeImpl->isVolumeGlobal()) || force_global);
+
+ BOOL face_res = face->genVolumeBBoxes(*volume, i,
+ mRelativeXform,
+ (mVolumeImpl && mVolumeImpl->isVolumeGlobal()) || force_global);
+ res &= face_res; // note that this result is never used
+ // MAINT-8264 - ignore bboxes of ill-formed faces.
+ if (!face_res)
+ {
+ continue;
+ }
if (rebuild)
{
- if (i == 0)
+ if (getRiggedVolume())
+ {
+ LL_DEBUGS("RiggedBox") << "rebuilding box, face " << i << " extents " << face->mExtents[0] << ", " << face->mExtents[1] << LL_ENDL;
+ }
+ if (!any_valid_boxes)
{
min = face->mExtents[0];
max = face->mExtents[1];
+ any_valid_boxes = true;
}
else
{
@@ -1493,17 +1585,28 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global)
}
}
}
-
- if (rebuild)
- {
- mDrawable->setSpatialExtents(min,max);
- min.add(max);
- min.mul(0.5f);
- mDrawable->setPositionGroup(min);
- }
- updateRadius();
- mDrawable->movePartition();
+ if (any_valid_boxes)
+ {
+ if (rebuild)
+ {
+ if (getRiggedVolume())
+ {
+ LL_DEBUGS("RiggedBox") << "rebuilding got extents " << min << ", " << max << LL_ENDL;
+ }
+ mDrawable->setSpatialExtents(min,max);
+ min.add(max);
+ min.mul(0.5f);
+ mDrawable->setPositionGroup(min);
+ }
+
+ updateRadius();
+ mDrawable->movePartition();
+ }
+ else
+ {
+ LL_DEBUGS("RiggedBox") << "genBBoxes failed to find any valid face boxes" << LL_ENDL;
+ }
return res;
}
@@ -1651,6 +1754,11 @@ bool LLVOVolume::lodOrSculptChanged(LLDrawable *drawable, BOOL &compiled)
if ((new_lod != old_lod) || mSculptChanged)
{
+ if (mDrawable->isState(LLDrawable::RIGGED))
+ {
+ updateVisualComplexity();
+ }
+
compiled = TRUE;
sNumLODChanges += new_num_faces;
@@ -3269,6 +3377,171 @@ BOOL LLVOVolume::setIsFlexible(BOOL is_flexible)
return res;
}
+const LLMeshSkinInfo* LLVOVolume::getSkinInfo() const
+{
+ if (getVolume())
+ {
+ return gMeshRepo.getSkinInfo(getVolume()->getParams().getSculptID(), this);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// virtual
+BOOL LLVOVolume::isRiggedMesh() const
+{
+ return isMesh() && getSkinInfo();
+}
+
+//----------------------------------------------------------------------------
+U32 LLVOVolume::getExtendedMeshFlags() const
+{
+ const LLExtendedMeshParams *param_block =
+ (const LLExtendedMeshParams *)getParameterEntry(LLNetworkData::PARAMS_EXTENDED_MESH);
+ if (param_block)
+ {
+ return param_block->getFlags();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+void LLVOVolume::onSetExtendedMeshFlags(U32 flags)
+{
+
+ // The isAnySelected() check was needed at one point to prevent
+ // graphics problems. These are now believed to be fixed so the
+ // check has been disabled.
+ if (/*!getRootEdit()->isAnySelected() &&*/ mDrawable.notNull())
+ {
+ // Need to trigger rebuildGeom(), which is where control avatars get created/removed
+ getRootEdit()->recursiveMarkForUpdate(TRUE);
+ }
+ if (isAttachment() && getAvatarAncestor())
+ {
+ updateVisualComplexity();
+ if (flags & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG)
+ {
+ // Making a rigged mesh into an animated object
+ getAvatarAncestor()->rebuildAttachmentOverrides();
+ }
+ else
+ {
+ // Making an animated object into a rigged mesh
+ getAvatarAncestor()->rebuildAttachmentOverrides();
+ }
+ }
+}
+
+void LLVOVolume::setExtendedMeshFlags(U32 flags)
+{
+ U32 curr_flags = getExtendedMeshFlags();
+ if (curr_flags != flags)
+ {
+ bool in_use = true;
+ setParameterEntryInUse(LLNetworkData::PARAMS_EXTENDED_MESH, in_use, true);
+ LLExtendedMeshParams *param_block =
+ (LLExtendedMeshParams *)getParameterEntry(LLNetworkData::PARAMS_EXTENDED_MESH);
+ if (param_block)
+ {
+ param_block->setFlags(flags);
+ }
+ parameterChanged(LLNetworkData::PARAMS_EXTENDED_MESH, true);
+ LL_DEBUGS("AnimatedObjects") << this
+ << " new flags " << flags << " curr_flags " << curr_flags
+ << ", calling onSetExtendedMeshFlags()"
+ << LL_ENDL;
+ onSetExtendedMeshFlags(flags);
+ }
+}
+
+bool LLVOVolume::canBeAnimatedObject() const
+{
+ F32 est_tris = recursiveGetEstTrianglesMax();
+ if (est_tris < 0 || est_tris > getAnimatedObjectMaxTris())
+ {
+ return false;
+ }
+ return true;
+}
+
+bool LLVOVolume::isAnimatedObject() const
+{
+ LLVOVolume *root_vol = (LLVOVolume*)getRootEdit();
+ bool root_is_animated_flag = root_vol->getExtendedMeshFlags() & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG;
+ return root_is_animated_flag;
+}
+
+// Called any time parenting changes for a volume. Update flags and
+// control av accordingly. This is called after parent has been
+// changed to new_parent, but before new_parent's mChildList has changed.
+
+// virtual
+void LLVOVolume::onReparent(LLViewerObject *old_parent, LLViewerObject *new_parent)
+{
+ LLVOVolume *old_volp = dynamic_cast<LLVOVolume*>(old_parent);
+
+ if (new_parent && !new_parent->isAvatar())
+ {
+ if (mControlAvatar.notNull())
+ {
+ // Here an animated object is being made the child of some
+ // other prim. Should remove the control av from the child.
+ LLControlAvatar *av = mControlAvatar;
+ mControlAvatar = NULL;
+ av->markForDeath();
+ }
+ }
+ if (old_volp && old_volp->isAnimatedObject())
+ {
+ if (old_volp->getControlAvatar())
+ {
+ // We have been removed from an animated object, need to do cleanup.
+ old_volp->getControlAvatar()->rebuildAttachmentOverrides();
+ old_volp->getControlAvatar()->updateAnimations();
+ }
+ }
+}
+
+// This needs to be called after onReparent(), because mChildList is
+// not updated until the end of LLViewerObject::addChild()
+
+// virtual
+void LLVOVolume::afterReparent()
+{
+ {
+ LL_DEBUGS("AnimatedObjects") << "new child added for parent "
+ << ((LLViewerObject*)getParent())->getID() << LL_ENDL;
+ }
+
+ if (isAnimatedObject() && getControlAvatar())
+ {
+ LL_DEBUGS("AnimatedObjects") << "adding attachment overrides, parent is animated object "
+ << ((LLViewerObject*)getParent())->getID() << LL_ENDL;
+
+ // MAINT-8239 - doing a full rebuild whenever parent is set
+ // makes the joint overrides load more robustly. In theory,
+ // addAttachmentOverrides should be sufficient, but in
+ // practice doing a full rebuild helps compensate for
+ // notifyMeshLoaded() not being called reliably enough.
+
+ // was: getControlAvatar()->addAttachmentOverridesForObject(this);
+ getControlAvatar()->rebuildAttachmentOverrides();
+ getControlAvatar()->updateAnimations();
+ }
+ else
+ {
+ LL_DEBUGS("AnimatedObjects") << "not adding overrides, parent: "
+ << ((LLViewerObject*)getParent())->getID()
+ << " isAnimated: " << isAnimatedObject() << " cav "
+ << getControlAvatar() << LL_ENDL;
+ }
+}
+
//----------------------------------------------------------------------------
void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point)
@@ -3330,7 +3603,7 @@ void LLVOVolume::updateRadius()
BOOL LLVOVolume::isAttachment() const
{
- return mState != 0 ;
+ return mAttachmentState != 0 ;
}
BOOL LLVOVolume::isHUDAttachment() const
@@ -3338,7 +3611,7 @@ BOOL LLVOVolume::isHUDAttachment() const
// *NOTE: we assume hud attachment points are in defined range
// since this range is constant for backwards compatibility
// reasons this is probably a reasonable assumption to make
- S32 attachment_id = ATTACHMENT_ID_FROM_STATE(mState);
+ S32 attachment_id = ATTACHMENT_ID_FROM_STATE(mAttachmentState);
return ( attachment_id >= 31 && attachment_id <= 38 );
}
@@ -3441,7 +3714,7 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const
S32 size = gMeshRepo.getMeshSize(volume_params.getSculptID(), getLOD());
if ( size > 0)
{
- if (gMeshRepo.getSkinInfo(volume_params.getSculptID(), this))
+ if (isRiggedMesh())
{
// weighted attachment - 1 point for every 3 bytes
weighted_mesh = 1;
@@ -3623,6 +3896,15 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const
return (U32)shame;
}
+F32 LLVOVolume::getEstTrianglesMax() const
+{
+ if (isMesh() && getVolume())
+ {
+ return gMeshRepo.getEstTrianglesMax(getVolume()->getParams().getSculptID());
+ }
+ return 0.f;
+}
+
F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const
{
F32 radius = getScale().length()*0.5f;
@@ -3714,6 +3996,21 @@ void LLVOVolume::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_u
{
mVolumeImpl->onParameterChanged(param_type, data, in_use, local_origin);
}
+ if (!local_origin && param_type == LLNetworkData::PARAMS_EXTENDED_MESH)
+ {
+ U32 extended_mesh_flags = getExtendedMeshFlags();
+ bool enabled = (extended_mesh_flags & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG);
+ bool was_enabled = (getControlAvatar() != NULL);
+ if (enabled != was_enabled)
+ {
+ LL_DEBUGS("AnimatedObjects") << this
+ << " calling onSetExtendedMeshFlags, enabled " << (U32) enabled
+ << " was_enabled " << (U32) was_enabled
+ << " local_origin " << (U32) local_origin
+ << LL_ENDL;
+ onSetExtendedMeshFlags(extended_mesh_flags);
+ }
+ }
if (mDrawable.notNull())
{
BOOL is_light = getIsLight();
@@ -3727,10 +4024,17 @@ void LLVOVolume::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_u
void LLVOVolume::setSelected(BOOL sel)
{
LLViewerObject::setSelected(sel);
- if (mDrawable.notNull())
- {
- markForUpdate(TRUE);
- }
+ if (isAnimatedObject())
+ {
+ getRootEdit()->recursiveMarkForUpdate(TRUE);
+ }
+ else
+ {
+ if (mDrawable.notNull())
+ {
+ markForUpdate(TRUE);
+ }
+ }
}
void LLVOVolume::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
@@ -3840,6 +4144,22 @@ const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const
return xform->getWorldMatrix();
}
+void LLVOVolume::markForUpdate(BOOL priority)
+{
+ if (debugLoggingEnabled("AnimatedObjectsLinkset"))
+ {
+ if (isAnimatedObject() && isRiggedMesh())
+ {
+ std::string vobj_name = llformat("Vol%p", this);
+ F32 est_tris = getEstTrianglesMax();
+ LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " markForUpdate, tris " << est_tris << LL_ENDL;
+ }
+ }
+
+ LLViewerObject::markForUpdate(priority);
+ mVolumeChanged = TRUE;
+}
+
LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const
{
LLVector3 ret = pos - getRenderPosition();
@@ -4092,9 +4412,9 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a&
bool LLVOVolume::treatAsRigged()
{
return isSelected() &&
- isAttachment() &&
- mDrawable.notNull() &&
- mDrawable->isState(LLDrawable::RIGGED);
+ (isAttachment() || isAnimatedObject()) &&
+ mDrawable.notNull() &&
+ mDrawable->isState(LLDrawable::RIGGED);
}
LLRiggedVolume* LLVOVolume::getRiggedVolume()
@@ -4124,9 +4444,7 @@ void LLVOVolume::updateRiggedVolume(bool force_update)
}
LLVolume* volume = getVolume();
-
- const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(volume->getParams().getSculptID(), this);
-
+ const LLMeshSkinInfo* skin = getSkinInfo();
if (!skin)
{
clearRiggedVolume();
@@ -4134,7 +4452,6 @@ void LLVOVolume::updateRiggedVolume(bool force_update)
}
LLVOAvatar* avatar = getAvatar();
-
if (!avatar)
{
clearRiggedVolume();
@@ -4149,7 +4466,6 @@ void LLVOVolume::updateRiggedVolume(bool force_update)
}
mRiggedVolume->update(skin, avatar, volume);
-
}
static LLTrace::BlockTimerStatHandle FTM_SKIN_RIGGED("Skin");
@@ -4179,6 +4495,21 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
{
copyVolumeFaces(volume);
}
+ else
+ {
+#if 1
+ bool is_paused = avatar && avatar->areAnimationsPaused();
+ if (is_paused)
+ {
+ S32 frames_paused = LLFrameTimer::getFrameCount() - avatar->getMotionController().getPausedFrame();
+ if (frames_paused > 2)
+ {
+ return;
+ }
+ }
+#endif
+ }
+
//build matrix palette
static const size_t kMaxJoints = LL_MAX_JOINTS_PER_MESH_OBJECT;
@@ -4187,6 +4518,9 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
U32 maxJoints = LLSkinningUtil::getMeshJointCount(skin);
LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, maxJoints, skin, avatar);
+ S32 rigged_vert_count = 0;
+ S32 rigged_face_count = 0;
+ LLVector4a box_min, box_max;
for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
{
const LLVolumeFace& vol_face = volume->getVolumeFace(i);
@@ -4208,6 +4542,8 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
LL_RECORD_BLOCK_TIME(FTM_SKIN_RIGGED);
U32 max_joints = LLSkinningUtil::getMaxJointCount();
+ rigged_vert_count += dst_face.mNumVertices;
+ rigged_face_count++;
for (U32 j = 0; j < dst_face.mNumVertices; ++j)
{
LLMatrix4a final_mat;
@@ -4222,11 +4558,17 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
}
//update bounding box
+ // VFExtents change
LLVector4a& min = dst_face.mExtents[0];
LLVector4a& max = dst_face.mExtents[1];
min = pos[0];
max = pos[1];
+ if (i==0)
+ {
+ box_min = min;
+ box_max = max;
+ }
for (U32 j = 1; j < dst_face.mNumVertices; ++j)
{
@@ -4234,6 +4576,9 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
max.setMax(max, pos[j]);
}
+ box_min.setMin(min,box_min);
+ box_max.setMax(max,box_max);
+
dst_face.mCenter->setAdd(dst_face.mExtents[0], dst_face.mExtents[1]);
dst_face.mCenter->mul(0.5f);
@@ -4252,6 +4597,10 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
}
}
}
+ mExtraDebugText = llformat("rigged %d/%d - box (%f %f %f) (%f %f %f)",
+ rigged_face_count, rigged_vert_count,
+ box_min[0], box_min[1], box_min[2],
+ box_max[0], box_max[1], box_max[2]);
}
U32 LLVOVolume::getPartitionType() const
@@ -4643,8 +4992,6 @@ static LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj)
void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
{
-
-
if (group->changeLOD())
{
group->mLastUpdateDistance = group->mDistance;
@@ -4665,27 +5012,19 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
group->mBuilt = 1.f;
- LLVOAvatar* pAvatarVO = NULL;
-
LLSpatialBridge* bridge = group->getSpatialPartition()->asBridge();
- if (bridge)
- {
- if (bridge->mAvatar.isNull())
- {
- LLViewerObject* vobj = bridge->mDrawable->getVObj();
- if (vobj)
- {
- bridge->mAvatar = vobj->getAvatar();
- }
- }
-
- pAvatarVO = bridge->mAvatar;
- }
+ LLViewerObject *vobj = NULL;
+ LLVOVolume *vol_obj = NULL;
- if (pAvatarVO)
+ if (bridge)
{
- pAvatarVO->subtractAttachmentArea( group->mSurfaceArea );
+ vobj = bridge->mDrawable->getVObj();
+ vol_obj = dynamic_cast<LLVOVolume*>(vobj);
}
+ if (vol_obj)
+ {
+ vol_obj->updateVisualComplexity();
+ }
group->mGeometryBytes = 0;
group->mSurfaceArea = 0;
@@ -4728,7 +5067,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_FACE_LIST);
//get all the faces into a list
- for (LLSpatialGroup::element_iter drawable_iter = group->getDataBegin(); drawable_iter != group->getDataEnd(); ++drawable_iter)
+ for (LLSpatialGroup::element_iter drawable_iter = group->getDataBegin();
+ drawable_iter != group->getDataEnd(); ++drawable_iter)
{
LLDrawable* drawablep = (LLDrawable*)(*drawable_iter)->getDrawable();
@@ -4743,12 +5083,14 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
}
LLVOVolume* vobj = drawablep->getVOVolume();
-
+
if (!vobj)
{
continue;
}
+ std::string vobj_name = llformat("Vol%p", vobj);
+
if (vobj->isMesh() &&
((vobj->getVolume() && !vobj->getVolume()->isMeshAssetLoaded()) || !gMeshRepo.meshRezEnabled()))
{
@@ -4762,29 +5104,39 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
group->mSurfaceArea += volume->getSurfaceArea() * llmax(llmax(scale.mV[0], scale.mV[1]), scale.mV[2]);
}
+ bool is_mesh = vobj->isMesh();
+ F32 est_tris = vobj->getEstTrianglesMax();
+
+ LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " rebuilding, isAttachment: " << (U32) vobj->isAttachment()
+ << " is_mesh " << is_mesh
+ << " est_tris " << est_tris
+ << " is_animated " << vobj->isAnimatedObject()
+ << " can_animate " << vobj->canBeAnimatedObject()
+ << " cav " << vobj->getControlAvatar()
+ << " lod " << vobj->getLOD()
+ << " drawable rigged " << (drawablep->isState(LLDrawable::RIGGED))
+ << " drawable state " << drawablep->getState()
+ << " playing " << (U32) (vobj->getControlAvatar() ? vobj->getControlAvatar()->mPlaying : false)
+ << " frame " << LLFrameTimer::getFrameCount()
+ << LL_ENDL;
+
llassert_always(vobj);
vobj->updateTextureVirtualSize(true);
vobj->preRebuild();
drawablep->clearState(LLDrawable::HAS_ALPHA);
- bool rigged = vobj->isAttachment() &&
- vobj->isMesh() &&
- gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID(), vobj);
+ // Standard rigged mesh attachments:
+ bool rigged = !vobj->isAnimatedObject() && vobj->isRiggedMesh() && vobj->isAttachment();
+ // Animated objects. Have to check for isRiggedMesh() to
+ // exclude static objects in animated object linksets.
+ rigged = rigged || (vobj->isAnimatedObject() && vobj->isRiggedMesh() &&
+ vobj->getControlAvatar() && vobj->getControlAvatar()->mPlaying);
+ vobj->updateControlAvatar();
+
bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic();
-
- bool is_rigged = false;
-
- if (rigged && pAvatarVO)
- {
- pAvatarVO->addAttachmentOverridesForObject(vobj);
- if (!LLApp::isExiting() && pAvatarVO->isSelf() && debugLoggingEnabled("AvatarAttachments"))
- {
- bool verbose = true;
- pAvatarVO->showAttachmentOverrides(verbose);
- }
- }
+ bool any_rigged_face = false;
//for each face
for (S32 i = 0; i < drawablep->getNumFaces(); i++)
@@ -4802,7 +5154,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
//sum up face verts and indices
drawablep->updateFaceSize(i);
- if (rigged)
+ if (rigged)
{
if (!facep->isState(LLFace::RIGGED))
{ //completely reset vertex buffer
@@ -4810,7 +5162,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
}
facep->setState(LLFace::RIGGED);
- is_rigged = true;
+ any_rigged_face = true;
//get drawpool of avatar with rigged face
LLDrawPoolAvatar* pool = get_avatar_drawpool(vobj);
@@ -5154,7 +5506,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
}
}
- if (is_rigged)
+ if (any_rigged_face)
{
if (!drawablep->isState(LLDrawable::RIGGED))
{
@@ -5168,6 +5520,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
else
{
drawablep->clearState(LLDrawable::RIGGED);
+ vobj->updateRiggedVolume();
}
}
}
@@ -5221,9 +5574,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
LLDrawable* drawablep = (LLDrawable*)(*drawable_iter)->getDrawable();
if(drawablep)
{
- drawablep->clearState(LLDrawable::REBUILD_ALL);
- }
- }
+ drawablep->clearState(LLDrawable::REBUILD_ALL);
+ }
+ }
}
group->mLastUpdateTime = gFrameTimeSeconds;
@@ -5236,11 +5589,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
}
mFaceList.clear();
-
- if (pAvatarVO)
- {
- pAvatarVO->addAttachmentArea( group->mSurfaceArea );
- }
}
static LLTrace::BlockTimerStatHandle FTM_REBUILD_MESH_FLUSH("Flush Mesh");
@@ -5269,6 +5617,17 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group)
if (drawablep && !drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL) && !drawablep->isState(LLDrawable::RIGGED) )
{
LLVOVolume* vobj = drawablep->getVOVolume();
+
+ if (debugLoggingEnabled("AnimatedObjectsLinkset"))
+ {
+ if (vobj->isAnimatedObject() && vobj->isRiggedMesh())
+ {
+ std::string vobj_name = llformat("Vol%p", vobj);
+ F32 est_tris = vobj->getEstTrianglesMax();
+ LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " rebuildMesh, tris " << est_tris << LL_ENDL;
+ }
+ }
+
vobj->preRebuild();
if (drawablep->isState(LLDrawable::ANIMATED_CHILD))
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index a331908320..4aec48e8ff 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -62,6 +62,8 @@ public:
}
void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume);
+
+ std::string mExtraDebugText;
};
// Base class for implementations of the volume - Primitive, Flexible Object, etc.
@@ -132,6 +134,7 @@ public:
/*virtual*/ const LLMatrix4 getRenderMatrix() const;
typedef std::map<LLUUID, S32> texture_cost_t;
U32 getRenderCost(texture_cost_t &textures) const;
+ /*virtual*/ F32 getEstTrianglesMax() const;
F32 getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const;
/*virtual*/ F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL) { return getStreamingCost(bytes, visible_bytes, NULL); }
@@ -159,7 +162,7 @@ public:
/*virtual*/ F32 getRadius() const { return mVObjRadius; };
const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const;
- void markForUpdate(BOOL priority) { LLViewerObject::markForUpdate(priority); mVolumeChanged = TRUE; }
+ void markForUpdate(BOOL priority);
void faceMappingChanged() { mFaceMappingChanged=TRUE; };
/*virtual*/ void onShift(const LLVector4a &shift_vector); // Called when the drawable shifts
@@ -259,12 +262,27 @@ public:
virtual BOOL isFlexible() const;
virtual BOOL isSculpted() const;
virtual BOOL isMesh() const;
+ virtual BOOL isRiggedMesh() const;
virtual BOOL hasLightTexture() const;
+
BOOL isVolumeGlobal() const;
BOOL canBeFlexible() const;
BOOL setIsFlexible(BOOL is_flexible);
+ const LLMeshSkinInfo* getSkinInfo() const;
+
+ // Extended Mesh Properties
+ U32 getExtendedMeshFlags() const;
+ void onSetExtendedMeshFlags(U32 flags);
+ void setExtendedMeshFlags(U32 flags);
+ bool canBeAnimatedObject() const;
+ bool isAnimatedObject() const;
+ virtual void onReparent(LLViewerObject *old_parent, LLViewerObject *new_parent);
+ virtual void afterReparent();
+
+ std::map<LLUUID, S32> mObjectSignaledAnimations; // requested state of Animation name/value
+
// Functions that deal with media, or media navigation
// Update this object's media data with the given media data array
@@ -298,7 +316,10 @@ public:
bool hasMedia() const;
LLVector3 getApproximateFaceNormal(U8 face_id);
-
+
+ // Flag any corresponding avatars as needing update.
+ void updateVisualComplexity();
+
void notifyMeshLoaded();
// Returns 'true' iff the media data for this object is in flight
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 1d083bb2fd..740da863af 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -3350,6 +3350,18 @@ void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags f
{
if (drawablep && !drawablep->isDead() && assertInitialized())
{
+ if (debugLoggingEnabled("AnimatedObjectsLinkset"))
+ {
+ LLVOVolume *vol_obj = drawablep->getVOVolume();
+ if (vol_obj && vol_obj->isAnimatedObject() && vol_obj->isRiggedMesh())
+ {
+ std::string vobj_name = llformat("Vol%p", vol_obj);
+ F32 est_tris = vol_obj->getEstTrianglesMax();
+ LL_DEBUGS("AnimatedObjectsLinkset") << vobj_name << " markRebuild, tris " << est_tris
+ << " priority " << (S32) priority << " flag " << std::hex << flag << LL_ENDL;
+ }
+ }
+
if (!drawablep->isState(LLDrawable::BUILT))
{
priority = true;
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index c9670a60f2..d7fbbdafb6 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -513,11 +513,11 @@ public:
RENDER_DEBUG_TEXTURE_AREA = 0x00000100,
RENDER_DEBUG_FACE_AREA = 0x00000200,
RENDER_DEBUG_PARTICLES = 0x00000400,
- RENDER_DEBUG_GLOW = 0x00000800,
+ RENDER_DEBUG_GLOW = 0x00000800, // not used
RENDER_DEBUG_TEXTURE_ANIM = 0x00001000,
RENDER_DEBUG_LIGHTS = 0x00002000,
RENDER_DEBUG_BATCH_SIZE = 0x00004000,
- RENDER_DEBUG_ALPHA_BINS = 0x00008000,
+ RENDER_DEBUG_ALPHA_BINS = 0x00008000, // not used
RENDER_DEBUG_RAYCAST = 0x00010000,
RENDER_DEBUG_AVATAR_DRAW_INFO = 0x00020000,
RENDER_DEBUG_SHADOW_FRUSTA = 0x00040000,
@@ -531,8 +531,9 @@ public:
RENDER_DEBUG_NORMALS = 0x04000000,
RENDER_DEBUG_LOD_INFO = 0x08000000,
RENDER_DEBUG_RENDER_COMPLEXITY = 0x10000000,
- RENDER_DEBUG_ATTACHMENT_BYTES = 0x20000000,
- RENDER_DEBUG_TEXEL_DENSITY = 0x40000000
+ RENDER_DEBUG_ATTACHMENT_BYTES = 0x20000000, // not used
+ RENDER_DEBUG_TEXEL_DENSITY = 0x40000000,
+ RENDER_DEBUG_TRIANGLE_COUNT = 0x80000000
};
public:
diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml
index ed3cc26851..63e2ed4bb9 100644
--- a/indra/newview/skins/default/xui/en/floater_tools.xml
+++ b/indra/newview/skins/default/xui/en/floater_tools.xml
@@ -2,7 +2,7 @@
<floater
positioning="cascading"
legacy_header_height="18"
- height="590"
+ height="600"
layout="topleft"
bg_opaque_image="Window_NoTitle_Foreground"
bg_alpha_image="Window_NoTitle_Background"
@@ -2131,7 +2131,7 @@ even though the user gets a free copy.
<panel
border="false"
follows="all"
- height="367"
+ height="387"
label="Features"
layout="topleft"
left_delta="0"
@@ -2169,13 +2169,23 @@ even though the user gets a free copy.
Edit object features:
</text>
<check_box
- height="19"
+ height="15"
+ label="Animated Mesh"
+ layout="topleft"
+ left="10"
+ name="Animated Mesh Checkbox Ctrl"
+ tool_tip="Allows rigged mesh objects to be animated independently"
+ top_pad="10"
+ width="121" />
+ <check_box
+ height="10"
label="Flexible Path"
+ follows="left|top"
layout="topleft"
left="10"
name="Flexible1D Checkbox Ctrl"
tool_tip="Allows object to flex about the Z axis (Client-side only)"
- top_pad="20"
+ top_pad="15"
width="121" />
<spinner
follows="left|top"
diff --git a/indra/newview/skins/default/xui/en/menu_object.xml b/indra/newview/skins/default/xui/en/menu_object.xml
index dc9622a27d..ce34508303 100644
--- a/indra/newview/skins/default/xui/en/menu_object.xml
+++ b/indra/newview/skins/default/xui/en/menu_object.xml
@@ -214,4 +214,20 @@
<menu_item_call.on_enable
function="EnableMuteParticle" />
</menu_item_call>
+ <menu_item_call
+ label="Dump XML"
+ name="Dump XML">
+ <menu_item_call.on_click
+ function="Advanced.AppearanceToXML" />
+ <menu_item_call.on_visible
+ function="Advanced.EnableAppearanceToXML"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Reset Skeleton"
+ name="Reset Skeleton">
+ <menu_item_call.on_click
+ function="Avatar.ResetSkeleton" />
+ <menu_item_call.on_visible
+ function="Avatar.EnableResetSkeleton"/>
+ </menu_item_call>
</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 92511167c0..eea7124eb8 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2648,6 +2648,16 @@
parameter="lod info" />
</menu_item_check>
<menu_item_check
+ label="Triangle Count"
+ name="Triangle Count">
+ <menu_item_check.on_check
+ function="Advanced.CheckInfoDisplay"
+ parameter="triangle count" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleInfoDisplay"
+ parameter="triangle count" />
+ </menu_item_check>
+ <menu_item_check
label="Build Queue"
name="Build Queue">
<menu_item_check.on_check
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 6aa6653f42..982d12d702 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -10197,6 +10197,14 @@ You have been teleported by the object '[OBJECT_NAME]' owned by an unknown user.
Unable to create requested object. The region is full.
</notification>
+ <notification
+ icon="alertmodal.tga"
+ name="CantCreateAnimatedObjectTooLarge"
+ type="notify">
+ <tag>fail</tag>
+Unable to create requested animated object because it exceeds the size limit.
+ </notification>
+
<notification
icon="alertmodal.tga"
name="CantAttackMultipleObjOneSpot"
@@ -10327,6 +10335,38 @@ You are not allowed to change this shape.
<notification
icon="alertmodal.tga"
+ name="NoPermsTooManyAttachedAnimatedObjects"
+ type="notify">
+ <tag>fail</tag>
+Operation would cause the number of attached animated objects to exceed the limit.
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="NoPermsLinkAnimatedObjectTooLarge"
+ type="notify">
+ <tag>fail</tag>
+Can't link these objects because the resulting animated object would exceed the size limit.
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="NoPermsSetFlagAnimatedObjectTooLarge"
+ type="notify">
+ <tag>fail</tag>
+Can't make this object into an animated object because it would exceed the size limit.
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="ErrorNoMeshData"
+ type="notify">
+ <tag>fail</tag>
+Server error: cannot complete this operation because mesh data is not loaded.
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
name="NoAccessToClaimObjects"
type="notify">
<tag>fail</tag>
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
index 80c84d9bea..bdeaeec970 100644
--- a/indra/test/test.cpp
+++ b/indra/test/test.cpp
@@ -34,7 +34,6 @@
*
*/
-
#include "linden_common.h"
#include "llerrorcontrol.h"
#include "lltut.h"
@@ -685,5 +684,4 @@ int main(int argc, char **argv)
return retval;
//delete mycallback;
-
}
diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg
index c56eaae6fe..18264ee59c 100755
--- a/scripts/messages/message_template.msg
+++ b/scripts/messages/message_template.msg
@@ -8,7 +8,7 @@ version 2.0
// for each type is listed below:
// Low: 423
// Medium: 18
-// High: 29
+// High: 30
// PLEASE UPDATE THIS WHEN YOU ADD A NEW MESSAGE!
@@ -3571,7 +3571,6 @@ version 2.0
}
}
-
// AvatarAppearance - Update visual params
{
AvatarAppearance Low 158 Trusted Zerocoded
@@ -7286,6 +7285,29 @@ version 2.0
// *************************************************************************
+// Object animation messages
+// *************************************************************************
+
+// Note this is basically identical to AvatarAnimation.
+// Needs to be a different message because existing viewers
+// have insufficiently smart handler functions.
+
+// ObjectAnimation - Update animation state
+// simulator --> viewer
+{
+ ObjectAnimation High 30 Trusted Unencoded
+ {
+ Sender Single
+ { ID LLUUID }
+ }
+ {
+ AnimationList Variable
+ { AnimID LLUUID }
+ { AnimSequenceID S32 }
+ }
+}
+
+// *************************************************************************
// Asset storage messages
// *************************************************************************
diff --git a/scripts/messages/message_template.msg.sha1 b/scripts/messages/message_template.msg.sha1
index 5bc06ec042..db87ad5e77 100755
--- a/scripts/messages/message_template.msg.sha1
+++ b/scripts/messages/message_template.msg.sha1
@@ -1 +1 @@
-337f351910b0c8821cb3d447bc6578516a043c80 \ No newline at end of file
+55df2c7135733c5da64ef8f01859b83a433a3a09 \ No newline at end of file
diff --git a/scripts/testing/lsl/axon_test_region_driver.lsl b/scripts/testing/lsl/axon_test_region_driver.lsl
new file mode 100644
index 0000000000..dcf146a9cf
--- /dev/null
+++ b/scripts/testing/lsl/axon_test_region_driver.lsl
@@ -0,0 +1,54 @@
+list buttons = ["anim start", "anim stop", "step", "verbose on", "verbose off", " "];
+string dialogInfo = "\nPlease make a choice.";
+
+key ToucherID;
+integer dialogChannel;
+integer listenHandle;
+integer commandChannel;
+
+default
+{
+ state_entry()
+ {
+ dialogChannel = -1 - (integer)("0x" + llGetSubString( (string)llGetKey(), -7, -1) );
+ commandChannel = -2001;
+ }
+
+ touch_start(integer num_detected)
+ {
+ ToucherID = llDetectedKey(0);
+ llListenRemove(listenHandle);
+ listenHandle = llListen(dialogChannel, "", ToucherID, "");
+ llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
+ //llSetTimerEvent(60.0); // Here we set a time limit for responses
+ }
+
+ listen(integer channel, string name, key id, string message)
+ {
+ if (message == "-")
+ {
+ llDialog(ToucherID, dialogInfo, buttons, dialogChannel);
+ return;
+ }
+
+ llListenRemove(listenHandle);
+ // stop timer since the menu was clicked
+ llSetTimerEvent(0);
+
+ //llOwnerSay("Sending message " + message + " on channel " + (string)commandChannel);
+ llRegionSay(commandChannel, message);
+ }
+
+ timer()
+ {
+ // stop timer
+ llSetTimerEvent(0);
+
+ llListenRemove(listenHandle);
+ //llWhisper(0, "Sorry. You snooze; you lose.");
+ }
+}
+
+// Local Variables:
+// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/axon_test_region_driver.lsl"
+// End:
diff --git a/scripts/testing/lsl/cycle_object_animations.lsl b/scripts/testing/lsl/cycle_object_animations.lsl
new file mode 100644
index 0000000000..46910e3656
--- /dev/null
+++ b/scripts/testing/lsl/cycle_object_animations.lsl
@@ -0,0 +1,118 @@
+integer listenHandle;
+integer verbose;
+integer current_animation_number;
+string NowPlaying;
+
+say_if_verbose(integer channel, string message)
+{
+ if (verbose)
+ {
+ llSay(channel, message);
+ }
+}
+
+stop_all_animations()
+{
+ integer count = llGetInventoryNumber(INVENTORY_ANIMATION);
+ string ItemName;
+ string NowPlaying;
+ while (count--)
+ {
+ ItemName = llGetInventoryName(INVENTORY_ANIMATION, count);
+ say_if_verbose(0, "Stopping " + ItemName);
+ llStopObjectAnimation(ItemName);
+ }
+}
+
+start_cycle_animations()
+{
+ current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION);
+ next_animation(); // Do first iteration without waiting for timer
+ llSetTimerEvent(5.0);
+}
+
+next_animation()
+{
+ string ItemName;
+ if (NowPlaying != "")
+ {
+ say_if_verbose(0, "Stopping " + NowPlaying);
+ llStopObjectAnimation(NowPlaying);
+ }
+ if (current_animation_number--)
+ {
+ ItemName = llGetInventoryName(INVENTORY_ANIMATION, current_animation_number);
+ say_if_verbose(0, "Starting " + ItemName);
+ llStartObjectAnimation(ItemName);
+ NowPlaying = ItemName;
+ }
+ else
+ {
+ // Start again at the top
+ current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION);
+ }
+}
+
+stop_cycle_animations()
+{
+ llSetTimerEvent(0);
+}
+
+default
+{
+ state_entry()
+ {
+ say_if_verbose(0, "Animated Object here");
+ listenHandle = llListen(-2001,"","","");
+ verbose = 0;
+
+ stop_all_animations();
+ }
+
+ listen(integer channel, string name, key id, string message)
+ {
+ //llOwnerSay("got message " + name + " " + (string) id + " " + message);
+ list words = llParseString2List(message,[" "],[]);
+ string command = llList2String(words,0);
+ string option = llList2String(words,1);
+ if (command=="anim")
+ {
+ stop_all_animations();
+ if (option=="start")
+ {
+ start_cycle_animations();
+ }
+ else if (option=="stop")
+ {
+ stop_cycle_animations();
+ }
+ }
+ if (command=="verbose")
+ {
+ if (option=="on")
+ {
+ verbose = 1;
+ }
+ else if (option=="off")
+ {
+ verbose = 0;
+ }
+ }
+ }
+
+ timer()
+ {
+ say_if_verbose(0, "timer triggered");
+ next_animation();
+ }
+
+ touch_start(integer total_number)
+ {
+ say_if_verbose(0, "Touch started.");
+ start_cycle_animations();
+ }
+}
+
+// Local Variables:
+// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/cycle_object_animations.lsl"
+// End:
diff --git a/scripts/testing/lsl/cycle_object_animations_v2.lsl b/scripts/testing/lsl/cycle_object_animations_v2.lsl
new file mode 100644
index 0000000000..60879fcce2
--- /dev/null
+++ b/scripts/testing/lsl/cycle_object_animations_v2.lsl
@@ -0,0 +1,133 @@
+integer listenHandle;
+integer verbose;
+integer current_animation_number;
+string NowPlaying;
+
+say_if_verbose(integer channel, string message)
+{
+ if (verbose)
+ {
+ llSay(channel, message);
+ }
+}
+
+stop_all_animations()
+{
+ list curr_anims = llGetObjectAnimationNames();
+ say_if_verbose(0,"stopping all, curr_anims are " + (string) curr_anims);
+ integer length = llGetListLength(curr_anims);
+ integer index = 0;
+ while (index < length)
+ {
+ string anim = llList2String(curr_anims, index);
+ say_if_verbose(0, "Stopping " + anim);
+ llStopObjectAnimation(anim);
+ // This check isn't really needed, just included to demonstrate is_animation_running()
+ if (is_animation_running(anim))
+ {
+ say_if_verbose(0, "ERROR - failed to stop " + anim + "!");
+ }
+ ++index;
+ }
+}
+
+integer is_animation_running(string anim)
+{
+ list curr_anims = llGetObjectAnimationNames();
+ return ~llListFindList(curr_anims, (list)anim);
+}
+
+start_cycle_animations()
+{
+ current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION);
+ next_animation(); // Do first iteration without waiting for timer
+ llSetTimerEvent(5.0);
+}
+
+next_animation()
+{
+ string ItemName;
+ if (NowPlaying != "")
+ {
+ say_if_verbose(0, "Stopping " + NowPlaying);
+ llStopObjectAnimation(NowPlaying);
+ }
+ if (current_animation_number--)
+ {
+ ItemName = llGetInventoryName(INVENTORY_ANIMATION, current_animation_number);
+ say_if_verbose(0, "Starting " + ItemName);
+ llStartObjectAnimation(ItemName);
+ key anim_id = llGetInventoryKey(ItemName);
+ say_if_verbose(0, "Started item " + ItemName + " inv key " + (string) anim_id);
+ NowPlaying = ItemName;
+ }
+ else
+ {
+ // Start again at the top
+ current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION);
+ }
+}
+
+stop_cycle_animations()
+{
+ llSetTimerEvent(0);
+}
+
+default
+{
+ state_entry()
+ {
+ say_if_verbose(0, "Animated Object here");
+ listenHandle = llListen(-2001,"","","");
+ verbose = 0;
+
+ stop_all_animations();
+ }
+
+ listen(integer channel, string name, key id, string message)
+ {
+ //llOwnerSay("got message " + name + " " + (string) id + " " + message);
+ list words = llParseString2List(message,[" "],[]);
+ string command = llList2String(words,0);
+ string option = llList2String(words,1);
+ if (command=="anim")
+ {
+ stop_all_animations();
+ if (option=="start")
+ {
+ start_cycle_animations();
+ }
+ else if (option=="stop")
+ {
+ stop_cycle_animations();
+ }
+ }
+ if (command=="verbose")
+ {
+ if (option=="on")
+ {
+ verbose = 1;
+ }
+ else if (option=="off")
+ {
+ verbose = 0;
+ }
+ }
+ }
+
+ timer()
+ {
+ say_if_verbose(0, "timer triggered");
+ next_animation();
+ }
+
+ touch_start(integer total_number)
+ {
+ say_if_verbose(0, "Touch started.");
+ start_cycle_animations();
+ }
+}
+
+// Local Variables:
+// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/cycle_object_animations_v2.lsl"
+// End:
diff --git a/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl b/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl
new file mode 100644
index 0000000000..cc5b899b67
--- /dev/null
+++ b/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl
@@ -0,0 +1,90 @@
+integer listenHandle;
+integer verbose;
+integer num_steps = 12;
+float circle_time = 5.0;
+integer circle_step;
+vector circle_pos;
+vector circle_center;
+float circle_radius;
+
+start_circle(vector center, float radius)
+{
+ vector currentPosition = llGetPos();
+ circle_center = center;
+ circle_radius = radius;
+ circle_step = 0;
+ llSetTimerEvent(circle_time/num_steps);
+ llTargetOmega(<0.0, 0.0, 1.0>, TWO_PI/circle_time, 1.0);
+}
+
+stop_circle()
+{
+ llSetTimerEvent(0);
+ llTargetOmega(<0.0, 0.0, 1.0>, TWO_PI/circle_time, 0.0);
+ llSetRegionPos(circle_center);
+}
+
+next_circle()
+{
+ float rad = (circle_step * TWO_PI)/num_steps;
+ float x = circle_center.x + llCos(rad)*circle_radius;
+ float y = circle_center.y + llSin(rad)*circle_radius;
+ float z = circle_center.z;
+ llSetRegionPos(<x,y,z>);
+ circle_step = (circle_step+1)%num_steps;
+}
+
+default
+{
+ state_entry()
+ {
+ //llSay(0, "Hello, Avatar!");
+ listenHandle = llListen(-2001,"","","");
+ verbose = 0;
+ circle_center = llGetPos();
+ }
+
+ listen(integer channel, string name, key id, string message)
+ {
+ //llOwnerSay("got message " + name + " " + (string) id + " " + message);
+ list words = llParseString2List(message,[" "],[]);
+ string command = llList2String(words,0);
+ string option = llList2String(words,1);
+ if (command=="anim")
+ {
+ if (option=="start")
+ {
+ start_circle(llGetPos(), 3.0);
+ }
+ else if (option=="stop")
+ {
+ stop_circle();
+ }
+ }
+ if (command=="verbose")
+ {
+ if (option=="on")
+ {
+ verbose = 1;
+ }
+ else if (option=="off")
+ {
+ verbose = 0;
+ }
+ }
+ if (command=="step")
+ {
+ llSetTimerEvent(0);
+ next_circle();
+ }
+ }
+
+ timer()
+ {
+ next_circle();
+ }
+}
+
+// Local Variables:
+// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl"
+// End: