summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xetc/message.xml8
-rw-r--r--indra/llappearance/llavatarappearance.cpp32
-rw-r--r--indra/llappearance/llavatarappearance.h3
-rw-r--r--indra/llcharacter/lljoint.cpp9
-rw-r--r--indra/llcharacter/lljoint.h10
-rw-r--r--indra/llcharacter/llmotioncontroller.cpp10
-rw-r--r--indra/llcharacter/llmotioncontroller.h6
-rw-r--r--indra/llcommon/llcallstack.h7
-rw-r--r--indra/llcommon/llerror.cpp128
-rw-r--r--indra/llcommon/llerrorcontrol.h6
-rw-r--r--indra/llcommon/llmemory.h6
-rw-r--r--indra/llcommon/llstring.cpp19
-rw-r--r--indra/llcommon/llstring.h3
-rw-r--r--indra/llmath/CMakeLists.txt3
-rw-r--r--indra/llmath/llmatrix4a.cpp80
-rw-r--r--indra/llmath/llmatrix4a.h14
-rw-r--r--indra/llmath/llrigginginfo.cpp159
-rw-r--r--indra/llmath/llrigginginfo.h100
-rw-r--r--indra/llmath/llvector4a.h7
-rw-r--r--indra/llmath/llvolume.cpp104
-rw-r--r--indra/llmath/llvolume.h7
-rw-r--r--indra/llmath/v3math.cpp36
-rw-r--r--indra/llmath/v3math.h2
-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.cpp41
-rw-r--r--indra/llprimitive/llmodel.h5
-rw-r--r--indra/llprimitive/llmodelloader.h1
-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/VIEWER_VERSION.txt2
-rw-r--r--indra/newview/app_settings/logcontrol.xml17
-rw-r--r--indra/newview/app_settings/settings.xml88
-rw-r--r--indra/newview/llagent.cpp10
-rw-r--r--indra/newview/llappearancemgr.cpp5
-rw-r--r--indra/newview/llappviewer.cpp8
-rw-r--r--indra/newview/llappviewerwin32.cpp5
-rw-r--r--indra/newview/llappviewerwin32.h1
-rw-r--r--indra/newview/llavatarrenderinfoaccountant.cpp12
-rw-r--r--indra/newview/llcontrolavatar.cpp642
-rw-r--r--indra/newview/llcontrolavatar.h113
-rw-r--r--indra/newview/lldrawable.cpp36
-rw-r--r--indra/newview/lldrawpool.cpp2
-rw-r--r--indra/newview/lldrawpoolavatar.cpp59
-rw-r--r--indra/newview/lldrawpoolavatar.h2
-rw-r--r--indra/newview/llface.cpp99
-rw-r--r--indra/newview/llfloaterbvhpreview.cpp8
-rw-r--r--indra/newview/llfloaterimagepreview.cpp9
-rw-r--r--indra/newview/llfloatermodelpreview.cpp43
-rw-r--r--indra/newview/llfloatermodelpreview.h5
-rw-r--r--indra/newview/llmeshrepository.cpp266
-rw-r--r--indra/newview/llmeshrepository.h59
-rw-r--r--indra/newview/llpanelvolume.cpp66
-rw-r--r--indra/newview/llpanelvolume.h1
-rw-r--r--indra/newview/llpipelinelistener.cpp6
-rw-r--r--indra/newview/llsceneview.cpp19
-rw-r--r--indra/newview/llselectmgr.cpp115
-rw-r--r--indra/newview/llselectmgr.h7
-rw-r--r--indra/newview/llskinningutil.cpp234
-rw-r--r--indra/newview/llskinningutil.h23
-rw-r--r--indra/newview/llspatialpartition.cpp78
-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.cpp21
-rw-r--r--indra/newview/lltoolselect.cpp3
-rw-r--r--indra/newview/lltoolselectrect.cpp3
-rw-r--r--indra/newview/lluiavatar.cpp61
-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.cpp90
-rw-r--r--indra/newview/llviewermenu.h2
-rw-r--r--indra/newview/llviewermessage.cpp80
-rw-r--r--indra/newview/llviewermessage.h1
-rw-r--r--indra/newview/llviewerobject.cpp364
-rw-r--r--indra/newview/llviewerobject.h54
-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.cpp59
-rw-r--r--indra/newview/llviewerregion.h1
-rw-r--r--indra/newview/llviewerwindow.cpp13
-rw-r--r--indra/newview/llvoavatar.cpp2507
-rw-r--r--indra/newview/llvoavatar.h82
-rw-r--r--indra/newview/llvoavatarself.cpp10
-rw-r--r--indra/newview/llvoavatarself.h3
-rw-r--r--indra/newview/llvograss.cpp2
-rw-r--r--indra/newview/llvovolume.cpp761
-rw-r--r--indra/newview/llvovolume.h37
-rw-r--r--indra/newview/pipeline.cpp17
-rw-r--r--indra/newview/pipeline.h78
-rw-r--r--indra/newview/skins/default/xui/en/floater_model_preview.xml1
-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.xml20
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml48
-rw-r--r--indra/test/test.cpp2
-rw-r--r--scripts/content_tools/anim_tool.py17
-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
107 files changed, 6405 insertions, 1468 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 60359ca304..92217c60ff 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -190,7 +190,8 @@ LLAvatarAppearance::LLAvatarAppearance(LLWearableData* wearable_data) :
mNumBones(0),
mNumCollisionVolumes(0),
mCollisionVolumes(NULL),
- mIsBuilt(FALSE)
+ mIsBuilt(FALSE),
+ mInitFlags(0)
{
llassert_always(mWearableData);
mBakedTextureDatas.resize(LLAvatarAppearanceDefines::BAKED_NUM_INDICES);
@@ -281,6 +282,8 @@ void LLAvatarAppearance::initInstance()
buildCharacter();
+ mInitFlags |= 1<<0;
+
}
// virtual
@@ -1724,7 +1727,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)
{
@@ -1739,13 +1742,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..7815c1844b 100644
--- a/indra/llappearance/llavatarappearance.h
+++ b/indra/llappearance/llavatarappearance.h
@@ -70,6 +70,7 @@ public:
static void initClass();
static void cleanupClass(); // Cleanup data that's only init'd once per class.
virtual void initInstance(); // Called after construction to initialize the instance.
+ S32 mInitFlags;
virtual BOOL loadSkeletonNode();
BOOL loadMeshNodes();
BOOL loadLayersets();
@@ -227,7 +228,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..e2f512f86e 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -428,13 +428,6 @@ void LLJoint::addAttachmentPosOverride( const LLVector3& pos, const LLUUID& mesh
{
return;
}
- // BENTO
- // Not clear pelvis overrides are meaningful/useful.
- //if (mName == "mPelvis")
- //{
- // return;
- //}
-
LLVector3 before_pos;
LLUUID before_mesh_id;
bool has_active_override_before = hasAttachmentPosOverride( before_pos, before_mesh_id );
@@ -881,7 +874,7 @@ void LLJoint::setWorldRotation( const LLQuaternion& rot )
//--------------------------------------------------------------------
const LLVector3& LLJoint::getScale()
{
- return mXform.getScale();
+ return mXform.getScale();
}
//--------------------------------------------------------------------
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
index 0c8fbfebb0..8112d246f2 100644
--- a/indra/llcharacter/lljoint.h
+++ b/indra/llcharacter/lljoint.h
@@ -70,6 +70,16 @@ private:
map_type m_map;
};
+inline bool operator==(const LLVector3OverrideMap& a, const LLVector3OverrideMap& b)
+{
+ return a.getMap() == b.getMap();
+}
+
+inline bool operator!=(const LLVector3OverrideMap& a, const LLVector3OverrideMap& b)
+{
+ return !(a == b);
+}
+
//-----------------------------------------------------------------------------
// class LLJoint
//-----------------------------------------------------------------------------
diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp
index 35e76f1d9d..c48d02b652 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),
@@ -441,7 +441,8 @@ BOOL LLMotionController::stopMotionLocally(const LLUUID &id, BOOL stop_immediate
{
// if already inactive, return false
LLMotion *motion = findMotion(id);
- return stopMotionInstance(motion, stop_immediate);
+ // SL-1290: always stop immediate if paused
+ return stopMotionInstance(motion, stop_immediate||mPaused);
}
BOOL LLMotionController::stopMotionInstance(LLMotion* motion, BOOL stop_immediate)
@@ -814,6 +815,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 +1130,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/llerror.cpp b/indra/llcommon/llerror.cpp
index 06c7aef8ab..49ed8b495d 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -29,6 +29,7 @@
#include "llerror.h"
#include "llerrorcontrol.h"
+#include "llsdutil.h"
#include <cctype>
#ifdef __GNUC__
@@ -89,9 +90,14 @@ namespace {
{
closelog();
}
-
+
+ virtual bool enabled() override
+ {
+ return LLError::getEnabledLogTypesMask() & 0x01;
+ }
+
virtual void recordMessage(LLError::ELevel level,
- const std::string& message)
+ const std::string& message) override
{
int syslogPriority = LOG_CRIT;
switch (level) {
@@ -119,6 +125,13 @@ namespace {
{
LL_INFOS() << "Error setting log file to " << filename << LL_ENDL;
}
+ else
+ {
+ if (!LLError::getAlwaysFlush())
+ {
+ mFile.sync_with_stdio(false);
+ }
+ }
mWantsTime = true;
mWantsTags = true;
}
@@ -128,12 +141,28 @@ namespace {
mFile.close();
}
+ virtual bool enabled() override
+ {
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+ return 1;
+#else
+ return LLError::getEnabledLogTypesMask() & 0x02;
+#endif
+ }
+
bool okay() { return mFile.good(); }
virtual void recordMessage(LLError::ELevel level,
- const std::string& message)
+ const std::string& message) override
{
- mFile << message << std::endl;
+ if (LLError::getAlwaysFlush())
+ {
+ mFile << message << std::endl;
+ }
+ else
+ {
+ mFile << message << "\n";
+ }
}
private:
@@ -149,8 +178,13 @@ namespace {
mWantsTime = timestamp;
}
+ virtual bool enabled() override
+ {
+ return LLError::getEnabledLogTypesMask() & 0x04;
+ }
+
virtual void recordMessage(LLError::ELevel level,
- const std::string& message)
+ const std::string& message) override
{
if (ANSI_PROBE == mUseANSI)
mUseANSI = (checkANSI() ? ANSI_YES : ANSI_NO);
@@ -209,8 +243,13 @@ namespace {
public:
RecordToFixedBuffer(LLLineBuffer* buffer) : mBuffer(buffer) { }
+ virtual bool enabled() override
+ {
+ return LLError::getEnabledLogTypesMask() & 0x08;
+ }
+
virtual void recordMessage(LLError::ELevel level,
- const std::string& message)
+ const std::string& message) override
{
mBuffer->addLine(message);
}
@@ -226,8 +265,13 @@ namespace {
RecordToWinDebug()
{}
+ virtual bool enabled() override
+ {
+ return LLError::getEnabledLogTypesMask() & 0x10;
+ }
+
virtual void recordMessage(LLError::ELevel level,
- const std::string& message)
+ const std::string& message) override
{
debugger_print(message);
}
@@ -394,7 +438,7 @@ namespace
i != callSites.end();
++i)
{
- (*i)->invalidate();
+ (*i)->invalidate();
}
callSites.clear();
@@ -413,7 +457,11 @@ namespace LLError
bool mPrintLocation;
LLError::ELevel mDefaultLevel;
-
+
+ bool mLogAlwaysFlush;
+
+ U32 mEnabledLogTypesMask;
+
LevelMap mFunctionLevelMap;
LevelMap mClassLevelMap;
LevelMap mFileLevelMap;
@@ -454,6 +502,8 @@ namespace LLError
: LLRefCount(),
mPrintLocation(false),
mDefaultLevel(LLError::LEVEL_DEBUG),
+ mLogAlwaysFlush(true),
+ mEnabledLogTypesMask(255),
mFunctionLevelMap(),
mClassLevelMap(),
mFileLevelMap(),
@@ -618,6 +668,8 @@ namespace
LLError::Settings::getInstance()->reset();
LLError::setDefaultLevel(LLError::LEVEL_INFO);
+ LLError::setAlwaysFlush(true);
+ LLError::setEnabledLogTypesMask(0xFFFFFFFF);
LLError::setFatalFunction(LLError::crashAndLoop);
LLError::setTimeFunction(LLError::utcTime);
@@ -691,6 +743,30 @@ namespace LLError
return s->mDefaultLevel;
}
+ void setAlwaysFlush(bool flush)
+ {
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mLogAlwaysFlush = flush;
+ }
+
+ bool getAlwaysFlush()
+ {
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ return s->mLogAlwaysFlush;
+ }
+
+ void setEnabledLogTypesMask(U32 mask)
+ {
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mEnabledLogTypesMask = mask;
+ }
+
+ U32 getEnabledLogTypesMask()
+ {
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ return s->mEnabledLogTypesMask;
+ }
+
void setFunctionLevel(const std::string& function_name, ELevel level)
{
Globals::getInstance()->invalidateCallSites();
@@ -771,7 +847,15 @@ namespace LLError
setPrintLocation(config["print-location"]);
setDefaultLevel(decodeLevel(config["default-level"]));
-
+ if (config.has("log-always-flush"))
+ {
+ setAlwaysFlush(config["log-always-flush"]);
+ }
+ if (config.has("enabled-log-types-mask"))
+ {
+ setEnabledLogTypesMask(config["enabled-log-types-mask"].asInteger());
+ }
+
LLSD sets = config["settings"];
LLSD::array_const_iterator a, end;
for (a = sets.beginArray(), end = sets.endArray(); a != end; ++a)
@@ -954,7 +1038,12 @@ namespace
++i)
{
LLError::RecorderPtr r = *i;
-
+
+ if (!r->enabled())
+ {
+ continue;
+ }
+
std::ostringstream message_stream;
if (r->wantsTime() && s->mTimeFunction != NULL)
@@ -1088,6 +1177,7 @@ namespace {
namespace LLError
{
+
bool Log::shouldLog(CallSite& site)
{
LogLock lock;
@@ -1553,18 +1643,16 @@ namespace LLError
bool debugLoggingEnabled(const std::string& tag)
{
- const char* tags[] = {tag.c_str()};
- ::size_t tag_count = 1;
- LLError::CallSite _site(LLError::LEVEL_DEBUG, __FILE__, __LINE__,
- typeid(_LL_CLASS_TO_LOG), __FUNCTION__, false, tags, tag_count);
- if (LL_UNLIKELY(_site.shouldLog()))
- {
- return true;
- }
- else
+ LogLock lock;
+ if (!lock.ok())
{
return false;
}
+
+ LLError::SettingsConfigPtr s = LLError::Settings::getInstance()->getSettingsConfig();
+ LLError::ELevel level = LLError::LEVEL_DEBUG;
+ bool res = checkLevelMap(s->mTagLevelMap, tag, level);
+ return res;
}
diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h
index caf2ba72c2..1730f0c640 100644
--- a/indra/llcommon/llerrorcontrol.h
+++ b/indra/llcommon/llerrorcontrol.h
@@ -74,6 +74,10 @@ namespace LLError
LL_COMMON_API void setPrintLocation(bool);
LL_COMMON_API void setDefaultLevel(LLError::ELevel);
LL_COMMON_API ELevel getDefaultLevel();
+ LL_COMMON_API void setAlwaysFlush(bool flush);
+ LL_COMMON_API bool getAlwaysFlush();
+ LL_COMMON_API void setEnabledLogTypesMask(U32 mask);
+ LL_COMMON_API U32 getEnabledLogTypesMask();
LL_COMMON_API void setFunctionLevel(const std::string& function_name, LLError::ELevel);
LL_COMMON_API void setClassLevel(const std::string& class_name, LLError::ELevel);
LL_COMMON_API void setFileLevel(const std::string& file_name, LLError::ELevel);
@@ -140,6 +144,8 @@ namespace LLError
virtual void recordMessage(LLError::ELevel, const std::string& message) = 0;
// use the level for better display, not for filtering
+ virtual bool enabled() { return true; }
+
bool wantsTime();
bool wantsTags();
bool wantsLevel();
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index 5b17d9e3a4..f04ae5f5cb 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -60,6 +60,12 @@ class LLMutex ;
LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment);
#ifdef SHOW_ASSERT
+// This is incredibly expensive - in profiling Windows RWD builds, 30%
+// of CPU time was in aligment checks.
+//#define ASSERT_ALIGNMENT
+#endif
+
+#ifdef ASSERT_ALIGNMENT
#define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(uintptr_t(ptr),((U32)alignment))
#else
#define ll_assert_aligned(ptr,alignment)
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/CMakeLists.txt b/indra/llmath/CMakeLists.txt
index 61d13c0b1c..999bee0a3f 100644
--- a/indra/llmath/CMakeLists.txt
+++ b/indra/llmath/CMakeLists.txt
@@ -20,10 +20,12 @@ set(llmath_SOURCE_FILES
llcoordframe.cpp
llline.cpp
llmatrix3a.cpp
+ llmatrix4a.cpp
llmodularmath.cpp
lloctree.cpp
llperlin.cpp
llquaternion.cpp
+ llrigginginfo.cpp
llrect.cpp
llsphere.cpp
llvector4a.cpp
@@ -70,6 +72,7 @@ set(llmath_HEADER_FILES
llquaternion2.h
llquaternion2.inl
llrect.h
+ llrigginginfo.h
llsimdmath.h
llsimdtypes.h
llsimdtypes.inl
diff --git a/indra/llmath/llmatrix4a.cpp b/indra/llmath/llmatrix4a.cpp
new file mode 100644
index 0000000000..fe8f0b98f3
--- /dev/null
+++ b/indra/llmath/llmatrix4a.cpp
@@ -0,0 +1,80 @@
+/**
+* @file llmatrix4a.cpp
+* @brief Functions for vectorized matrix/vector operations
+*
+* $LicenseInfo:firstyear=2018&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2018, 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 "llmath.h"
+#include "llmatrix4a.h"
+
+// Convert a bounding box into other coordinate system. Should give
+// the same results as transforming every corner of the bounding box
+// and extracting the bounding box of that, although that's not
+// necessarily the fastest way to implement.
+void matMulBoundBox(const LLMatrix4a &mat, const LLVector4a *in_extents, LLVector4a *out_extents)
+{
+ //get 8 corners of bounding box
+ LLVector4Logical mask[6];
+
+ for (U32 i = 0; i < 6; ++i)
+ {
+ mask[i].clear();
+ }
+
+ mask[0].setElement<2>(); //001
+ mask[1].setElement<1>(); //010
+ mask[2].setElement<1>(); //011
+ mask[2].setElement<2>();
+ mask[3].setElement<0>(); //100
+ mask[4].setElement<0>(); //101
+ mask[4].setElement<2>();
+ mask[5].setElement<0>(); //110
+ mask[5].setElement<1>();
+
+ LLVector4a v[8];
+
+ v[6] = in_extents[0];
+ v[7] = in_extents[1];
+
+ for (U32 i = 0; i < 6; ++i)
+ {
+ v[i].setSelectWithMask(mask[i], in_extents[0], in_extents[1]);
+ }
+
+ LLVector4a tv[8];
+
+ //transform bounding box into drawable space
+ for (U32 i = 0; i < 8; ++i)
+ {
+ mat.affineTransform(v[i], tv[i]);
+ }
+
+ //find bounding box
+ out_extents[0] = out_extents[1] = tv[0];
+
+ for (U32 i = 1; i < 8; ++i)
+ {
+ out_extents[0].setMin(out_extents[0], tv[i]);
+ out_extents[1].setMax(out_extents[1], tv[i]);
+ }
+}
diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h
index 216334752a..7ba347062f 100644
--- a/indra/llmath/llmatrix4a.h
+++ b/indra/llmath/llmatrix4a.h
@@ -121,7 +121,7 @@ public:
res.add(z);
}
- inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res)
+ inline void affineTransformSSE(const LLVector4a& v, LLVector4a& res) const
{
LLVector4a x,y,z;
@@ -138,7 +138,7 @@ public:
res.setAdd(x,z);
}
- inline void affineTransformNonSSE(const LLVector4a& v, LLVector4a& res)
+ inline void affineTransformNonSSE(const LLVector4a& v, LLVector4a& res) const
{
F32 x = v[0] * mMatrix[0][0] + v[1] * mMatrix[1][0] + v[2] * mMatrix[2][0] + mMatrix[3][0];
F32 y = v[0] * mMatrix[0][1] + v[1] * mMatrix[1][1] + v[2] * mMatrix[2][1] + mMatrix[3][1];
@@ -147,7 +147,7 @@ public:
res.set(x,y,z,w);
}
- inline void affineTransform(const LLVector4a& v, LLVector4a& res)
+ inline void affineTransform(const LLVector4a& v, LLVector4a& res) const
{
affineTransformSSE(v,res);
}
@@ -176,4 +176,12 @@ 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;
+}
+
+void matMulBoundBox(const LLMatrix4a &a, const LLVector4a *in_extents, LLVector4a *out_extents);
+
#endif
diff --git a/indra/llmath/llrigginginfo.cpp b/indra/llmath/llrigginginfo.cpp
new file mode 100644
index 0000000000..0de07950c1
--- /dev/null
+++ b/indra/llmath/llrigginginfo.cpp
@@ -0,0 +1,159 @@
+/**
+* @file llrigginginfo.cpp
+* @brief Functions for tracking rigged box extents
+*
+* $LicenseInfo:firstyear=2018&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2018, 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 "llmath.h"
+#include "llrigginginfo.h"
+
+//-----------------------------------------------------------------------------
+// LLJointRiggingInfo
+//-----------------------------------------------------------------------------
+LLJointRiggingInfo::LLJointRiggingInfo()
+{
+ mRiggedExtents[0].clear();
+ mRiggedExtents[1].clear();
+ mIsRiggedTo = false;
+}
+
+bool LLJointRiggingInfo::isRiggedTo() const
+{
+ return mIsRiggedTo;
+}
+
+void LLJointRiggingInfo::setIsRiggedTo(bool val)
+{
+ mIsRiggedTo = val;
+}
+
+LLVector4a *LLJointRiggingInfo::getRiggedExtents()
+{
+ return mRiggedExtents;
+}
+
+const LLVector4a *LLJointRiggingInfo::getRiggedExtents() const
+{
+ return mRiggedExtents;
+}
+
+// Combine two rigging info states.
+// - isRiggedTo if either of the source infos are rigged to
+// - box is union of the two sources
+void LLJointRiggingInfo::merge(const LLJointRiggingInfo& other)
+{
+ if (other.mIsRiggedTo)
+ {
+ if (mIsRiggedTo)
+ {
+ // Combine existing boxes
+ update_min_max(mRiggedExtents[0], mRiggedExtents[1], other.mRiggedExtents[0]);
+ update_min_max(mRiggedExtents[0], mRiggedExtents[1], other.mRiggedExtents[1]);
+ }
+ else
+ {
+ // Initialize box
+ mIsRiggedTo = true;
+ mRiggedExtents[0] = other.mRiggedExtents[0];
+ mRiggedExtents[1] = other.mRiggedExtents[1];
+ }
+ }
+}
+
+LLJointRiggingInfoTab::LLJointRiggingInfoTab():
+ mRigInfoPtr(NULL),
+ mSize(0),
+ mNeedsUpdate(true)
+{
+}
+
+LLJointRiggingInfoTab::~LLJointRiggingInfoTab()
+{
+ clear();
+}
+
+// This doesn't preserve data if the size changes. In practice
+// this doesn't matter because the size is always either
+// LL_CHARACTER_MAX_ANIMATED_JOINTS or 0.
+void LLJointRiggingInfoTab::resize(S32 size)
+{
+ if (size != mSize)
+ {
+ clear();
+ if (size > 0)
+ {
+ mRigInfoPtr = new LLJointRiggingInfo[size];
+ mSize = size;
+ }
+ }
+}
+
+void LLJointRiggingInfoTab::clear()
+{
+ if (mRigInfoPtr)
+ {
+ delete[](mRigInfoPtr);
+ mRigInfoPtr = NULL;
+ mSize = 0;
+ }
+}
+
+void showDetails(const LLJointRiggingInfoTab& src, const std::string& str)
+{
+ S32 count_rigged = 0;
+ S32 count_box = 0;
+ LLVector4a zero_vec;
+ zero_vec.clear();
+ for (S32 i=0; i<src.size(); i++)
+ {
+ if (src[i].isRiggedTo())
+ {
+ count_rigged++;
+ if ((!src[i].getRiggedExtents()[0].equals3(zero_vec)) ||
+ (!src[i].getRiggedExtents()[1].equals3(zero_vec)))
+ {
+ count_box++;
+ }
+ }
+ }
+ LL_DEBUGS("RigSpammish") << "details: " << str << " has " << count_rigged << " rigged joints, of which " << count_box << " are non-empty" << LL_ENDL;
+}
+
+void LLJointRiggingInfoTab::merge(const LLJointRiggingInfoTab& src)
+{
+ //showDetails(*this, "input this");
+ // Size should be either LL_CHARACTER_MAX_ANIMATED_JOINTS, or 0 if
+ // no data. Not necessarily the same for both inputs.
+ if (src.size() > size())
+ {
+ resize(src.size());
+ }
+ S32 min_size = llmin(size(), src.size());
+ for (S32 i=0; i<min_size; i++)
+ {
+ (*this)[i].merge(src[i]);
+ }
+ //showDetails(src, "input src");
+ //showDetails(*this, "output this");
+
+}
diff --git a/indra/llmath/llrigginginfo.h b/indra/llmath/llrigginginfo.h
new file mode 100644
index 0000000000..b3d6bc2d19
--- /dev/null
+++ b/indra/llmath/llrigginginfo.h
@@ -0,0 +1,100 @@
+/**
+* @file llrigginginfo.h
+* @brief Functions for tracking rigged box extents
+*
+* $LicenseInfo:firstyear=2018&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2018, 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$
+*/
+
+// Stores information related to associated rigged mesh vertices
+// This lives in llmath because llvolume lives in llmath.
+
+#ifndef LL_LLRIGGINGINFO_H
+#define LL_LLRIGGINGINFO_H
+
+#include "llvector4a.h"
+
+// Extents are in joint space
+// isRiggedTo is based on the state of all currently associated rigged meshes
+LL_ALIGN_PREFIX(16)
+class LLJointRiggingInfo
+{
+public:
+ LLJointRiggingInfo();
+ bool isRiggedTo() const;
+ void setIsRiggedTo(bool val);
+ LLVector4a *getRiggedExtents();
+ const LLVector4a *getRiggedExtents() const;
+ void merge(const LLJointRiggingInfo& other);
+
+ void* operator new(size_t size)
+ {
+ return ll_aligned_malloc_16(size);
+ }
+
+ void operator delete(void* ptr)
+ {
+ ll_aligned_free_16(ptr);
+ }
+
+ void* operator new[](size_t size)
+ {
+ return ll_aligned_malloc_16(size);
+ }
+
+ void operator delete[](void* ptr)
+ {
+ ll_aligned_free_16(ptr);
+ }
+
+
+private:
+ LL_ALIGN_16(LLVector4a mRiggedExtents[2]);
+ bool mIsRiggedTo;
+} LL_ALIGN_POSTFIX(16);
+
+// For storing all the rigging info associated with a given avatar or
+// object, keyed by joint_num.
+// Using direct memory management instead of std::vector<> to avoid alignment issues.
+class LLJointRiggingInfoTab
+{
+public:
+ LLJointRiggingInfoTab();
+ ~LLJointRiggingInfoTab();
+ void resize(S32 size);
+ void clear();
+ S32 size() const { return mSize; }
+ void merge(const LLJointRiggingInfoTab& src);
+ LLJointRiggingInfo& operator[](S32 i) { return mRigInfoPtr[i]; }
+ const LLJointRiggingInfo& operator[](S32 i) const { return mRigInfoPtr[i]; };
+ bool needsUpdate() { return mNeedsUpdate; }
+ void setNeedsUpdate(bool val) { mNeedsUpdate = val; }
+private:
+ // Not implemented
+ LLJointRiggingInfoTab& operator=(const LLJointRiggingInfoTab& src);
+ LLJointRiggingInfoTab(const LLJointRiggingInfoTab& src);
+
+ LLJointRiggingInfo *mRigInfoPtr;
+ S32 mSize;
+ bool mNeedsUpdate;
+};
+
+#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 b1d1b9a3e8..0e1748b337 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -1,5 +1,4 @@
/**
-
* @file llvolume.cpp
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
@@ -2645,6 +2644,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
}
//calculate bounding box
+ // VFExtents change
LLVector4a& min = face.mExtents[0];
LLVector4a& max = face.mExtents[1];
@@ -4774,6 +4774,7 @@ LLVolumeFace::~LLVolumeFace()
{
ll_aligned_free_16(mExtents);
mExtents = NULL;
+ mCenter = NULL;
freeData();
}
@@ -4955,7 +4956,7 @@ void LLVolumeFace::optimize(F32 angle_cutoff)
//
if (new_face.mNumVertices <= mNumVertices)
{
- llassert(new_face.mNumIndices == mNumIndices);
+ llassert(new_face.mNumIndices == mNumIndices);
swapData(new_face);
}
@@ -5576,7 +5577,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];
@@ -5853,7 +5854,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];
@@ -6292,6 +6293,9 @@ void LLVolumeFace::resizeVertices(S32 num_verts)
mNumVertices = num_verts;
mNumAllocatedVertices = num_verts;
+
+ // Force update
+ mJointRiggingInfoTab.clear();
}
void LLVolumeFace::pushVertex(const LLVolumeFace::VertexData& cv)
@@ -6413,96 +6417,6 @@ void LLVolumeFace::fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v,
}
}
-void LLVolumeFace::appendFace(const LLVolumeFace& face, LLMatrix4& mat_in, LLMatrix4& norm_mat_in)
-{
- U16 offset = mNumVertices;
-
- S32 new_count = face.mNumVertices + mNumVertices;
-
- if (new_count > 65536)
- {
- LL_ERRS() << "Cannot append face -- 16-bit overflow will occur." << LL_ENDL;
- }
-
- if (face.mNumVertices == 0)
- {
- LL_ERRS() << "Cannot append empty face." << LL_ENDL;
- }
-
- U32 old_vsize = mNumVertices*16;
- U32 new_vsize = new_count * 16;
- U32 old_tcsize = (mNumVertices*sizeof(LLVector2)+0xF) & ~0xF;
- U32 new_tcsize = (new_count*sizeof(LLVector2)+0xF) & ~0xF;
- U32 new_size = new_vsize * 2 + new_tcsize;
-
- //allocate new buffer space
- LLVector4a* old_buf = mPositions;
- mPositions = (LLVector4a*) ll_aligned_malloc<64>(new_size);
- mNormals = mPositions + new_count;
- mTexCoords = (LLVector2*) (mNormals+new_count);
-
- mNumAllocatedVertices = new_count;
-
- LLVector4a::memcpyNonAliased16((F32*) mPositions, (F32*) old_buf, old_vsize);
- LLVector4a::memcpyNonAliased16((F32*) mNormals, (F32*) (old_buf+mNumVertices), old_vsize);
- LLVector4a::memcpyNonAliased16((F32*) mTexCoords, (F32*) (old_buf+mNumVertices*2), old_tcsize);
-
- mNumVertices = new_count;
-
- //get destination address of appended face
- LLVector4a* dst_pos = mPositions+offset;
- LLVector2* dst_tc = mTexCoords+offset;
- LLVector4a* dst_norm = mNormals+offset;
-
- //get source addresses of appended face
- const LLVector4a* src_pos = face.mPositions;
- const LLVector2* src_tc = face.mTexCoords;
- const LLVector4a* src_norm = face.mNormals;
-
- //load aligned matrices
- LLMatrix4a mat, norm_mat;
- mat.loadu(mat_in);
- norm_mat.loadu(norm_mat_in);
-
- for (U32 i = 0; i < face.mNumVertices; ++i)
- {
- //transform appended face position and store
- mat.affineTransform(src_pos[i], dst_pos[i]);
-
- //transform appended face normal and store
- norm_mat.rotate(src_norm[i], dst_norm[i]);
- dst_norm[i].normalize3fast();
-
- //copy appended face texture coordinate
- dst_tc[i] = src_tc[i];
-
- if (offset == 0 && i == 0)
- { //initialize bounding box
- mExtents[0] = mExtents[1] = dst_pos[i];
- }
- else
- {
- //stretch bounding box
- update_min_max(mExtents[0], mExtents[1], dst_pos[i]);
- }
- }
-
-
- new_count = mNumIndices + face.mNumIndices;
-
- //allocate new index buffer
- mIndices = (U16*) ll_aligned_realloc_16(mIndices, (new_count*sizeof(U16)+0xF) & ~0xF, (mNumIndices*sizeof(U16)+0xF) & ~0xF);
-
- //get destination address into new index buffer
- U16* dst_idx = mIndices+mNumIndices;
- mNumIndices = new_count;
-
- for (U32 i = 0; i < face.mNumIndices; ++i)
- { //copy indices, offsetting by old vertex count
- dst_idx[i] = face.mIndices[i]+offset;
- }
-}
-
BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
{
LL_CHECK_MEMORY
@@ -6648,7 +6562,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/llmath/llvolume.h b/indra/llmath/llvolume.h
index d3c1ac46fe..1d6d35c432 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -57,6 +57,7 @@ class LLVolumeTriangle;
#include "llpointer.h"
#include "llfile.h"
#include "llalignedarray.h"
+#include "llrigginginfo.h"
//============================================================================
@@ -871,8 +872,6 @@ public:
BOOL create(LLVolume* volume, BOOL partial_build = FALSE);
void createTangents();
- void appendFace(const LLVolumeFace& face, LLMatrix4& transform, LLMatrix4& normal_tranform);
-
void resizeVertices(S32 num_verts);
void allocateTangents(S32 num_verts);
void allocateWeights(S32 num_verts);
@@ -958,6 +957,10 @@ public:
LLVector4a* mWeights;
mutable BOOL mWeightsScrubbed;
+
+ // Which joints are rigged to, and the bounding box of any rigged
+ // vertices per joint.
+ LLJointRiggingInfoTab mJointRiggingInfoTab;
LLOctreeNode<LLVolumeTriangle>* mOctree;
diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp
index e7107dee16..b04c67d926 100644
--- a/indra/llmath/v3math.cpp
+++ b/indra/llmath/v3math.cpp
@@ -369,3 +369,39 @@ BOOL LLVector3::parseVector3(const std::string& buf, LLVector3* value)
return FALSE;
}
+
+// Displacement from query point to nearest neighbor point on bounding box.
+// Returns zero vector for points within or on the box.
+LLVector3 point_to_box_offset(LLVector3& pos, const LLVector3* box)
+{
+ LLVector3 offset;
+ for (S32 k=0; k<3; k++)
+ {
+ offset[k] = 0;
+ if (pos[k] < box[0][k])
+ {
+ offset[k] = pos[k] - box[0][k];
+ }
+ else if (pos[k] > box[1][k])
+ {
+ offset[k] = pos[k] - box[1][k];
+ }
+ }
+ return offset;
+}
+
+bool box_valid_and_non_zero(const LLVector3* box)
+{
+ if (!box[0].isFinite() || !box[1].isFinite())
+ {
+ return false;
+ }
+ LLVector3 zero_vec;
+ zero_vec.clear();
+ if ((box[0] != zero_vec) || (box[1] != zero_vec))
+ {
+ return true;
+ }
+ return false;
+}
+
diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h
index f3fbce4843..6f857d7061 100644
--- a/indra/llmath/v3math.h
+++ b/indra/llmath/v3math.h
@@ -163,6 +163,8 @@ LLVector3 inverse_projected_vec(const LLVector3 &a, const LLVector3 &b); // Retu
LLVector3 parallel_component(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b (same as projected_vec)
LLVector3 orthogonal_component(const LLVector3 &a, const LLVector3 &b); // Returns component of vector a not parallel to vector b (same as projected_vec)
LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u); // Returns a vector that is a linear interpolation between a and b
+LLVector3 point_to_box_offset(LLVector3& pos, const LLVector3* box); // Displacement from query point to nearest point on bounding box.
+bool box_valid_and_non_zero(const LLVector3* box);
inline LLVector3::LLVector3(void)
{
diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp
index 6e5923862b..ed2d580d48 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 30763d426e..d7eb04df57 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..37548e3fe3 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);
@@ -400,40 +401,6 @@ void LLModel::setVolumeFaceData(
LLVector4a::memcpyNonAliased16((F32*) face.mIndices, (F32*) ind.get(), size);
}
-void LLModel::appendFaces(LLModel *model, LLMatrix4 &transform, LLMatrix4& norm_mat)
-{
- if (mVolumeFaces.empty())
- {
- setNumVolumeFaces(1);
- }
-
- LLVolumeFace& face = mVolumeFaces[mVolumeFaces.size()-1];
-
-
- for (S32 i = 0; i < model->getNumFaces(); ++i)
- {
- face.appendFace(model->getVolumeFace(i), transform, norm_mat);
- }
-
-}
-
-void LLModel::appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat)
-{
- S32 rindex = getNumVolumeFaces()-1;
- if (rindex == -1 ||
- mVolumeFaces[rindex].mNumVertices + src_face.mNumVertices >= 65536)
- { //empty or overflow will occur, append new face
- LLVolumeFace cur_face;
- cur_face.appendFace(src_face, mat, norm_mat);
- addFace(cur_face);
- mMaterialList.push_back(src_material);
- }
- else
- { //append to existing end face
- mVolumeFaces.rbegin()->appendFace(src_face, mat, norm_mat);
- }
-}
-
void LLModel::addFace(const LLVolumeFace& face)
{
if (face.mNumVertices == 0)
@@ -1391,14 +1358,16 @@ bool LLModel::loadDecomposition(LLSD& header, std::istream& is)
LLMeshSkinInfo::LLMeshSkinInfo():
mPelvisOffset(0.0),
mLockScaleIfJointPosition(false),
- mInvalidJointsScrubbed(false)
+ mInvalidJointsScrubbed(false),
+ mJointNumsInitialized(false)
{
}
LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin):
mPelvisOffset(0.0),
mLockScaleIfJointPosition(false),
- mInvalidJointsScrubbed(false)
+ mInvalidJointsScrubbed(false),
+ mJointNumsInitialized(false)
{
fromLLSD(skin);
}
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index 097558ef67..cf3288489a 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -38,7 +38,6 @@ class domMesh;
#define MAX_MODEL_FACES 8
-
class LLMeshSkinInfo
{
public:
@@ -57,6 +56,7 @@ public:
float mPelvisOffset;
bool mLockScaleIfJointPosition;
bool mInvalidJointsScrubbed;
+ bool mJointNumsInitialized;
};
class LLModel : public LLVolume
@@ -158,9 +158,6 @@ public:
EModelStatus getStatus() const {return mStatus;}
static std::string getStatusString(U32 status) ;
- void appendFaces(LLModel* model, LLMatrix4& transform, LLMatrix4& normal_transform);
- void appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat);
-
void setNumVolumeFaces(S32 count);
void setVolumeFaceData(
S32 f,
diff --git a/indra/llprimitive/llmodelloader.h b/indra/llprimitive/llmodelloader.h
index d64e0a0773..643c45a6d8 100644
--- a/indra/llprimitive/llmodelloader.h
+++ b/indra/llprimitive/llmodelloader.h
@@ -81,6 +81,7 @@ public:
GENERATING_VERTEX_BUFFERS,
GENERATING_LOD,
DONE,
+ WARNING_BIND_SHAPE_ORIENTATION,
ERROR_PARSING, //basically loading failed
ERROR_MATERIALS,
ERROR_PASSWORD_REQUIRED,
diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp
index edf7c41e40..c69c059880 100644
--- a/indra/llprimitive/llprimitive.cpp
+++ b/indra/llprimitive/llprimitive.cpp
@@ -1609,6 +1609,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;
@@ -2036,3 +2038,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 99a32e614c..c138c2ac2b 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 7727dc4f95..967ba2d8d9 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
@@ -608,6 +609,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
@@ -1221,6 +1224,7 @@ set(viewer_HEADER_FILES
lltranslate.h
lltwitterconnect.h
lluiconstants.h
+ lluiavatar.h
lluilistener.h
lluploaddialog.h
lluploadfloaterobservers.h
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 48ea19be07..09b254e90c 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-5.1.10
+6.0.0
diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml
index 8ced81fdb3..482012cdd6 100644
--- a/indra/newview/app_settings/logcontrol.xml
+++ b/indra/newview/app_settings/logcontrol.xml
@@ -2,6 +2,22 @@
<map>
<!-- default-level can be ALL, DEBUG, INFO, WARN, ERROR, or NONE -->
<key>default-level</key> <string>INFO</string>
+ <key>print-location</key> <boolean>false</boolean>
+ <key>log-always-flush</key> <boolean>true</boolean>
+ <!-- All log types are enabled by default. Can be toggled individually;
+ bitwise-or all the ones you want to enable.
+ Log types and their masks are:
+
+ 1 - RecordToSyslog (not used by viewer)
+ 2 - RecordToFile (SecondLife.log)
+ 4 - RecordToStderr (this will appear in the console window, if there is one)
+ 8 - RecordToFixedBuffer (viewer debug console)
+ 16 - RecordToWinDebug (on windows, output to VS IDE window)
+
+ For example, value of 10 = 2|8 would enable logging only to SecondLife.log and the viewer debug console.
+ Note: RecordToFile is always enabled in release builds.
+ -->
+ <key>enabled-log-types-mask</key> <integer>255</integer>
<key>settings</key>
<array>
<!-- Suppress anything but ERROR for some very verbose components -->
@@ -50,6 +66,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 83afa0ed8c..d7b90e0691 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -2193,6 +2193,94 @@
<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>DebugObjectLODs</key>
+ <map>
+ <key>Comment</key>
+ <string>Show info related to lod calculations for attached or 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>AnimatedObjectsGlobalScale</key>
+ <map>
+ <key>Comment</key>
+ <string>Temporary testing: allow an extra scale factor to be forced on animated objects.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.00</real>
+ </map>
+ <key>AnimatedObjectsMaxLegalOffset</key>
+ <map>
+ <key>Comment</key>
+ <string>Max visual offset between object position and rendered position</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>3.0</real>
+ </map>
+ <key>AnimatedObjectsMaxLegalSize</key>
+ <map>
+ <key>Comment</key>
+ <string>Max bounding box size for animated object's rendered position</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>64.0</real>
+ </map>
+ <key>AvatarBoundingBoxComplexity</key>
+ <map>
+ <key>Comment</key>
+ <string>How many aspects to consider for avatar bounding box</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>3</integer>
+ </map>
<key>DebugAvatarAppearanceMessage</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 5135d5072a..e23827cf12 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -881,6 +881,16 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
{
gSky.mVOGroundp->setRegion(regionp);
}
+
+ if (regionp->capabilitiesReceived())
+ {
+ regionp->requestSimulatorFeatures();
+ }
+ else
+ {
+ regionp->setCapabilitiesReceivedCallback(boost::bind(&LLViewerRegion::requestSimulatorFeatures, regionp));
+ }
+
}
else
{
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index e4a5c58c7b..2bdffec7fb 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 cb1a2e28fd..cac951a3c5 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1087,7 +1087,7 @@ bool LLAppViewer::init()
// don't nag developers who need to run the executable directly
#if LL_RELEASE_FOR_DOWNLOAD
// MAINT-8305: If we're processing a SLURL, skip the launcher check.
- if (gSavedSettings.getString("CmdLineLoginLocation").empty())
+ if (gSavedSettings.getString("CmdLineLoginLocation").empty() && !beingDebugged())
{
const char* PARENT = getenv("PARENT");
if (! (PARENT && std::string(PARENT) == "SL_Launcher"))
@@ -3921,12 +3921,6 @@ void LLAppViewer::requestQuit()
gAgentAvatarp->updateAvatarRezMetrics(true); // force a last packet to be sent.
}
- // Try to send last batch of avatar rez metrics.
- if (!gDisconnected && isAgentAvatarValid())
- {
- gAgentAvatarp->updateAvatarRezMetrics(true); // force a last packet to be sent.
- }
-
LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
effectp->setPositionGlobal(gAgent.getPositionGlobal());
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index f0a4a54fbf..3942613ee0 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/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp
index 7c7f55f68c..ca83afb5ab 100644
--- a/indra/newview/llavatarrenderinfoaccountant.cpp
+++ b/indra/newview/llavatarrenderinfoaccountant.cpp
@@ -112,8 +112,13 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro(std::string url, U64
)
{
LLUUID target_agent_id = LLUUID(agent_iter->first);
- LLViewerObject* avatarp = gObjectList.findObject(target_agent_id);
- if (avatarp && avatarp->isAvatar())
+ LLViewerObject* vobjp = gObjectList.findObject(target_agent_id);
+ LLVOAvatar *avatarp = NULL;
+ if (vobjp)
+ {
+ avatarp = vobjp->asAvatar();
+ }
+ if (avatarp && !avatarp->isControlAvatar())
{
const LLSD & agent_info_map = agent_iter->second;
if (agent_info_map.isMap())
@@ -123,7 +128,7 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro(std::string url, U64
if (agent_info_map.has(KEY_WEIGHT))
{
- ((LLVOAvatar *) avatarp)->setReportedVisualComplexity(agent_info_map[KEY_WEIGHT].asInteger());
+ avatarp->setReportedVisualComplexity(agent_info_map[KEY_WEIGHT].asInteger());
}
}
else
@@ -201,6 +206,7 @@ void LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro(std::string url, U
if (avatar &&
avatar->getRezzedStatus() >= 2 && // Mostly rezzed (maybe without baked textures downloaded)
!avatar->isDead() && // Not dead yet
+ !avatar->isControlAvatar() && // Not part of an animated object
avatar->getObjectHost() == regionp->getHost()) // Ensure it's on the same region
{
avatar->calculateUpdateRenderComplexity(); // Make sure the numbers are up-to-date
diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp
new file mode 100644
index 0000000000..d3fd5813a0
--- /dev/null
+++ b/indra/newview/llcontrolavatar.cpp
@@ -0,0 +1,642 @@
+/**
+ * @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"
+#include "llskinningutil.h"
+
+//#pragma optimize("", off)
+
+const F32 LLControlAvatar::MAX_LEGAL_OFFSET = 3.0f;
+const F32 LLControlAvatar::MAX_LEGAL_SIZE = 64.0f;
+
+//static
+boost::signals2::connection LLControlAvatar::sRegionChangedSlot;
+
+LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) :
+ LLVOAvatar(id, pcode, regionp),
+ mPlaying(false),
+ mGlobalScale(1.0f),
+ mMarkedForDeath(false),
+ mRootVolp(NULL),
+ mScaleConstraintFixup(1.0),
+ mRegionChanged(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();
+
+ mInitFlags |= 1<<4;
+}
+
+void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_scale_fixup) const
+{
+
+ F32 max_legal_offset = MAX_LEGAL_OFFSET;
+ if (gSavedSettings.getControl("AnimatedObjectsMaxLegalOffset"))
+ {
+ max_legal_offset = gSavedSettings.getF32("AnimatedObjectsMaxLegalOffset");
+ }
+ max_legal_offset = llmax(max_legal_offset,0.f);
+
+ F32 max_legal_size = MAX_LEGAL_SIZE;
+ if (gSavedSettings.getControl("AnimatedObjectsMaxLegalSize"))
+ {
+ max_legal_size = gSavedSettings.getF32("AnimatedObjectsMaxLegalSize");
+ }
+ max_legal_size = llmax(max_legal_size, 1.f);
+
+ new_pos_fixup = LLVector3();
+ new_scale_fixup = 1.0f;
+ LLVector3 vol_pos = mRootVolp->getRenderPosition();
+
+ // Fix up position if needed to prevent visual encroachment
+ if (box_valid_and_non_zero(getLastAnimExtents())) // wait for state to settle down
+ {
+ // The goal here is to ensure that the extent of the avatar's
+ // bounding box does not wander too far from the
+ // official position of the corresponding volume. We
+ // do this by tracking the distance and applying a
+ // correction to the control avatar position if
+ // needed.
+ const LLVector3 *extents = getLastAnimExtents();
+ LLVector3 unshift_extents[2];
+ unshift_extents[0] = extents[0] - mPositionConstraintFixup;
+ unshift_extents[1] = extents[1] - mPositionConstraintFixup;
+ LLVector3 box_dims = extents[1]-extents[0];
+ F32 box_size = llmax(box_dims[0],box_dims[1],box_dims[2]);
+
+ if (!mRootVolp->isAttachment())
+ {
+ LLVector3 pos_box_offset = point_to_box_offset(vol_pos, unshift_extents);
+ F32 offset_dist = pos_box_offset.length();
+ if (offset_dist > max_legal_offset && offset_dist > 0.f)
+ {
+ F32 target_dist = (offset_dist - max_legal_offset);
+ new_pos_fixup = (target_dist/offset_dist)*pos_box_offset;
+ }
+ if (new_pos_fixup != mPositionConstraintFixup)
+ {
+ LL_DEBUGS("ConstraintFix") << getFullname() << " pos fix, offset_dist " << offset_dist << " pos fixup "
+ << new_pos_fixup << " was " << mPositionConstraintFixup << LL_ENDL;
+ LL_DEBUGS("ConstraintFix") << "vol_pos " << vol_pos << LL_ENDL;
+ LL_DEBUGS("ConstraintFix") << "extents " << extents[0] << " " << extents[1] << LL_ENDL;
+ LL_DEBUGS("ConstraintFix") << "unshift_extents " << unshift_extents[0] << " " << unshift_extents[1] << LL_ENDL;
+
+ }
+ }
+ if (box_size/mScaleConstraintFixup > max_legal_size)
+ {
+ new_scale_fixup = mScaleConstraintFixup*max_legal_size/box_size;
+ LL_DEBUGS("ConstraintFix") << getFullname() << " scale fix, box_size " << box_size << " fixup "
+ << mScaleConstraintFixup << " max legal " << max_legal_size
+ << " -> new scale " << new_scale_fixup << LL_ENDL;
+ }
+ }
+}
+
+void LLControlAvatar::matchVolumeTransform()
+{
+ if (mRootVolp)
+ {
+ LLVector3 new_pos_fixup;
+ F32 new_scale_fixup;
+ if (mRegionChanged)
+ {
+ new_scale_fixup = mScaleConstraintFixup;
+ new_pos_fixup = mPositionConstraintFixup;
+ mRegionChanged = false;
+ }
+ else
+ {
+ getNewConstraintFixups(new_pos_fixup, new_scale_fixup);
+ }
+ mPositionConstraintFixup = new_pos_fixup;
+ mScaleConstraintFixup = new_scale_fixup;
+
+ 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());
+
+ F32 global_scale = gSavedSettings.getF32("AnimatedObjectsGlobalScale");
+ setGlobalScale(global_scale * mScaleConstraintFixup);
+ }
+ else
+ {
+ LL_WARNS_ONCE() << "can't find attached av!" << LL_ENDL;
+ }
+ }
+ else
+ {
+ LLVector3 vol_pos = mRootVolp->getRenderPosition();
+
+ // FIXME: Currently if you're doing something like playing an
+ // animation that moves the pelvis (on an avatar or
+ // animated object), the name tag and debug text will be
+ // left behind. Ideally setPosition() would follow the
+ // skeleton around in a smarter way, so name tags,
+ // complexity info and such line up better. Should defer
+ // this until avatars also get fixed.
+
+ LLQuaternion obj_rot;
+ if (mRootVolp->mDrawable)
+ {
+ obj_rot = mRootVolp->mDrawable->getRotation();
+ }
+ else
+ {
+ obj_rot = mRootVolp->getRotation();
+ }
+
+ LLMatrix3 bind_mat;
+
+ LLQuaternion bind_rot;
+#define MATCH_BIND_SHAPE
+#ifdef MATCH_BIND_SHAPE
+ // MAINT-8671 - based on a patch from Beq Janus
+ const LLMeshSkinInfo* skin_info = mRootVolp->getSkinInfo();
+ if (skin_info)
+ {
+ LL_DEBUGS("BindShape") << getFullname() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL;
+ bind_rot = LLSkinningUtil::getUnscaledQuaternion(skin_info->mBindShapeMatrix);
+ }
+#endif
+ setRotation(bind_rot*obj_rot);
+ mRoot->setWorldRotation(bind_rot*obj_rot);
+ setPositionAgent(vol_pos);
+ mRoot->setPosition(vol_pos + mPositionConstraintFixup);
+
+ F32 global_scale = gSavedSettings.getF32("AnimatedObjectsGlobalScale");
+ setGlobalScale(global_scale * mScaleConstraintFixup);
+ }
+ }
+}
+
+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;
+ std::string animated_object_flag_string;
+ S32 total_tris = 0;
+ S32 total_verts = 0;
+ F32 est_tris = 0.f;
+ F32 est_streaming_tris = 0.f;
+ F32 streaming_cost = 0.f;
+ std::string cam_dist_string = "";
+ S32 cam_dist_count = 0;
+ F32 lod_radius = mRootVolp->mLODRadius;
+
+ 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();
+ est_streaming_tris += volp->getEstTrianglesStreamingCost();
+ streaming_cost += volp->getStreamingCost();
+ lod_string += llformat("%d",volp->getLOD());
+ if (volp && volp->mDrawable)
+ {
+ bool is_animated_flag = volp->getExtendedMeshFlags() & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG;
+ if (is_animated_flag)
+ {
+ animated_object_flag_string += "1";
+ }
+ else
+ {
+ animated_object_flag_string += "0";
+ }
+ if (volp->mDrawable->isActive())
+ {
+ active_string += "A";
+ }
+ else
+ {
+ active_string += "S";
+ }
+ if (volp->isRiggedMesh())
+ {
+ // Rigged/animatable mesh
+ type_string += "R";
+ lod_radius = volp->mLODRadius;
+ }
+ else if (volp->isMesh())
+ {
+ // Static mesh
+ type_string += "M";
+ }
+ else
+ {
+ // Any other prim
+ type_string += "P";
+ }
+ if (cam_dist_count < 4)
+ {
+ cam_dist_string += LLStringOps::getReadableNumber(volp->mLODDistance) + "/" +
+ LLStringOps::getReadableNumber(volp->mLODAdjustedDistance) + " ";
+ cam_dist_count++;
+ }
+ }
+ else
+ {
+ active_string += "-";
+ type_string += "-";
+ }
+ }
+ addDebugText(llformat("CAV obj %d anim %d active %s impost %d upprd %d strcst %f",
+ total_linkset_count, animated_volume_count,
+ active_string.c_str(), (S32) isImpostor(), getUpdatePeriod(), streaming_cost));
+ addDebugText(llformat("types %s lods %s", type_string.c_str(), lod_string.c_str()));
+ addDebugText(llformat("flags %s", animated_object_flag_string.c_str()));
+ addDebugText(llformat("tris %d (est %.1f, streaming %.1f), verts %d", total_tris, est_tris, est_streaming_tris, total_verts));
+ addDebugText(llformat("pxarea %s rank %d", LLStringOps::getReadableNumber(getPixelArea()).c_str(), getVisibilityRank()));
+ addDebugText(llformat("lod_radius %s dists %s", LLStringOps::getReadableNumber(lod_radius).c_str(),cam_dist_string.c_str()));
+ if (mPositionConstraintFixup.length() > 0.0f || mScaleConstraintFixup != 1.0f)
+ {
+ addDebugText(llformat("pos fix (%.1f %.1f %.1f) scale %f",
+ mPositionConstraintFixup[0],
+ mPositionConstraintFixup[1],
+ mPositionConstraintFixup[2],
+ mScaleConstraintFixup));
+ }
+
+#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("AnimatedObjectsNotify") << "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;
+ //LL_INFOS("AnimatedObjects") << "updating anim for vol " << volp->getID() << " root " << mRootVolp->getID() << LL_ENDL;
+ signaled_animation_map_t& signaled_animations = LLObjectSignaledAnimationMap::instance().getMap()[volp->getID()];
+ for (std::map<LLUUID,S32>::iterator anim_it = signaled_animations.begin();
+ anim_it != signaled_animations.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;
+ }
+ LL_DEBUGS("AnimatedObjectsNotify") << "found anim for vol " << volp->getID() << " anim " << anim_it->first << " root " << mRootVolp->getID() << LL_ENDL;
+ }
+ }
+ if (!mPlaying)
+ {
+ 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";
+ }
+}
+
+// virtual
+bool LLControlAvatar::shouldRenderRigged() const
+{
+ if (mRootVolp && mRootVolp->isAttachment())
+ {
+ LLVOAvatar *attached_av = mRootVolp->getAvatarAncestor();
+ if (attached_av)
+ {
+ return attached_av->shouldRenderRigged();
+ }
+ }
+ return true;
+}
+
+// virtual
+BOOL LLControlAvatar::isImpostor()
+{
+ if (mRootVolp && mRootVolp->isAttachment())
+ {
+ // Attached animated objects should match state of their attached av.
+ LLVOAvatar *attached_av = mRootVolp->getAvatarAncestor();
+ if (attached_av)
+ {
+ return attached_av->isImpostor();
+ }
+ }
+ return LLVOAvatar::isImpostor();
+}
+
+//static
+void LLControlAvatar::onRegionChanged()
+{
+ std::vector<LLCharacter*>::iterator it = LLCharacter::sInstances.begin();
+ for ( ; it != LLCharacter::sInstances.end(); ++it)
+ {
+ LLControlAvatar* cav = dynamic_cast<LLControlAvatar*>(*it);
+ if (!cav) continue;
+ cav->mRegionChanged = true;
+ }
+}
diff --git a/indra/newview/llcontrolavatar.h b/indra/newview/llcontrolavatar.h
new file mode 100644
index 0000000000..288d07cd48
--- /dev/null
+++ b/indra/newview/llcontrolavatar.h
@@ -0,0 +1,113 @@
+/**
+ * @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 getNewConstraintFixups(LLVector3& new_pos_constraint, F32& new_scale_constraint) const;
+ 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;
+
+ virtual bool shouldRenderRigged() const;
+
+ virtual BOOL isImpostor();
+
+ bool mPlaying;
+
+ F32 mGlobalScale;
+
+ LLVOVolume *mRootVolp;
+
+ bool mMarkedForDeath;
+
+ LLVector3 mPositionConstraintFixup;
+ F32 mScaleConstraintFixup;
+
+ static const F32 MAX_LEGAL_OFFSET;
+ static const F32 MAX_LEGAL_SIZE;
+
+ static void onRegionChanged();
+ bool mRegionChanged;
+ static boost::signals2::connection sRegionChangedSlot;
+};
+
+typedef std::map<LLUUID, S32> signaled_animation_map_t;
+typedef std::map<LLUUID, signaled_animation_map_t> object_signaled_animation_map_t;
+
+// Stores information about previously requested animations, by object id.
+class LLObjectSignaledAnimationMap: public LLSingleton<LLObjectSignaledAnimationMap>
+{
+ LLSINGLETON_EMPTY_CTOR(LLObjectSignaledAnimationMap);
+
+public:
+ object_signaled_animation_map_t mMap;
+
+ object_signaled_animation_map_t& getMap() { return mMap; }
+};
+
+#endif //LL_CONTROLAVATAR_H
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
index 6ca8f1ae9c..8c6cbc020b 100644
--- a/indra/newview/lldrawable.cpp
+++ b/indra/newview/lldrawable.cpp
@@ -32,6 +32,7 @@
#include "material_codes.h"
// viewer includes
+#include "llagent.h"
#include "llcriticaldamp.h"
#include "llface.h"
#include "lllightconstants.h"
@@ -50,6 +51,7 @@
#include "llviewerobjectlist.h"
#include "llviewerwindow.h"
#include "llvocache.h"
+#include "llcontrolavatar.h"
#include "lldrawpoolavatar.h"
const F32 MIN_INTERPOLATE_DISTANCE_SQUARED = 0.001f * 0.001f;
@@ -573,7 +575,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);
@@ -726,6 +729,10 @@ F32 LLDrawable::updateXform(BOOL undamped)
mXform.setRotation(target_rot);
mXform.setScale(LLVector3(1,1,1)); //no scale in drawable transforms (IT'S A RULE!)
mXform.updateMatrix();
+ if (isRoot() && mVObjp->isAnimatedObject() && mVObjp->getControlAvatar())
+ {
+ mVObjp->getControlAvatar()->matchVolumeTransform();
+ }
if (mSpatialBridge)
{
@@ -897,6 +904,30 @@ void LLDrawable::updateDistance(LLCamera& camera, bool force_update)
}
}
}
+
+
+ // MAINT-7926 Handle volumes in an animated object as a special case
+ // SL-937: add dynamic box handling for rigged mesh on regular avatars.
+ //if (volume->getAvatar() && volume->getAvatar()->isControlAvatar())
+ if (volume->getAvatar())
+ {
+ const LLVector3* av_box = volume->getAvatar()->getLastAnimExtents();
+ LLVector3d cam_pos = gAgent.getPosGlobalFromAgent(LLViewerCamera::getInstance()->getOrigin());
+ LLVector3 cam_region_pos = LLVector3(cam_pos - volume->getRegion()->getOriginGlobal());
+
+ LLVector3 cam_to_box_offset = point_to_box_offset(cam_region_pos, av_box);
+ mDistanceWRTCamera = llmax(0.01f, ll_round(cam_to_box_offset.magVec(), 0.01f));
+ LL_DEBUGS("DynamicBox") << volume->getAvatar()->getFullname()
+ << " pos (ignored) " << pos
+ << " cam pos " << cam_pos
+ << " cam region pos " << cam_region_pos
+ << " box " << av_box[0] << "," << av_box[1]
+ << " -> dist " << mDistanceWRTCamera
+ << LL_ENDL;
+ mVObjp->updateLOD();
+ return;
+ }
+
}
else
{
@@ -1113,7 +1144,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 075375082d..82888b2df6 100644
--- a/indra/newview/lldrawpool.cpp
+++ b/indra/newview/lldrawpool.cpp
@@ -141,7 +141,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 2651e3531b..d4dc6f3558 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;
}
@@ -1794,7 +1820,7 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(
void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)
{
- if (avatar->isSelf() && !gAgent.needsRenderAvatar())
+ if (!avatar->shouldRenderRigged())
{
return;
}
@@ -1825,13 +1851,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;
@@ -2045,13 +2065,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;
@@ -2172,11 +2186,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;
@@ -2189,6 +2208,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 14b13c1f5f..8df898f8a9 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -336,11 +336,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);
}
@@ -820,17 +816,11 @@ bool less_than_max_mag(const LLVector4a& vec)
}
BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
- const LLMatrix4& mat_vert_in, BOOL global_volume)
+ const LLMatrix4& mat_vert_in, BOOL global_volume)
{
//get bounding box
if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED))
{
- //VECTORIZE THIS
- LLMatrix4a mat_vert;
- mat_vert.loadu(mat_vert_in);
-
- LLVector4a min,max;
-
if (f >= volume.getNumVolumeFaces())
{
LL_WARNS() << "Generating bounding box for invalid face index!" << LL_ENDL;
@@ -838,77 +828,52 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
}
const LLVolumeFace &face = volume.getVolumeFace(f);
- min = face.mExtents[0];
- max = face.mExtents[1];
- llassert(less_than_max_mag(min));
- llassert(less_than_max_mag(max));
-
- //min, max are in volume space, convert to drawable render space
-
- //get 8 corners of bounding box
- LLVector4Logical mask[6];
-
- for (U32 i = 0; i < 6; ++i)
- {
- mask[i].clear();
- }
-
- mask[0].setElement<2>(); //001
- mask[1].setElement<1>(); //010
- mask[2].setElement<1>(); //011
- mask[2].setElement<2>();
- mask[3].setElement<0>(); //100
- mask[4].setElement<0>(); //101
- mask[4].setElement<2>();
- mask[5].setElement<0>(); //110
- mask[5].setElement<1>();
-
- LLVector4a v[8];
-
- v[6] = min;
- v[7] = max;
-
- for (U32 i = 0; i < 6; ++i)
- {
- v[i].setSelectWithMask(mask[i], min, max);
- }
-
- LLVector4a tv[8];
+ LL_DEBUGS("RiggedBox") << "updating extents for face " << f
+ << " starting extents " << mExtents[0] << ", " << mExtents[1]
+ << " starting vf extents " << face.mExtents[0] << ", " << face.mExtents[1]
+ << " num verts " << face.mNumVertices << LL_ENDL;
+
+ // 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;
+ }
+
+ //VECTORIZE THIS
+ LLMatrix4a mat_vert;
+ mat_vert.loadu(mat_vert_in);
+ LLVector4a new_extents[2];
- //transform bounding box into drawable space
- for (U32 i = 0; i < 8; ++i)
- {
- mat_vert.affineTransform(v[i], tv[i]);
- }
-
- //find bounding box
- LLVector4a& newMin = mExtents[0];
- LLVector4a& newMax = mExtents[1];
+ llassert(less_than_max_mag(face.mExtents[0]));
+ llassert(less_than_max_mag(face.mExtents[1]));
- newMin = newMax = tv[0];
+ matMulBoundBox(mat_vert, face.mExtents, mExtents);
- for (U32 i = 1; i < 8; ++i)
- {
- newMin.setMin(newMin, tv[i]);
- newMax.setMax(newMax, tv[i]);
- }
+ LL_DEBUGS("RiggedBox") << "updated extents for face " << f
+ << " bbox gave extents " << mExtents[0] << ", " << mExtents[1] << LL_ENDL;
if (!mDrawablep->isActive())
{ // Shift position for region
LLVector4a offset;
offset.load3(mDrawablep->getRegion()->getOriginAgent().mV);
- newMin.add(offset);
- newMax.add(offset);
+ mExtents[0].add(offset);
+ mExtents[1].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.setAdd(mExtents[0],mExtents[1]);
t.mul(0.5f);
mCenterLocal.set(t.getF32ptr());
- t.setSub(newMax,newMin);
+ t.setSub(mExtents[1],mExtents[0]);
mBoundingSphereRadius = t.getLength3().getF32()*0.5f;
updateCenterAgent();
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 0c2bb25e07..3c428a70f3 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 7a2ab37a0d..268c646719 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -685,6 +685,11 @@ void LLFloaterModelPreview::draw()
childSetTextArg("status", "[STATUS]", getString("status_parse_error"));
toggleCalculateButton(false);
}
+ else
+ if (mModelPreview->getLoadState() == LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION)
+ {
+ childSetTextArg("status", "[STATUS]", getString("status_bind_shape_orientation"));
+ }
else
{
childSetTextArg("status", "[STATUS]", getString("status_idle"));
@@ -1368,7 +1373,11 @@ U32 LLModelPreview::calcResourceCost()
F32 radius = scale.length()*0.5f*debug_scale;
- streaming_cost += LLMeshRepository::getStreamingCost(ret, radius);
+ LLMeshCostData costs;
+ if (gMeshRepo.getCostData(ret, costs))
+ {
+ streaming_cost += costs.getRadiusBasedStreamingCost(radius);
+ }
}
}
@@ -1618,6 +1627,19 @@ void LLModelPreview::rebuildUploadData()
mFMP->childDisable( "calculate_btn" );
}
}
+ LLFloaterModelPreview* fmp = (LLFloaterModelPreview*) mFMP;
+ bool upload_skinweights = fmp && fmp->childGetValue("upload_skin").asBoolean();
+ if (upload_skinweights && high_lod_model->mSkinInfo.mJointNames.size() > 0)
+ {
+ LLQuaternion bind_rot = LLSkinningUtil::getUnscaledQuaternion(high_lod_model->mSkinInfo.mBindShapeMatrix);
+ LLQuaternion identity;
+ if (!bind_rot.isEqualEps(identity,0.01))
+ {
+ LL_WARNS() << "non-identity bind shape rot. mat is " << high_lod_model->mSkinInfo.mBindShapeMatrix
+ << " bind_rot " << bind_rot << LL_ENDL;
+ setLoadState( LLModelLoader::WARNING_BIND_SHAPE_ORIENTATION );
+ }
+ }
}
instance.mTransform = mat;
mUploadData.push_back(instance);
@@ -1763,9 +1785,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;
}
}
@@ -3451,16 +3481,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/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h
index 53b03c6069..7ec6a58ac7 100644
--- a/indra/newview/llfloatermodelpreview.h
+++ b/indra/newview/llfloatermodelpreview.h
@@ -169,7 +169,10 @@ protected:
void draw();
void initDecompControls();
-
+
+ // FIXME - this function and mStatusMessage have no visible effect, and the
+ // actual status messages are managed by directly manipulation of
+ // the UI element.
void setStatusMessage(const std::string& msg);
LLModelPreview* mModelPreview;
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index d0ef9dd326..c934cd2980 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -4214,22 +4214,80 @@ void LLMeshRepository::uploadError(LLSD& args)
mUploadErrorQ.push(args);
}
-F32 LLMeshRepository::getStreamingCost(LLUUID mesh_id, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value)
+F32 LLMeshRepository::getEstTrianglesMax(LLUUID mesh_id)
{
+ LLMeshCostData costs;
+ if (getCostData(mesh_id, costs))
+ {
+ return costs.getEstTrisMax();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLMeshRepository::getEstTrianglesStreamingCost(LLUUID mesh_id)
+{
+ LLMeshCostData costs;
+ if (getCostData(mesh_id, costs))
+ {
+ return costs.getEstTrisForStreamingCost();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+// FIXME replace with calc based on LLMeshCostData
+F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value)
+{
+ F32 result = 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)
{
- return getStreamingCost(iter->second, radius, bytes, bytes_visible, lod, unscaled_value);
+ result = getStreamingCostLegacy(iter->second, radius, bytes, bytes_visible, lod, unscaled_value);
+ }
+ }
+ if (result > 0.f)
+ {
+ LLMeshCostData data;
+ if (getCostData(mesh_id, data))
+ {
+ F32 ref_streaming_cost = data.getRadiusBasedStreamingCost(radius);
+ F32 ref_weighted_tris = data.getRadiusWeightedTris(radius);
+ if (!is_approx_equal(ref_streaming_cost,result))
+ {
+ LL_WARNS() << mesh_id << "streaming mismatch " << result << " " << ref_streaming_cost << LL_ENDL;
+ }
+ if (unscaled_value && !is_approx_equal(ref_weighted_tris,*unscaled_value))
+ {
+ LL_WARNS() << mesh_id << "weighted_tris mismatch " << *unscaled_value << " " << ref_weighted_tris << LL_ENDL;
+ }
+ if (bytes && (*bytes != data.getSizeTotal()))
+ {
+ LL_WARNS() << mesh_id << "bytes mismatch " << *bytes << " " << data.getSizeTotal() << LL_ENDL;
+ }
+ if (bytes_visible && (lod >=0) && (lod < 4) && (*bytes_visible != data.getSizeByLOD(lod)))
+ {
+ LL_WARNS() << mesh_id << "bytes_visible mismatch " << *bytes_visible << " " << data.getSizeByLOD(lod) << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_WARNS() << "getCostData failed!!!" << LL_ENDL;
}
}
- return 0.f;
+ return result;
}
+// FIXME replace with calc based on LLMeshCostData
//static
-F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value)
+F32 LLMeshRepository::getStreamingCostLegacy(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value)
{
if (header.has("404")
|| !header.has("lowest_lod")
@@ -4323,7 +4381,7 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32
F32 weighted_avg = triangles_high*high_area +
triangles_mid*mid_area +
triangles_low*low_area +
- triangles_lowest*lowest_area;
+ triangles_lowest*lowest_area;
if (unscaled_value)
{
@@ -4333,6 +4391,204 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32
return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;
}
+LLMeshCostData::LLMeshCostData()
+{
+ mSizeByLOD.resize(4);
+ mEstTrisByLOD.resize(4);
+
+ std::fill(mSizeByLOD.begin(), mSizeByLOD.end(), 0);
+ std::fill(mEstTrisByLOD.begin(), mEstTrisByLOD.end(), 0.f);
+}
+
+bool LLMeshCostData::init(const LLSD& header)
+{
+ mSizeByLOD.resize(4);
+ mEstTrisByLOD.resize(4);
+
+ std::fill(mSizeByLOD.begin(), mSizeByLOD.end(), 0);
+ std::fill(mEstTrisByLOD.begin(), mEstTrisByLOD.end(), 0.f);
+
+ S32 bytes_high = header["high_lod"]["size"].asInteger();
+ S32 bytes_med = header["medium_lod"]["size"].asInteger();
+ if (bytes_med == 0)
+ {
+ bytes_med = bytes_high;
+ }
+ S32 bytes_low = header["low_lod"]["size"].asInteger();
+ if (bytes_low == 0)
+ {
+ bytes_low = bytes_med;
+ }
+ S32 bytes_lowest = header["lowest_lod"]["size"].asInteger();
+ if (bytes_lowest == 0)
+ {
+ bytes_lowest = bytes_low;
+ }
+ mSizeByLOD[0] = bytes_lowest;
+ mSizeByLOD[1] = bytes_low;
+ mSizeByLOD[2] = bytes_med;
+ mSizeByLOD[3] = bytes_high;
+
+ 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");
+
+ for (S32 i=0; i<4; i++)
+ {
+ mEstTrisByLOD[i] = llmax((F32) mSizeByLOD[i]-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
+ }
+
+ return true;
+}
+
+
+S32 LLMeshCostData::getSizeByLOD(S32 lod)
+{
+ if (llclamp(lod,0,3) != lod)
+ {
+ return 0;
+ }
+ return mSizeByLOD[lod];
+}
+
+S32 LLMeshCostData::getSizeTotal()
+{
+ return mSizeByLOD[0] + mSizeByLOD[1] + mSizeByLOD[2] + mSizeByLOD[3];
+}
+
+F32 LLMeshCostData::getEstTrisByLOD(S32 lod)
+{
+ if (llclamp(lod,0,3) != lod)
+ {
+ return 0.f;
+ }
+ return mEstTrisByLOD[lod];
+}
+
+F32 LLMeshCostData::getEstTrisMax()
+{
+ return llmax(mEstTrisByLOD[0], mEstTrisByLOD[1], mEstTrisByLOD[2], mEstTrisByLOD[3]);
+}
+
+F32 LLMeshCostData::getRadiusWeightedTris(F32 radius)
+{
+ F32 max_distance = 512.f;
+
+ F32 dlowest = llmin(radius/0.03f, max_distance);
+ F32 dlow = llmin(radius/0.06f, max_distance);
+ F32 dmid = llmin(radius/0.24f, max_distance);
+
+ F32 triangles_lowest = mEstTrisByLOD[0];
+ F32 triangles_low = mEstTrisByLOD[1];
+ F32 triangles_mid = mEstTrisByLOD[2];
+ F32 triangles_high = mEstTrisByLOD[3];
+
+ F32 max_area = 102944.f; //area of circle that encompasses region (see MAINT-6559)
+ F32 min_area = 1.f;
+
+ F32 high_area = llmin(F_PI*dmid*dmid, max_area);
+ F32 mid_area = llmin(F_PI*dlow*dlow, max_area);
+ F32 low_area = llmin(F_PI*dlowest*dlowest, max_area);
+ F32 lowest_area = max_area;
+
+ lowest_area -= low_area;
+ low_area -= mid_area;
+ mid_area -= high_area;
+
+ high_area = llclamp(high_area, min_area, max_area);
+ mid_area = llclamp(mid_area, min_area, max_area);
+ low_area = llclamp(low_area, min_area, max_area);
+ lowest_area = llclamp(lowest_area, min_area, max_area);
+
+ F32 total_area = high_area + mid_area + low_area + lowest_area;
+ high_area /= total_area;
+ mid_area /= total_area;
+ low_area /= total_area;
+ lowest_area /= total_area;
+
+ F32 weighted_avg = triangles_high*high_area +
+ triangles_mid*mid_area +
+ triangles_low*low_area +
+ triangles_lowest*lowest_area;
+
+ return weighted_avg;
+}
+
+F32 LLMeshCostData::getEstTrisForStreamingCost()
+{
+ LL_DEBUGS("StreamingCost") << "tris_by_lod: "
+ << mEstTrisByLOD[0] << ", "
+ << mEstTrisByLOD[1] << ", "
+ << mEstTrisByLOD[2] << ", "
+ << mEstTrisByLOD[3] << LL_ENDL;
+
+ F32 charged_tris = mEstTrisByLOD[3];
+ F32 allowed_tris = mEstTrisByLOD[3];
+ const F32 ENFORCE_FLOOR = 64.0f;
+ for (S32 i=2; i>=0; i--)
+ {
+ // How many tris can we have in this LOD without affecting land impact?
+ // - normally an LOD should be at most half the size of the previous one.
+ // - once we reach a floor of ENFORCE_FLOOR, don't require LODs to get any smaller.
+ allowed_tris = llclamp(allowed_tris/2.0f,ENFORCE_FLOOR,mEstTrisByLOD[i]);
+ F32 excess_tris = mEstTrisByLOD[i]-allowed_tris;
+ if (excess_tris>0.f)
+ {
+ LL_DEBUGS("StreamingCost") << "excess tris in lod[" << i << "] " << excess_tris << " allowed " << allowed_tris << LL_ENDL;
+ charged_tris += excess_tris;
+ }
+ }
+ return charged_tris;
+}
+
+F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius)
+{
+ return getRadiusWeightedTris(radius)/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;
+}
+
+F32 LLMeshCostData::getTriangleBasedStreamingCost()
+{
+ F32 result = ANIMATED_OBJECT_COST_PER_KTRI * 0.001 * getEstTrisForStreamingCost();
+ return result;
+}
+
+bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data)
+{
+ data = LLMeshCostData();
+
+ 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;
+
+ bool header_invalid = (header.has("404")
+ || !header.has("lowest_lod")
+ || (header.has("version") && header["version"].asInteger() > MAX_MESH_VERSION));
+ if (!header_invalid)
+ {
+ return getCostData(header, data);
+ }
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLMeshRepository::getCostData(LLSD& header, LLMeshCostData& data)
+{
+ data = LLMeshCostData();
+
+ if (!data.init(header))
+ {
+ return false;
+ }
+
+ return true;
+}
LLPhysicsDecomp::LLPhysicsDecomp()
: LLThread("Physics Decomp")
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 22215c784a..bba0c9f2cb 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -490,6 +490,53 @@ private:
LLCore::HttpRequest::priority_t mHttpPriority;
};
+// Params related to streaming cost, render cost, and scene complexity tracking.
+class LLMeshCostData
+{
+public:
+ LLMeshCostData();
+
+ bool init(const LLSD& header);
+
+ // Size for given LOD
+ S32 getSizeByLOD(S32 lod);
+
+ // Sum of all LOD sizes.
+ S32 getSizeTotal();
+
+ // Estimated triangle counts for the given LOD.
+ F32 getEstTrisByLOD(S32 lod);
+
+ // Estimated triangle counts for the largest LOD. Typically this
+ // is also the "high" LOD, but not necessarily.
+ F32 getEstTrisMax();
+
+ // Triangle count as computed by original streaming cost
+ // formula. Triangles in each LOD are weighted based on how
+ // frequently they will be seen.
+ // This was called "unscaled_value" in the original getStreamingCost() functions.
+ F32 getRadiusWeightedTris(F32 radius);
+
+ // Triangle count used by triangle-based cost formula. Based on
+ // triangles in highest LOD plus potentially partial charges for
+ // lower LODs depending on complexity.
+ F32 getEstTrisForStreamingCost();
+
+ // Streaming cost. This should match the server-side calculation
+ // for the corresponding volume.
+ F32 getRadiusBasedStreamingCost(F32 radius);
+
+ // New streaming cost formula, currently only used for animated objects.
+ F32 getTriangleBasedStreamingCost();
+
+private:
+ // From the "size" field of the mesh header. LOD 0=lowest, 3=highest.
+ std::vector<S32> mSizeByLOD;
+
+ // Estimated triangle counts derived from the LOD sizes. LOD 0=lowest, 3=highest.
+ std::vector<F32> mEstTrisByLOD;
+};
+
class LLMeshRepository
{
public:
@@ -511,8 +558,13 @@ public:
static LLDeadmanTimer sQuiescentTimer; // Time-to-complete-mesh-downloads after significant events
- 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);
+ // Estimated triangle count of the largest LOD
+ F32 getEstTrianglesMax(LLUUID mesh_id);
+ F32 getEstTrianglesStreamingCost(LLUUID mesh_id);
+ F32 getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
+ static F32 getStreamingCostLegacy(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
+ bool getCostData(LLUUID mesh_id, LLMeshCostData& data);
+ bool getCostData(LLSD& header, LLMeshCostData& data);
LLMeshRepository();
@@ -623,5 +675,8 @@ public:
extern LLMeshRepository gMeshRepo;
+const F32 ANIMATED_OBJECT_BASE_COST = 15.0f;
+const F32 ANIMATED_OBJECT_COST_PER_KTRI = 1.5f;
+
#endif
diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp
index 1c4384ff08..96dd309fa4 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)
@@ -345,7 +355,35 @@ 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 0
+ if (!enabled_animated_object_box)
+ {
+ LL_INFOS() << "not enabled: srv " << single_root_volume << " root_volobjp " << (bool) root_volobjp << LL_ENDL;
+ if (root_volobjp)
+ {
+ LL_INFOS() << " cba " << root_volobjp->canBeAnimatedObject()
+ << " editable " << editable << " permModify() " << root_volobjp->permModify()
+ << " ispermenf " << root_volobjp->isPermanentEnforced() << LL_ENDL;
+ }
+ }
+#endif
+ 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);
@@ -582,6 +620,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);
@@ -891,6 +930,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/llpipelinelistener.cpp b/indra/newview/llpipelinelistener.cpp
index dfbe689f56..f11cdcf876 100644
--- a/indra/newview/llpipelinelistener.cpp
+++ b/indra/newview/llpipelinelistener.cpp
@@ -87,7 +87,7 @@ namespace {
U32 render_feature = feature_from_string( iter->asString() );
if ( render_feature != 0 )
{
- LLPipeline::toggleRenderDebugControl( render_feature );
+ LLPipeline::toggleRenderDebugFeatureControl( render_feature );
}
}
}
@@ -123,7 +123,7 @@ namespace {
iter != request["displays"].endArray();
++iter)
{
- U32 info_display = info_display_from_string( iter->asString() );
+ U64 info_display = info_display_from_string( iter->asString() );
if ( info_display != 0 )
{
LLPipeline::toggleRenderDebug( info_display );
@@ -134,7 +134,7 @@ namespace {
void has_info_display_wrapper(LLSD const& request)
{
LLEventAPI::Response response(LLSD(), request);
- U32 info_display = info_display_from_string( request["display"].asString() );
+ U64 info_display = info_display_from_string( request["display"].asString() );
if ( info_display != 0 )
{
response["value"] = gPipeline.hasRenderDebugMask(info_display);
diff --git a/indra/newview/llsceneview.cpp b/indra/newview/llsceneview.cpp
index 112fa5b4e1..f7aa63e34d 100644
--- a/indra/newview/llsceneview.cpp
+++ b/indra/newview/llsceneview.cpp
@@ -33,6 +33,7 @@
#include "llviewerregion.h"
#include "llagent.h"
#include "llvolumemgr.h"
+#include "llmeshrepository.h"
LLSceneView* gSceneView = NULL;
@@ -129,17 +130,23 @@ void LLSceneView::draw()
visible_triangles[idx].push_back(visible);
triangles[idx].push_back(high_triangles);
- S32 bytes = 0;
- S32 visible_bytes = 0;
-
- F32 streaming = object->getStreamingCost(&bytes, &visible_bytes);
- total_streaming[idx] += streaming;
- streaming_cost[idx].push_back(streaming);
+ F32 streaming = object->getStreamingCost();
+ total_streaming[idx] += streaming;
+ streaming_cost[idx].push_back(streaming);
F32 physics = object->getPhysicsCost();
total_physics[idx] += physics;
physics_cost[idx].push_back(physics);
+ S32 bytes = 0;
+ S32 visible_bytes = 0;
+ LLMeshCostData costs;
+ if (object->getCostData(costs))
+ {
+ bytes = costs.getSizeTotal();
+ visible_bytes = costs.getSizeByLOD(object->getLOD());
+ }
+
total_bytes[idx] += bytes;
total_visible_bytes[idx] += visible_bytes;
}
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index fce21fa30a..4a2d545b33 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;
}
@@ -6659,7 +6668,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
@@ -6677,23 +6689,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
@@ -6770,6 +6771,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()
@@ -7246,10 +7300,16 @@ F32 LLObjectSelection::getSelectedObjectStreamingCost(S32* total_bytes, S32* vis
if (object)
{
- S32 bytes = 0;
- S32 visible = 0;
- cost += object->getStreamingCost(&bytes, &visible);
+ cost += object->getStreamingCost();
+ S32 bytes = 0;
+ S32 visible = 0;
+ LLMeshCostData costs;
+ if (object->getCostData(costs))
+ {
+ bytes = costs.getSizeTotal();
+ visible = costs.getSizeByLOD(object->getLOD());
+ }
if (total_bytes)
{
*total_bytes += bytes;
@@ -7406,6 +7466,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 87ac899325..caf104178f 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)
@@ -748,6 +751,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;
@@ -849,7 +854,7 @@ private:
LLFrameTimer mEffectsTimer;
BOOL mForceSelection;
- LLAnimPauseRequest mPauseRequest;
+ std::vector<LLAnimPauseRequest> mPauseRequests;
};
// *DEPRECATED: For callbacks or observers, use
diff --git a/indra/newview/llskinningutil.cpp b/indra/newview/llskinningutil.cpp
index dba690242a..0fa4c2b114 100644
--- a/indra/newview/llskinningutil.cpp
+++ b/indra/newview/llskinningutil.cpp
@@ -31,26 +31,73 @@
#include "llvoavatar.h"
#include "llviewercontrol.h"
#include "llmeshrepository.h"
+#include "llvolume.h"
+#include "llrigginginfo.h"
+
+void dump_avatar_and_skin_state(const std::string& reason, LLVOAvatar *avatar, const LLMeshSkinInfo *skin)
+{
+ static S32 dump_count = 0;
+ const S32 max_dump = 10;
+
+ if (dump_count < max_dump)
+ {
+ LL_WARNS("Avatar") << avatar->getFullname() << " dumping, reason " << reason
+ << " avatar build state: isBuilt() " << avatar->isBuilt()
+ << " mInitFlags " << avatar->mInitFlags << LL_ENDL;
+ LL_WARNS("Avatar") << "Skin num joints " << skin->mJointNames.size() << " " << skin->mJointNums.size() << LL_ENDL;
+ LL_WARNS("Avatar") << "Skin scrubbed " << skin->mInvalidJointsScrubbed
+ << " nums init " << skin->mJointNumsInitialized << LL_ENDL;
+ for (S32 j=0; j<skin->mJointNames.size(); j++)
+ {
+ LL_WARNS("Avatar") << "skin joint idx " << j << " name [" << skin->mJointNames[j]
+ << "] num " << skin->mJointNums[j] << LL_ENDL;
+ const std::string& name = skin->mJointNames[j];
+ S32 joint_num = skin->mJointNums[j];
+
+ LLJoint *name_joint = avatar->getJoint(name);
+ LLJoint *num_joint = avatar->getJoint(joint_num);
+ if (!name_joint)
+ {
+ LL_WARNS("Avatar") << "failed to find joint by name" << LL_ENDL;
+ }
+ if (!num_joint)
+ {
+ LL_WARNS("Avatar") << "failed to find joint by num" << LL_ENDL;
+ }
+ if (num_joint != name_joint)
+ {
+ LL_WARNS("Avatar") << "joint pointers don't match" << LL_ENDL;
+ }
+ if (num_joint && num_joint->getJointNum() != joint_num)
+ {
+ LL_WARNS("Avatar") << "joint found by num has wrong num " << joint_num << "!=" << num_joint->getJointNum() << LL_ENDL;
+ }
+ if (name_joint && name_joint->getJointNum() != joint_num)
+ {
+ LL_WARNS("Avatar") << "joint found by name has wrong num " << joint_num << "!=" << name_joint->getJointNum() << LL_ENDL;
+ }
+ }
+ LL_WARNS("Avatar") << LL_ENDL;
+
+ dump_count++;
+ }
+}
-// static
void LLSkinningUtil::initClass()
{
}
-// static
U32 LLSkinningUtil::getMaxJointCount()
{
U32 result = LL_MAX_JOINTS_PER_MESH_OBJECT;
return result;
}
-// static
U32 LLSkinningUtil::getMeshJointCount(const LLMeshSkinInfo *skin)
{
return llmin((U32)getMaxJointCount(), (U32)skin->mJointNames.size());
}
-// static
void LLSkinningUtil::scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin)
{
if (skin->mInvalidJointsScrubbed)
@@ -64,35 +111,25 @@ void LLSkinningUtil::scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin
// needed for handling of any legacy bad data.
if (!avatar->getJoint(skin->mJointNames[j]))
{
- LL_DEBUGS("Avatar") << "Mesh rigged to invalid joint" << skin->mJointNames[j] << LL_ENDL;
+ LL_DEBUGS("Avatar") << avatar->getFullname() << " mesh rigged to invalid joint " << skin->mJointNames[j] << LL_ENDL;
+ LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " mesh rigged to invalid joint" << skin->mJointNames[j] << LL_ENDL;
skin->mJointNames[j] = "mPelvis";
+ skin->mJointNumsInitialized = false; // force update after names change.
}
}
skin->mInvalidJointsScrubbed = true;
}
-// static
void LLSkinningUtil::initSkinningMatrixPalette(
LLMatrix4* mat,
S32 count,
const LLMeshSkinInfo* skin,
LLVOAvatar *avatar)
{
+ initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar);
for (U32 j = 0; j < count; ++j)
{
- LLJoint *joint = NULL;
- if (skin->mJointNums[j] == -1)
- {
- joint = avatar->getJoint(skin->mJointNames[j]);
- if (joint)
- {
- skin->mJointNums[j] = joint->getJointNum();
- }
- }
- else
- {
- joint = avatar->getJoint(skin->mJointNums[j]);
- }
+ LLJoint *joint = avatar->getJoint(skin->mJointNums[j]);
if (joint)
{
#define MAT_USE_SSE
@@ -114,12 +151,19 @@ void LLSkinningUtil::initSkinningMatrixPalette(
// rendering should be disabled unless all joints are
// valid. In other cases of skinned rendering, invalid
// joints should already have been removed during scrubInvalidJoints().
- LL_WARNS_ONCE("Avatar") << "Rigged to invalid joint name " << skin->mJointNames[j] << LL_ENDL;
+ LL_WARNS_ONCE("Avatar") << avatar->getFullname()
+ << " rigged to invalid joint name " << skin->mJointNames[j]
+ << " num " << skin->mJointNums[j] << LL_ENDL;
+ LL_WARNS_ONCE("Avatar") << avatar->getFullname()
+ << " avatar build state: isBuilt() " << avatar->isBuilt()
+ << " mInitFlags " << avatar->mInitFlags << LL_ENDL;
+#if 0
+ dump_avatar_and_skin_state("initSkinningMatrixPalette joint not found", avatar, skin);
+#endif
}
}
}
-// static
void LLSkinningUtil::checkSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin)
{
#ifdef SHOW_ASSERT // same condition that controls llassert()
@@ -159,7 +203,6 @@ void LLSkinningUtil::scrubSkinWeights(LLVector4a* weights, U32 num_vertices, con
checkSkinWeights(weights, num_vertices, skin);
}
-// static
void LLSkinningUtil::getPerVertexSkinMatrix(
F32* weights,
LLMatrix4a* mat,
@@ -216,3 +259,150 @@ void LLSkinningUtil::getPerVertexSkinMatrix(
llassert(valid_weights);
}
+void LLSkinningUtil::initJointNums(LLMeshSkinInfo* skin, LLVOAvatar *avatar)
+{
+ if (!skin->mJointNumsInitialized)
+ {
+ for (U32 j = 0; j < skin->mJointNames.size(); ++j)
+ {
+ LLJoint *joint = NULL;
+ if (skin->mJointNums[j] == -1)
+ {
+ joint = avatar->getJoint(skin->mJointNames[j]);
+ if (joint)
+ {
+ skin->mJointNums[j] = joint->getJointNum();
+ if (skin->mJointNums[j] < 0)
+ {
+ LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " joint has unusual number " << skin->mJointNames[j] << ": " << skin->mJointNums[j] << LL_ENDL;
+ LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " avatar build state: isBuilt() " << avatar->isBuilt() << " mInitFlags " << avatar->mInitFlags << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " unable to find joint " << skin->mJointNames[j] << LL_ENDL;
+ LL_WARNS_ONCE("Avatar") << avatar->getFullname() << " avatar build state: isBuilt() " << avatar->isBuilt() << " mInitFlags " << avatar->mInitFlags << LL_ENDL;
+#if 0
+ dump_avatar_and_skin_state("initJointNums joint not found", avatar, skin);
+#endif
+ }
+ }
+ }
+ skin->mJointNumsInitialized = true;
+ }
+}
+
+static LLTrace::BlockTimerStatHandle FTM_FACE_RIGGING_INFO("Face Rigging Info");
+
+void LLSkinningUtil::updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *avatar, LLVolumeFace& vol_face)
+{
+ LL_RECORD_BLOCK_TIME(FTM_FACE_RIGGING_INFO);
+
+ if (vol_face.mJointRiggingInfoTab.needsUpdate())
+ {
+ S32 num_verts = vol_face.mNumVertices;
+ if (num_verts>0 && vol_face.mWeights && (skin->mJointNames.size()>0))
+ {
+ initJointNums(const_cast<LLMeshSkinInfo*>(skin), avatar);
+ if (vol_face.mJointRiggingInfoTab.size()==0)
+ {
+ //std::set<S32> active_joints;
+ //S32 active_verts = 0;
+ vol_face.mJointRiggingInfoTab.resize(LL_CHARACTER_MAX_ANIMATED_JOINTS);
+ LLJointRiggingInfoTab &rig_info_tab = vol_face.mJointRiggingInfoTab;
+ for (S32 i=0; i<vol_face.mNumVertices; i++)
+ {
+ LLVector4a& pos = vol_face.mPositions[i];
+ F32 *weights = vol_face.mWeights[i].getF32ptr();
+ LLVector4 wght;
+ S32 idx[4];
+ F32 scale = 0.0f;
+ // FIXME unpacking of weights should be pulled into a common function and optimized if possible.
+ for (U32 k = 0; k < 4; k++)
+ {
+ F32 w = weights[k];
+ idx[k] = llclamp((S32) floorf(w), (S32)0, (S32)LL_CHARACTER_MAX_ANIMATED_JOINTS-1);
+ wght[k] = w - idx[k];
+ scale += wght[k];
+ }
+ if (scale > 0.0f)
+ {
+ for (U32 k=0; k<4; ++k)
+ {
+ wght[k] /= scale;
+ }
+ }
+ for (U32 k=0; k<4; ++k)
+ {
+ S32 joint_index = idx[k];
+ if (wght[k] > 0.0f)
+ {
+ S32 joint_num = skin->mJointNums[joint_index];
+ if (joint_num >= 0 && joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS)
+ {
+ rig_info_tab[joint_num].setIsRiggedTo(true);
+
+ // FIXME could precompute these matMuls.
+ LLMatrix4a bind_shape;
+ bind_shape.loadu(skin->mBindShapeMatrix);
+ LLMatrix4a inv_bind;
+ inv_bind.loadu(skin->mInvBindMatrix[joint_index]);
+ LLMatrix4a mat;
+ matMul(bind_shape, inv_bind, mat);
+ LLVector4a pos_joint_space;
+ mat.affineTransform(pos, pos_joint_space);
+ pos_joint_space.mul(wght[k]);
+ LLVector4a *extents = rig_info_tab[joint_num].getRiggedExtents();
+ update_min_max(extents[0], extents[1], pos_joint_space);
+ }
+ }
+ }
+ }
+ //LL_DEBUGS("RigSpammish") << "built rigging info for vf " << &vol_face
+ // << " num_verts " << vol_face.mNumVertices
+ // << " active joints " << active_joints.size()
+ // << " active verts " << active_verts
+ // << LL_ENDL;
+ vol_face.mJointRiggingInfoTab.setNeedsUpdate(false);
+ }
+ }
+ if (vol_face.mJointRiggingInfoTab.size()!=0)
+ {
+ LL_DEBUGS("RigSpammish") << "we have rigging info for vf " << &vol_face
+ << " num_verts " << vol_face.mNumVertices << LL_ENDL;
+ }
+ else
+ {
+ LL_DEBUGS("RigSpammish") << "no rigging info for vf " << &vol_face
+ << " num_verts " << vol_face.mNumVertices << LL_ENDL;
+ }
+
+ }
+}
+
+// This is used for extracting rotation from a bind shape matrix that
+// already has scales baked in
+LLQuaternion LLSkinningUtil::getUnscaledQuaternion(const LLMatrix4& mat4)
+{
+ LLMatrix3 bind_mat = mat4.getMat3();
+ for (auto i = 0; i < 3; i++)
+ {
+ F32 len = 0.0f;
+ for (auto j = 0; j < 3; j++)
+ {
+ len += bind_mat.mMatrix[i][j] * bind_mat.mMatrix[i][j];
+ }
+ if (len > 0.0f)
+ {
+ len = sqrt(len);
+ for (auto j = 0; j < 3; j++)
+ {
+ bind_mat.mMatrix[i][j] /= len;
+ }
+ }
+ }
+ bind_mat.invert();
+ LLQuaternion bind_rot = bind_mat.quaternion();
+ bind_rot.normalize();
+ return bind_rot;
+}
diff --git a/indra/newview/llskinningutil.h b/indra/newview/llskinningutil.h
index 135b25d4d2..ccc501adc0 100644
--- a/indra/newview/llskinningutil.h
+++ b/indra/newview/llskinningutil.h
@@ -30,18 +30,21 @@
class LLVOAvatar;
class LLMeshSkinInfo;
class LLMatrix4a;
+class LLVolumeFace;
-class LLSkinningUtil
+namespace LLSkinningUtil
{
-public:
- static void initClass();
- static U32 getMaxJointCount();
- static U32 getMeshJointCount(const LLMeshSkinInfo *skin);
- static void scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin);
- static void initSkinningMatrixPalette(LLMatrix4* mat, S32 count, const LLMeshSkinInfo* skin, LLVOAvatar *avatar);
- static void checkSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin);
- static void scrubSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin);
- static void getPerVertexSkinMatrix(F32* weights, LLMatrix4a* mat, bool handle_bad_scale, LLMatrix4a& final_mat, U32 max_joints);
+ void initClass();
+ U32 getMaxJointCount();
+ U32 getMeshJointCount(const LLMeshSkinInfo *skin);
+ void scrubInvalidJoints(LLVOAvatar *avatar, LLMeshSkinInfo* skin);
+ void initSkinningMatrixPalette(LLMatrix4* mat, S32 count, const LLMeshSkinInfo* skin, LLVOAvatar *avatar);
+ void checkSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin);
+ void scrubSkinWeights(LLVector4a* weights, U32 num_vertices, const LLMeshSkinInfo* skin);
+ void getPerVertexSkinMatrix(F32* weights, LLMatrix4a* mat, bool handle_bad_scale, LLMatrix4a& final_mat, U32 max_joints);
+ void initJointNums(LLMeshSkinInfo* skin, LLVOAvatar *avatar);
+ void updateRiggingInfo(const LLMeshSkinInfo* skin, LLVOAvatar *avatar, LLVolumeFace& vol_face);
+ LLQuaternion getUnscaledQuaternion(const LLMatrix4& mat4);
};
#endif
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 9791f4a921..1dc1e65fe5 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"
@@ -52,6 +53,9 @@
#include "llvolumemgr.h"
#include "lltextureatlas.h"
#include "llviewershadermgr.h"
+#include "llcontrolavatar.h"
+
+//#pragma optimize("", off)
static LLTrace::BlockTimerStatHandle FTM_FRUSTUM_CULL("Frustum Culling");
static LLTrace::BlockTimerStatHandle FTM_CULL_REBOUND("Cull Rebound Partition");
@@ -777,6 +781,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 +816,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 +825,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 +898,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 +2138,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 +2156,41 @@ 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);
+ LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(drawable->getVObj()->asAvatar());
+ if (cav)
+ {
+ bool has_pos_constraint = (cav->mPositionConstraintFixup != LLVector3());
+ bool has_scale_constraint = (cav->mScaleConstraintFixup != 1.0f);
+ if (has_pos_constraint || has_scale_constraint)
+ {
+ gGL.diffuseColor4f(1,0,0,1);
+ }
+ else
+ {
+ gGL.diffuseColor4f(0,1,0.5,1);
+ }
+ }
+ else
+ {
+ gGL.diffuseColor4f(1,0,1,1); // magenta
+ }
break;
}
}
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index 6104b92d43..7e65da42f7 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -468,8 +468,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 fbb9030700..a9a0b89078 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -2395,6 +2395,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 02fec1eb75..e43015a793 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -786,7 +786,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 5082e16685..9e37ca0394 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -111,9 +111,11 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask)
mMouseOutsideSlop = FALSE;
mMouseDownX = x;
mMouseDownY = y;
-
+ LLTimer pick_timer;
+ BOOL pick_rigged = false; //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 +130,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
@@ -544,7 +549,8 @@ void LLToolPie::selectionPropertiesReceived()
BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask)
{
- mHoverPick = gViewerWindow->pickImmediate(x, y, FALSE, FALSE);
+ BOOL pick_rigged = false; //gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick");
+ mHoverPick = gViewerWindow->pickImmediate(x, y, FALSE, pick_rigged);
LLViewerObject *parent = NULL;
LLViewerObject *object = mHoverPick.getObject();
LLSelectMgr::getInstance()->setHoverObject(object, mHoverPick.mObjectFace);
@@ -590,7 +596,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();
@@ -676,6 +682,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
@@ -765,6 +772,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)
@@ -1757,8 +1765,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 c22eb48eef..e52bc0b015 100644
--- a/indra/newview/lltoolselect.cpp
+++ b/indra/newview/lltoolselect.cpp
@@ -64,7 +64,8 @@ LLToolSelect::LLToolSelect( LLToolComposite* composite )
BOOL LLToolSelect::handleMouseDown(S32 x, S32 y, MASK mask)
{
// do immediate pick query
- mPick = gViewerWindow->pickImmediate(x, y, TRUE, FALSE);
+ BOOL pick_rigged = false; //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..bae32f7bc0 100644
--- a/indra/newview/lltoolselectrect.cpp
+++ b/indra/newview/lltoolselectrect.cpp
@@ -71,7 +71,8 @@ void dialog_refresh_all(void);
BOOL LLToolSelectRect::handleMouseDown(S32 x, S32 y, MASK mask)
{
- handlePick(gViewerWindow->pickImmediate(x, y, TRUE, FALSE));
+ BOOL pick_rigged = false; //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..e4e266c92a
--- /dev/null
+++ b/indra/newview/lluiavatar.cpp
@@ -0,0 +1,61 @@
+/**
+ * @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);
+
+ mInitFlags |= 1<<3;
+}
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 ddd287e6b1..00189f0b11 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -961,7 +961,7 @@ class LLAdvancedSetDisplayTextureDensity : public view_listener_t
//////////////////
// INFO DISPLAY //
//////////////////
-U32 info_display_from_string(std::string info_display)
+U64 info_display_from_string(std::string info_display)
{
if ("verify" == info_display)
{
@@ -1075,6 +1075,14 @@ 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 if ("impostors" == info_display)
+ {
+ return LLPipeline::RENDER_DEBUG_IMPOSTORS;
+ }
else
{
LL_WARNS() << "unrecognized feature name '" << info_display << "'" << LL_ENDL;
@@ -1086,7 +1094,7 @@ class LLAdvancedToggleInfoDisplay : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
- U32 info_display = info_display_from_string( userdata.asString() );
+ U64 info_display = info_display_from_string( userdata.asString() );
LL_INFOS("ViewerMenu") << "toggle " << userdata.asString() << LL_ENDL;
@@ -1104,7 +1112,7 @@ class LLAdvancedCheckInfoDisplay : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
- U32 info_display = info_display_from_string( userdata.asString() );
+ U64 info_display = info_display_from_string( userdata.asString() );
bool new_value = false;
if ( info_display != 0 )
@@ -1614,7 +1622,24 @@ 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 if (obj && obj->isAvatar())
+ {
+ // This has to be a non-control avatar, because control avs are invisible and unclickable.
+ return gSavedSettings.getBOOL("DebugAvatarAppearanceMessage");
+ }
+ else
+ {
+ return false;
+ }
}
};
@@ -1623,13 +1648,34 @@ class LLAdvancedAppearanceToXML : public view_listener_t
bool handleEvent(const LLSD& userdata)
{
std::string emptyname;
- LLVOAvatar* avatar =
- find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() );
- if (!avatar)
- {
+ LLViewerObject *obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject();
+ LLVOAvatar *avatar = NULL;
+ if (obj)
+ {
+ if (obj->isAvatar())
+ {
+ avatar = obj->asAvatar();
+ }
+ else
+ {
+ // If there is a selection, find the associated
+ // avatar. Normally there's only one obvious choice. But
+ // what should be returned if the object is in an attached
+ // animated object? getAvatar() will give the skeleton of
+ // the animated object. getAvatarAncestor() will give the
+ // actual human-driven avatar.
+ avatar = obj->getAvatar();
+ }
+ }
+ else
+ {
+ // If no selection, use the self avatar.
avatar = gAgentAvatarp;
- }
- avatar->dumpArchetypeXML(emptyname);
+ }
+ if (avatar)
+ {
+ avatar->dumpArchetypeXML(emptyname);
+ }
return true;
}
};
@@ -6055,7 +6101,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);
@@ -6064,6 +6115,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)
@@ -6897,7 +6962,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)
@@ -9105,6 +9170,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/llviewermenu.h b/indra/newview/llviewermenu.h
index 7abb0c8e74..6882405407 100644
--- a/indra/newview/llviewermenu.h
+++ b/indra/newview/llviewermenu.h
@@ -143,7 +143,7 @@ void handle_export_selected( void * );
// Convert strings to internal types
U32 render_type_from_string(std::string render_type);
U32 feature_from_string(std::string feature);
-U32 info_display_from_string(std::string info_display);
+U64 info_display_from_string(std::string info_display);
class LLViewerMenuHolderGL : public LLMenuHolderGL
{
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 04cfe270fe..8f8b9afaf5 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -54,6 +54,7 @@
#include "llagentcamera.h"
#include "llcallingcard.h"
#include "llbuycurrencyhtml.h"
+#include "llcontrolavatar.h"
#include "llfirstuse.h"
#include "llfloaterbump.h"
#include "llfloaterbuyland.h"
@@ -102,6 +103,7 @@
#include "llviewerwindow.h"
#include "llvlmanager.h"
#include "llvoavatarself.h"
+#include "llvovolume.h"
#include "llworld.h"
#include "pipeline.h"
#include "llfloaterworldmap.h"
@@ -4023,23 +4025,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())
@@ -4110,6 +4116,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("AnimatedObjectsNotify") << "Received animation state for object " << uuid << LL_ENDL;
+
+ signaled_animation_map_t signaled_anims;
+ S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList);
+ LL_DEBUGS("AnimatedObjectsNotify") << "processing object animation requests, num_blocks " << num_blocks << " uuid " << uuid << LL_ENDL;
+ 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);
+ signaled_anims[animation_id] = anim_sequence_id;
+ LL_DEBUGS("AnimatedObjectsNotify") << "added signaled_anims animation request for object "
+ << uuid << " animation id " << animation_id << LL_ENDL;
+ }
+ LLObjectSignaledAnimationMap::instance().getMap()[uuid] = signaled_anims;
+
+ LLViewerObject *objp = gObjectList.findObject(uuid);
+ if (!objp)
+ {
+ LL_DEBUGS("AnimatedObjectsNotify") << "Received animation state for unknown object " << uuid << LL_ENDL;
+ return;
+ }
+
+ LLVOVolume *volp = dynamic_cast<LLVOVolume*>(objp);
+ if (!volp)
+ {
+ LL_DEBUGS("AnimatedObjectsNotify") << "Received animation state for non-volume object " << uuid << LL_ENDL;
+ return;
+ }
+
+ if (!volp->isAnimatedObject())
+ {
+ LL_DEBUGS("AnimatedObjectsNotify") << "Received animation state for non-animated object " << uuid << LL_ENDL;
+ return;
+ }
+
+ volp->updateControlAvatar();
+ LLControlAvatar *avatarp = volp->getControlAvatar();
+ if (!avatarp)
+ {
+ LL_DEBUGS("AnimatedObjectsNotify") << "Received animation request for object with no control avatar, ignoring " << uuid << LL_ENDL;
+ return;
+ }
+
+ if (!avatarp->mPlaying)
+ {
+ avatarp->mPlaying = true;
+ //if (!avatarp->mRootVolp->isAnySelected())
+ {
+ avatarp->updateVolumeGeom();
+ avatarp->mRootVolp->recursiveMarkForUpdate(TRUE);
+ }
+ }
+
+ 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 913abef2be..2d6636f30d 100644
--- a/indra/newview/llviewermessage.h
+++ b/indra/newview/llviewermessage.h
@@ -100,6 +100,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 955cc79283..dcd09f66c7 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,8 @@
#include "llfloaterperms.h"
#include "llvocache.h"
#include "llcleanup.h"
+#include "llcallstack.h"
+#include "llmeshrepository.h"
//#define DEBUG_UPDATE_TYPE
@@ -139,8 +143,11 @@ const U32 MAX_INV_FILE_READ_FAILS = 25;
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);
@@ -167,6 +174,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);
@@ -236,6 +255,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),
@@ -260,7 +280,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),
@@ -366,17 +386,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->updateAttachmentOverrides();
+ }
+ }
+ if (getControlAvatar())
+ {
+ unlinkControlAvatar();
+ }
// Mark itself as dead
mDead = TRUE;
@@ -677,6 +703,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;
@@ -849,9 +887,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)
@@ -1068,6 +1115,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.
@@ -1122,10 +1172,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
@@ -1381,7 +1431,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);
@@ -1651,7 +1701,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
U8 state;
mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num );
- mState = state;
+ mAttachmentState = state;
break;
}
@@ -1674,7 +1724,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
U8 state;
dp->unpackU8(state, "State");
- mState = state;
+ mAttachmentState = state;
switch(update_type)
{
@@ -2907,6 +2957,131 @@ void LLViewerObject::fetchInventoryFromServer()
}
}
+LLControlAvatar *LLViewerObject::getControlAvatar()
+{
+ return getRootEdit()->mControlAvatar.get();
+}
+
+LLControlAvatar *LLViewerObject::getControlAvatar() const
+{
+ return getRootEdit()->mControlAvatar.get();
+}
+
+// Manage the control avatar state of a given object.
+// Any object can be flagged as animated, but for performance reasons
+// we don't want to incur the overhead of managing a control avatar
+// unless this would have some user-visible consequence. That is,
+// there should be at least one rigged mesh in the linkset. Operations
+// that change the state of a linkset, such as linking or unlinking
+// prims, can also mean that a control avatar needs to be added or
+// removed. At the end, if there is a control avatar, we make sure
+// that its animation state is current.
+void LLViewerObject::updateControlAvatar()
+{
+ LLViewerObject *root = getRootEdit();
+ bool is_animated_object = root->isAnimatedObject();
+ bool has_control_avatar = getControlAvatar();
+ if (!is_animated_object && !has_control_avatar)
+ {
+ return;
+ }
+
+ bool should_have_control_avatar = false;
+ if (is_animated_object)
+ {
+ bool any_rigged_mesh = root->isRiggedMesh();
+ 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_rigged_mesh = any_rigged_mesh || child->isRiggedMesh();
+ }
+ should_have_control_avatar = is_animated_object && any_rigged_mesh;
+ }
+
+ if (should_have_control_avatar && !has_control_avatar)
+ {
+ std::string vobj_name = llformat("Vol%p", root);
+ LL_DEBUGS("AnimatedObjects") << vobj_name << " calling linkControlAvatar()" << LL_ENDL;
+ root->linkControlAvatar();
+ }
+ if (!should_have_control_avatar && has_control_avatar)
+ {
+ std::string vobj_name = llformat("Vol%p", root);
+ LL_DEBUGS("AnimatedObjects") << vobj_name << " calling unlinkControlAvatar()" << LL_ENDL;
+ root->unlinkControlAvatar();
+ }
+ if (getControlAvatar())
+ {
+ getControlAvatar()->updateAnimations();
+ if (isSelected())
+ {
+ LLSelectMgr::getInstance()->pauseAssociatedAvatars();
+ }
+ }
+}
+
+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;
+ }
+ LLControlAvatar *cav = getControlAvatar();
+ if (cav)
+ {
+ cav->updateAttachmentOverrides();
+ if (!cav->mPlaying)
+ {
+ cav->mPlaying = true;
+ //if (!cav->mRootVolp->isAnySelected())
+ {
+ cav->updateVolumeGeom();
+ cav->mRootVolp->recursiveMarkForUpdate(TRUE);
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS() << "no control avatar found!" << LL_ENDL;
+ }
+}
+
+void LLViewerObject::unlinkControlAvatar()
+{
+ if (getControlAvatar())
+ {
+ getControlAvatar()->updateAttachmentOverrides();
+ }
+ 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;
@@ -3531,11 +3706,66 @@ F32 LLViewerObject::getLinksetPhysicsCost()
return mLinksetPhysicsCost;
}
-F32 LLViewerObject::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const
+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;
+ if (!child->isAvatar())
+ {
+ est_tris += child->recursiveGetEstTrianglesMax();
+ }
+ }
+ return est_tris;
+}
+
+S32 LLViewerObject::getAnimatedObjectMaxTris() const
+{
+ S32 max_tris = 0;
+ if (gSavedSettings.getBOOL("AnimatedObjectsIgnoreLimits"))
+ {
+ 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::getEstTrianglesStreamingCost() const
+{
+ return 0.f;
+}
+
+// virtual
+F32 LLViewerObject::getStreamingCost() const
{
return 0.f;
}
+// virtual
+bool LLViewerObject::getCostData(LLMeshCostData& costs) const
+{
+ costs = LLMeshCostData();
+ return false;
+}
+
U32 LLViewerObject::getTriangleCount(S32* vcount) const
{
return 0;
@@ -3546,6 +3776,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;
@@ -3649,7 +3931,6 @@ void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */)
}
}
-
void LLViewerObject::setLineWidthForWindowSize(S32 window_width)
{
if (window_width < 700)
@@ -3876,8 +4157,20 @@ const LLVector3 LLViewerObject::getRenderPosition() const
{
if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED))
{
+ LLControlAvatar *cav = getControlAvatar();
+ if (isRoot() && cav)
+ {
+ F32 fixup;
+ if ( cav->hasPelvisFixup( fixup) )
+ {
+ //Apply a pelvis fixup (as defined by the avs skin)
+ LLVector3 pos = mDrawable->getPositionAgent();
+ pos[VZ] += fixup;
+ return pos;
+ }
+ }
LLVOAvatar* avatar = getAvatar();
- if (avatar)
+ if ((avatar) && !getControlAvatar())
{
return avatar->getPositionAgent();
}
@@ -3901,7 +4194,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;
}
@@ -5163,7 +5456,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();
@@ -5502,6 +5801,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;
@@ -5901,6 +6205,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())
@@ -5960,6 +6275,11 @@ void LLViewerObject::setRegion(LLViewerRegion *regionp)
child->setRegion(regionp);
}
+ if (mControlAvatar)
+ {
+ mControlAvatar->setRegion(regionp);
+ }
+
setChanged(MOVED | SILHOUETTE);
updateDrawable(FALSE);
}
@@ -6408,6 +6728,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 d61832c2ad..d6c8b76147 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -42,11 +42,13 @@
#include "v3math.h"
#include "llvertexbuffer.h"
#include "llbbox.h"
+#include "llrigginginfo.h"
class LLAgent; // TODO: Get rid of this.
class LLAudioSource;
class LLAudioSourceVO;
class LLColor4;
+class LLControlAvatar;
class LLDataPacker;
class LLDataPackerBinaryBuffer;
class LLDrawable;
@@ -67,6 +69,8 @@ class LLViewerRegion;
class LLViewerTexture;
class LLWorld;
+class LLMeshCostData;
+
typedef enum e_object_update_type
{
OUT_FULL,
@@ -220,6 +224,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 +237,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 +262,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 +365,17 @@ public:
virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE);
- virtual F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL, F32* unscaled_value = NULL) const;
+ S32 getAnimatedObjectMaxTris() const;
+ F32 recursiveGetEstTrianglesMax() const;
+ virtual F32 getEstTrianglesMax() const;
+ virtual F32 getEstTrianglesStreamingCost() const;
+ virtual F32 getStreamingCost() const;
+ virtual bool getCostData(LLMeshCostData& costs) 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 +391,7 @@ public:
void sendShapeUpdate();
- U8 getState() { return mState; }
+ U8 getAttachmentState() { return mAttachmentState; }
F32 getAppAngle() const { return mAppAngle; }
F32 getPixelArea() const { return mPixelArea; }
@@ -411,7 +428,8 @@ public:
void setIcon(LLViewerTexture* icon_image);
void clearIcon();
- void markForUpdate(BOOL priority);
+ void recursiveMarkForUpdate(BOOL priority);
+ virtual void markForUpdate(BOOL priority);
void markForUnload(BOOL priority);
void updateVolume(const LLVolumeParams& volume_params);
virtual void updateSpatialExtents(LLVector4a& min, LLVector4a& max);
@@ -686,6 +704,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
@@ -696,8 +735,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);
@@ -785,7 +823,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
@@ -841,6 +879,10 @@ public:
BOOL getLastUpdateCached() const;
void setLastUpdateCached(BOOL last_update_cached);
+ virtual void updateRiggingInfo() {}
+
+ LLJointRiggingInfoTab mJointRiggingInfoTab;
+
private:
LLUUID mAttachmentItemID; // ItemID of the associated object is in user inventory.
EObjectUpdateType mLastUpdateType;
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index 23a51b99f6..fc06455c68 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
@@ -1274,6 +1305,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())
@@ -1960,12 +1994,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;
@@ -1985,6 +2019,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)
{
@@ -2019,6 +2056,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 9371711ca1..6c3b98bef9 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 263750dee4..082c9fa6c5 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"
#include "llsettingsdaycycle.h"
#ifdef LL_WINDOWS
@@ -242,6 +243,8 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)
LL_WARNS("AppInit", "Capabilities") << "Attempting to get capabilities for region that no longer exists!" << LL_ENDL;
return; // this error condition is not recoverable.
}
+ LL_DEBUGS("AppInit", "Capabilities") << "requesting seed caps for handle " << regionHandle
+ << " name " << regionp->getName() << LL_ENDL;
std::string url = regionp->getCapability("Seed");
if (url.empty())
@@ -270,7 +273,8 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)
buildCapabilityNames(capabilityNames);
LL_INFOS("AppInit", "Capabilities") << "Requesting seed from " << url
- << " (attempt #" << mSeedCapAttempts + 1 << ")" << LL_ENDL;
+ << " region name " << regionp->getName()
+ << " (attempt #" << mSeedCapAttempts + 1 << ")" << LL_ENDL;
regionp = NULL;
result = httpAdapter->postAndSuspend(httpRequest, url, capabilityNames);
@@ -324,6 +328,8 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)
#endif
regionp->setCapabilitiesReceived(true);
+ LL_DEBUGS("AppInit", "Capabilities") << "received caps for handle " << regionHandle
+ << " region name " << regionp->getName() << LL_ENDL;
if (STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState())
{
@@ -1260,7 +1266,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;
}
@@ -2144,6 +2150,26 @@ void LLViewerRegion::getInfo(LLSD& info)
info["Region"]["Handle"]["y"] = (LLSD::Integer)y;
}
+void LLViewerRegion::requestSimulatorFeatures()
+{
+ LL_DEBUGS("SimulatorFeatures") << "region " << getName() << " ptr " << this
+ << " trying to request SimulatorFeatures" << LL_ENDL;
+ // 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);
@@ -2175,7 +2201,7 @@ void LLViewerRegion::setSimulatorFeatures(const LLSD& sim_features)
std::stringstream str;
LLSDSerialize::toPrettyXML(sim_features, str);
- LL_INFOS() << str.str() << LL_ENDL;
+ LL_INFOS() << "region " << getName() << " " << str.str() << LL_ENDL;
mSimulatorFeatures = sim_features;
setSimulatorFeaturesReceived(true);
@@ -2221,7 +2247,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)
{
@@ -2384,12 +2410,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);
@@ -2400,6 +2432,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;
@@ -2504,7 +2539,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))
@@ -2527,12 +2562,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;
@@ -2569,6 +2606,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)
@@ -2875,6 +2915,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");
@@ -2971,12 +3012,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 302647215f..d5266ec873 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 58f26d6a2b..26678775fb 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -543,7 +543,14 @@ public:
object_count++;
S32 bytes = 0;
S32 visible = 0;
- cost += object->getStreamingCost(&bytes, &visible);
+ cost += object->getStreamingCost();
+ LLMeshCostData costs;
+ if (object->getCostData(costs))
+ {
+ bytes = costs.getSizeTotal();
+ visible = costs.getSizeByLOD(object->getLOD());
+ }
+
S32 vt = 0;
count += object->getTriangleCount(&vt);
vcount += vt;
@@ -1146,7 +1153,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 b221dc7c35..321f774210 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 ),
@@ -625,6 +630,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mTyping(FALSE),
mMeshValid(FALSE),
mVisible(FALSE),
+ mLastImpostorUpdateFrameTime(0.f),
mWindFreq(0.f),
mRipplePhase( 0.f ),
mBelowWater(FALSE),
@@ -663,7 +669,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;
@@ -689,6 +698,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mNeedsImpostorUpdate = TRUE;
mNeedsAnimUpdate = TRUE;
+ mNeedsExtentUpdate = true;
+
mImpostorDistance = 0;
mImpostorPixelArea = 0;
@@ -718,6 +729,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mCurrentGesticulationLevel = 0;
+
mRuthTimer.reset();
mRuthDebugTimer.reset();
mDebugExistenceTimer.reset();
@@ -733,8 +745,15 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
std::string LLVOAvatar::avString() const
{
- std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
- return " Avatar '" + getFullname() + "' " + viz_string + " ";
+ if (isControlAvatar())
+ {
+ return getFullname();
+ }
+ else
+ {
+ std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
+ return " Avatar '" + getFullname() + "' " + viz_string + " ";
+ }
}
void LLVOAvatar::debugAvatarRezTime(std::string notification_name, std::string comment)
@@ -1091,6 +1110,8 @@ void LLVOAvatar::initClass()
// Where should this be set initially?
LLJoint::setDebugJointNames(gSavedSettings.getString("DebugAvatarJoints"));
+
+ LLControlAvatar::sRegionChangedSlot = gAgent.addRegionChangedCallback(&LLControlAvatar::onRegionChanged);
}
@@ -1099,7 +1120,7 @@ void LLVOAvatar::cleanupClass()
}
// virtual
-void LLVOAvatar::initInstance(void)
+void LLVOAvatar::initInstance()
{
//-------------------------------------------------------------------------
// register motions
@@ -1170,6 +1191,8 @@ void LLVOAvatar::initInstance(void)
//VTPause(); // VTune
mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) );
+
+ mInitFlags |= 1<<1;
}
// virtual
@@ -1222,8 +1245,6 @@ const LLVector3 LLVOAvatar::getRenderPosition() const
{
return getPosition() * mDrawable->getParent()->getRenderMatrix();
}
-
-
}
void LLVOAvatar::updateDrawable(BOOL force_damped)
@@ -1240,6 +1261,29 @@ void LLVOAvatar::onShift(const LLVector4a& shift_vector)
void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)
{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ if (mNeedsExtentUpdate)
+ {
+ calculateSpatialExtents(newMin,newMax);
+ mLastAnimExtents[0].set(newMin.getF32ptr());
+ mLastAnimExtents[1].set(newMax.getF32ptr());
+ mLastAnimBasePos = mPelvisp->getWorldPosition();
+ mNeedsExtentUpdate = false;
+ }
+ else
+ {
+ LLVector3 new_base_pos = mPelvisp->getWorldPosition();
+ LLVector3 shift = new_base_pos-mLastAnimBasePos;
+ mLastAnimExtents[0] += shift;
+ mLastAnimExtents[1] += shift;
+ mLastAnimBasePos = new_base_pos;
+
+ }
+
if (isImpostor() && !needsImpostorUpdate())
{
LLVector3 delta = getRenderPosition() -
@@ -1250,98 +1294,185 @@ void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax)
}
else
{
- getSpatialExtents(newMin,newMax);
- mLastAnimExtents[0].set(newMin.getF32ptr());
- mLastAnimExtents[1].set(newMax.getF32ptr());
+ newMin.load3(mLastAnimExtents[0].mV);
+ newMax.load3(mLastAnimExtents[1].mV);
LLVector4a pos_group;
pos_group.setAdd(newMin,newMax);
pos_group.mul(0.5f);
mImpostorOffset = LLVector3(pos_group.getF32ptr())-getRenderPosition();
mDrawable->setPositionGroup(pos_group);
}
-
-
}
-void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
+
+static LLTrace::BlockTimerStatHandle FTM_AVATAR_EXTENT_UPDATE("Av Upd Extent");
+
+void LLVOAvatar::calculateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
{
- LLVector4a buffer(0.25f);
+ LL_RECORD_BLOCK_TIME(FTM_AVATAR_EXTENT_UPDATE);
+
+ S32 box_detail = gSavedSettings.getS32("AvatarBoundingBoxComplexity");
+
+ // FIXME the update_min_max function used below assumes there is a
+ // known starting point, but in general there isn't. Ideally the
+ // box update logic should be modified to handle the no-point-yet
+ // case. For most models, starting with the pelvis is safe though.
+ LLVector3 zero_pos;
LLVector4a pos;
- pos.load3(getRenderPosition().mV);
- newMin.setSub(pos, buffer);
- newMax.setAdd(pos, buffer);
+ if (dist_vec(zero_pos, mPelvisp->getWorldPosition())<0.001)
+ {
+ // Don't use pelvis until av initialized
+ pos.load3(getRenderPosition().mV);
+ }
+ else
+ {
+ pos.load3(mPelvisp->getWorldPosition().mV);
+ }
+ newMin = pos;
+ newMax = pos;
- float max_attachment_span = get_default_max_prim_scale() * 5.0f;
-
- //stretch bounding box by joint positions
- for (polymesh_map_t::iterator i = mPolyMeshes.begin(); i != mPolyMeshes.end(); ++i)
- {
- LLPolyMesh* mesh = i->second;
- for (S32 joint_num = 0; joint_num < mesh->mJointRenderData.size(); joint_num++)
- {
- LLVector4a trans;
- trans.load3( mesh->mJointRenderData[joint_num]->mWorldMatrix->getTranslation().mV);
- update_min_max(newMin, newMax, trans);
- }
- }
+ //stretch bounding box by joint positions. Doing this for
+ //control avs, where the polymeshes aren't maintained or
+ //displayed, can give inaccurate boxes due to joints stuck at (0,0,0).
+ if ((box_detail>=1) && !isControlAvatar())
+ {
+ for (polymesh_map_t::iterator i = mPolyMeshes.begin(); i != mPolyMeshes.end(); ++i)
+ {
+ LLPolyMesh* mesh = i->second;
+ for (S32 joint_num = 0; joint_num < mesh->mJointRenderData.size(); joint_num++)
+ {
+ LLVector4a trans;
+ trans.load3( mesh->mJointRenderData[joint_num]->mWorldMatrix->getTranslation().mV);
+ update_min_max(newMin, newMax, trans);
+ }
+ }
- LLVector4a center, size;
- center.setAdd(newMin, newMax);
- center.mul(0.5f);
+ }
- size.setSub(newMax,newMin);
- size.mul(0.5f);
+ // Pad bounding box for starting joint, plus polymesh if
+ // applicable. Subsequent calcs should be accurate enough to not
+ // need padding.
+ LLVector4a padding(0.25);
+ newMin.sub(padding);
+ newMax.add(padding);
- mPixelArea = LLPipeline::calcPixelArea(center, size, *LLViewerCamera::getInstance());
- //stretch bounding box by attachments
- for (attachment_map_t::iterator iter = mAttachmentPoints.begin();
- iter != mAttachmentPoints.end();
- ++iter)
- {
- LLViewerJointAttachment* attachment = iter->second;
+ //stretch bounding box by static attachments
+ if (box_detail >= 2)
+ {
+ float max_attachment_span = get_default_max_prim_scale() * 5.0f;
+
+ for (attachment_map_t::iterator iter = mAttachmentPoints.begin();
+ iter != mAttachmentPoints.end();
+ ++iter)
+ {
+ LLViewerJointAttachment* attachment = iter->second;
- if (attachment->getValid())
- {
- for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
- attachment_iter != attachment->mAttachedObjects.end();
- ++attachment_iter)
- {
- const LLViewerObject* attached_object = (*attachment_iter);
- if (attached_object && !attached_object->isHUDAttachment())
- {
- LLDrawable* drawable = attached_object->mDrawable;
- if (drawable && !drawable->isState(LLDrawable::RIGGED))
- {
- LLSpatialBridge* bridge = drawable->getSpatialBridge();
- if (bridge)
- {
- const LLVector4a* ext = bridge->getSpatialExtents();
- LLVector4a distance;
- distance.setSub(ext[1], ext[0]);
- LLVector4a max_span(max_attachment_span);
+ if (attachment->getValid())
+ {
+ for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
+ attachment_iter != attachment->mAttachedObjects.end();
+ ++attachment_iter)
+ {
+ // Don't we need to look at children of attached_object as well?
+ const LLViewerObject* attached_object = (*attachment_iter);
+ if (attached_object && !attached_object->isHUDAttachment())
+ {
+ const LLVOVolume *vol = dynamic_cast<const LLVOVolume*>(attached_object);
+ if (vol && vol->isAnimatedObject())
+ {
+ // Animated objects already have a bounding box in their control av, use that.
+ // Could lag by a frame if there's no guarantee on order of processing for avatars.
+ LLControlAvatar *cav = vol->getControlAvatar();
+ if (cav)
+ {
+ LLVector4a cav_min;
+ cav_min.load3(cav->mLastAnimExtents[0].mV);
+ LLVector4a cav_max;
+ cav_max.load3(cav->mLastAnimExtents[1].mV);
+ update_min_max(newMin,newMax,cav_min);
+ update_min_max(newMin,newMax,cav_max);
+ continue;
+ }
+ }
+ if (vol && vol->isRiggedMesh())
+ {
+ continue;
+ }
+ LLDrawable* drawable = attached_object->mDrawable;
+ if (drawable && !drawable->isState(LLDrawable::RIGGED))
+ {
+ LLSpatialBridge* bridge = drawable->getSpatialBridge();
+ if (bridge)
+ {
+ const LLVector4a* ext = bridge->getSpatialExtents();
+ LLVector4a distance;
+ distance.setSub(ext[1], ext[0]);
+ LLVector4a max_span(max_attachment_span);
- S32 lt = distance.lessThan(max_span).getGatheredBits() & 0x7;
+ S32 lt = distance.lessThan(max_span).getGatheredBits() & 0x7;
- // Only add the prim to spatial extents calculations if it isn't a megaprim.
- // max_attachment_span calculated at the start of the function
- // (currently 5 times our max prim size)
- if (lt == 0x7)
- {
- update_min_max(newMin,newMax,ext[0]);
- update_min_max(newMin,newMax,ext[1]);
- }
- }
- }
- }
- }
- }
- }
+ // Only add the prim to spatial extents calculations if it isn't a megaprim.
+ // max_attachment_span calculated at the start of the function
+ // (currently 5 times our max prim size)
+ if (lt == 0x7)
+ {
+ update_min_max(newMin,newMax,ext[0]);
+ update_min_max(newMin,newMax,ext[1]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
- //pad bounding box
+ // Stretch bounding box by rigged mesh joint boxes
+ if (box_detail>=3)
+ {
+ updateRiggingInfo();
+ for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++)
+ {
+ LLJoint *joint = getJoint(joint_num);
+ LLJointRiggingInfo *rig_info = NULL;
+ if (joint_num < mJointRiggingInfoTab.size())
+ {
+ rig_info = &mJointRiggingInfoTab[joint_num];
+ }
+
+ if (joint && rig_info && rig_info->isRiggedTo())
+ {
+ LLViewerJointAttachment *as_joint_attach = dynamic_cast<LLViewerJointAttachment*>(joint);
+ if (as_joint_attach && as_joint_attach->getIsHUDAttachment())
+ {
+ // Ignore bounding box of HUD joints
+ continue;
+ }
+ LLMatrix4a mat;
+ LLVector4a new_extents[2];
+ mat.loadu(joint->getWorldMatrix());
+ matMulBoundBox(mat, rig_info->getRiggedExtents(), new_extents);
+ update_min_max(newMin, newMax, new_extents[0]);
+ update_min_max(newMin, newMax, new_extents[1]);
+ //if (isSelf())
+ //{
+ // LL_INFOS() << joint->getName() << " extents " << new_extents[0] << "," << new_extents[1] << LL_ENDL;
+ // LL_INFOS() << joint->getName() << " av box is " << newMin << "," << newMax << LL_ENDL;
+ //}
+ }
+ }
+ }
- newMin.sub(buffer);
- newMax.add(buffer);
+ // Update pixel area
+ LLVector4a center, size;
+ center.setAdd(newMin, newMax);
+ center.mul(0.5f);
+
+ size.setSub(newMax,newMin);
+ size.mul(0.5f);
+
+ mPixelArea = LLPipeline::calcPixelArea(center, size, *LLViewerCamera::getInstance());
}
void render_sphere_and_line(const LLVector3& begin_pos, const LLVector3& end_pos, F32 sphere_scale, const LLVector3& occ_color, const LLVector3& visible_color)
@@ -1404,13 +1535,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 +1569,6 @@ void LLVOAvatar::renderCollisionVolumes()
mNameText->lineSegmentIntersect(unused, unused, unused, TRUE);
}
-
- mDebugText.clear();
- addDebugText(ostr.str());
}
void LLVOAvatar::renderBones()
@@ -1467,7 +1611,7 @@ void LLVOAvatar::renderBones()
}
else
{
- if (jointIsRiggedTo(jointp->getName()))
+ if (jointIsRiggedTo(jointp))
{
occ_color = RIGGED_COLOR_OCCLUDED;
visible_color = RIGGED_COLOR_VISIBLE;
@@ -1595,6 +1739,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 +1829,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 +1946,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 +2010,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,12 +2064,15 @@ 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
- rebuildAttachmentOverrides();
+ updateAttachmentOverrides();
// Animations
if (reset_animations)
@@ -1942,7 +2099,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 +2119,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 +2151,10 @@ void LLVOAvatar::releaseMeshData()
void LLVOAvatar::restoreMeshData()
{
llassert(!isSelf());
+ if (mDrawable.isNull())
+ {
+ return;
+ }
//LL_INFOS() << "Restoring" << LL_ENDL;
mMeshValid = TRUE;
@@ -2291,6 +2452,18 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time)
return;
}
+ // Update should be happening max once per frame.
+ const S32 upd_freq = 4; // force update every upd_freq frames.
+ if ((mLastAnimExtents[0]==LLVector3())||
+ (mLastAnimExtents[1])==LLVector3())
+ {
+ mNeedsExtentUpdate = true;
+ }
+ else
+ {
+ mNeedsExtentUpdate = ((LLDrawable::getCurrentFrame()+mID.mData[0])%upd_freq==0);
+ }
+
LLScopedContextString str("avatar_idle_update " + getFullname());
checkTextureLoading() ;
@@ -2303,7 +2476,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 +2642,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 );
@@ -2573,8 +2746,10 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update)
}
else
{
- //VECTORIZE THIS
- getSpatialExtents(ext[0], ext[1]);
+ ext[0].load3(mLastAnimExtents[0].mV);
+ ext[1].load3(mLastAnimExtents[1].mV);
+ // Expensive. Just call this once per frame, in updateSpatialExtents();
+ //calculateSpatialExtents(ext[0], ext[1]);
LLVector4a diff;
diff.setSub(ext[1], mImpostorExtents[1]);
if (diff.getLength3().getF32() > 0.05f)
@@ -2593,13 +2768,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 +2938,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 +3537,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 +3760,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 +3902,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 +4123,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 +4132,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 +4153,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 +4175,155 @@ 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)
+ {
+ // SL-1350: Moved to LLDrawable::updateXform()
+ 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 +4342,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 +4359,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 +4385,7 @@ void LLVOAvatar::updateHeadOffset()
{
midEyePt = midEyePt * ~mDrawable->getWorldRotation();
}
- if (mIsSitting)
+ if (isSitting())
{
mHeadOffset = midEyePt;
}
@@ -4115,7 +4481,7 @@ void LLVOAvatar::updateVisibility()
if (mIsDummy)
{
- visible = TRUE;
+ visible = FALSE;
}
else if (mDrawable.isNull())
{
@@ -4229,7 +4595,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 +4631,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 +4802,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 +4812,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 +4822,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 +4851,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 +4879,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 +4927,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);
@@ -4596,22 +4965,53 @@ U32 LLVOAvatar::renderImpostor(LLColor4U color, S32 diffuse_channel)
left *= mImpostorDim.mV[0];
up *= mImpostorDim.mV[1];
- LLGLEnable test(GL_ALPHA_TEST);
- gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.f);
-
- gGL.color4ubv(color.mV);
- gGL.getTexUnit(diffuse_channel)->bind(&mImpostor);
- gGL.begin(LLRender::QUADS);
- gGL.texCoord2f(0,0);
- gGL.vertex3fv((pos+left-up).mV);
- gGL.texCoord2f(1,0);
- gGL.vertex3fv((pos-left-up).mV);
- gGL.texCoord2f(1,1);
- gGL.vertex3fv((pos-left+up).mV);
- gGL.texCoord2f(0,1);
- gGL.vertex3fv((pos+left+up).mV);
- gGL.end();
- gGL.flush();
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_IMPOSTORS))
+ {
+ LLGLEnable blend(GL_BLEND);
+ gGL.setSceneBlendType(LLRender::BT_ADD);
+ gGL.getTexUnit(diffuse_channel)->unbind(LLTexUnit::TT_TEXTURE);
+
+ // gGL.begin(LLRender::QUADS);
+ // gGL.vertex3fv((pos+left-up).mV);
+ // gGL.vertex3fv((pos-left-up).mV);
+ // gGL.vertex3fv((pos-left+up).mV);
+ // gGL.vertex3fv((pos+left+up).mV);
+ // gGL.end();
+
+
+ gGL.begin(LLRender::LINES);
+ gGL.color4f(1.f,1.f,1.f,1.f);
+ F32 thickness = llmax(F32(5.0f-5.0f*(gFrameTimeSeconds-mLastImpostorUpdateFrameTime)),1.0f);
+ glLineWidth(thickness);
+ gGL.vertex3fv((pos+left-up).mV);
+ gGL.vertex3fv((pos-left-up).mV);
+ gGL.vertex3fv((pos-left-up).mV);
+ gGL.vertex3fv((pos-left+up).mV);
+ gGL.vertex3fv((pos-left+up).mV);
+ gGL.vertex3fv((pos+left+up).mV);
+ gGL.vertex3fv((pos+left+up).mV);
+ gGL.vertex3fv((pos+left-up).mV);
+ gGL.end();
+ gGL.flush();
+ }
+ {
+ LLGLEnable test(GL_ALPHA_TEST);
+ gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.f);
+
+ gGL.color4ubv(color.mV);
+ gGL.getTexUnit(diffuse_channel)->bind(&mImpostor);
+ gGL.begin(LLRender::QUADS);
+ gGL.texCoord2f(0,0);
+ gGL.vertex3fv((pos+left-up).mV);
+ gGL.texCoord2f(1,0);
+ gGL.vertex3fv((pos-left-up).mV);
+ gGL.texCoord2f(1,1);
+ gGL.vertex3fv((pos-left+up).mV);
+ gGL.texCoord2f(0,1);
+ gGL.vertex3fv((pos+left+up).mV);
+ gGL.end();
+ gGL.flush();
+ }
return 6;
}
@@ -5105,10 +5505,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 +5521,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
@@ -5488,72 +5897,30 @@ 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;
- }
- }
- }
- return false;
-}
-
-bool LLVOAvatar::jointIsRiggedTo(const std::string& joint_name)
-{
- for (attachment_map_t::iterator iter = mAttachmentPoints.begin();
- iter != mAttachmentPoints.end();
- ++iter)
- {
- LLViewerJointAttachment* attachment = iter->second;
- for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
- attachment_iter != attachment->mAttachedObjects.end();
- ++attachment_iter)
- {
- const LLViewerObject* attached_object = (*attachment_iter);
- if (attached_object && jointIsRiggedTo(joint_name, attached_object))
- {
+ {
+ mesh_id = pSkinData->mMeshID;
return true;
}
- }
+ }
}
- return false;
+ return false;
}
-bool LLVOAvatar::jointIsRiggedTo(const std::string& joint_name, const LLViewerObject *vo)
+bool LLVOAvatar::jointIsRiggedTo(const LLJoint *joint) const
{
- // Process all children
- LLViewerObject::const_child_list_t& children = vo->getChildren();
- for (LLViewerObject::const_child_list_t::const_iterator it = children.begin();
- it != children.end(); ++it)
- {
- LLViewerObject *childp = *it;
- if (jointIsRiggedTo(joint_name,childp))
- {
- return true;
- }
- }
-
- const LLVOVolume *vobj = dynamic_cast<const LLVOVolume*>(vo);
- if (!vobj)
- {
- return false;
- }
-
- LLUUID currentId = vobj->getVolume()->getParams().getSculptID();
- const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId, vobj );
-
- if ( vobj && vobj->isAttachment() && vobj->isMesh() && pSkinData )
- {
- if (std::find(pSkinData->mJointNames.begin(), pSkinData->mJointNames.end(), joint_name) !=
- pSkinData->mJointNames.end())
+ if (joint)
+ {
+ const LLJointRiggingInfoTab& tab = mJointRiggingInfoTab;
+ S32 joint_num = joint->getJointNum();
+ if (joint_num < tab.size() && tab[joint_num].isRiggedTo())
{
return true;
}
}
-
return false;
}
@@ -5570,6 +5937,20 @@ 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();
+ }
+
+ mActiveOverrideMeshes.clear();
+ onActiveOverrideMeshesChanged();
}
//-----------------------------------------------------------------------------
@@ -5579,7 +5960,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)
@@ -5590,33 +5989,164 @@ void LLVOAvatar::rebuildAttachmentOverrides()
for (LLViewerJointAttachment::attachedobjs_vec_t::iterator at_it = attachment_pt->mAttachedObjects.begin();
at_it != attachment_pt->mAttachedObjects.end(); ++at_it)
{
- addAttachmentOverridesForObject(*at_it);
+ LLViewerObject *vo = *at_it;
+ // Attached animated objects affect joints in their control
+ // avs, not the avs to which they are attached.
+ if (!vo->isAnimatedObject())
+ {
+ addAttachmentOverridesForObject(vo);
+ }
}
}
}
}
+
//-----------------------------------------------------------------------------
-// addAttachmentPosOverridesForObject
-//-----------------------------------------------------------------------------
-void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)
+// updateAttachmentOverrides
+//
+// This is intended to give the same results as
+// rebuildAttachmentOverrides(), while avoiding redundant work.
+// -----------------------------------------------------------------------------
+void LLVOAvatar::updateAttachmentOverrides()
{
- LLVOAvatar *av = vo->getAvatarAncestor();
- if (!av || (av != this))
+ LLScopedContextString str("updateAttachmentOverrides " + getFullname());
+
+ LL_DEBUGS("AnimatedObjects") << "updating" << LL_ENDL;
+ dumpStack("AnimatedObjectsStack");
+
+ std::set<LLUUID> meshes_seen;
+
+ // Handle the case that we're updating 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, &meshes_seen);
+ }
+ }
+
+ // Attached objects
+ for (attachment_map_t::iterator iter = mAttachmentPoints.begin();
+ iter != mAttachmentPoints.end();
+ ++iter)
{
+ LLViewerJointAttachment *attachment_pt = (*iter).second;
+ if (attachment_pt)
+ {
+ for (LLViewerJointAttachment::attachedobjs_vec_t::iterator at_it = attachment_pt->mAttachedObjects.begin();
+ at_it != attachment_pt->mAttachedObjects.end(); ++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, &meshes_seen);
+ }
+ }
+ }
+ }
+ // Remove meshes that are no longer present on the skeleton
+
+ // have to work with a copy because removeAttachmentOverrides() will change mActiveOverrideMeshes.
+ std::set<LLUUID> active_override_meshes = mActiveOverrideMeshes;
+ for (std::set<LLUUID>::iterator it = active_override_meshes.begin(); it != active_override_meshes.end(); ++it)
+ {
+ if (meshes_seen.find(*it) == meshes_seen.end())
+ {
+ removeAttachmentOverridesForObject(*it);
+ }
+ }
+
+
+#ifdef ATTACHMENT_OVERRIDE_VALIDATION
+ {
+ std::vector<LLVector3OverrideMap> pos_overrides_by_joint;
+ std::vector<LLVector3OverrideMap> scale_overrides_by_joint;
+ LLVector3OverrideMap pelvis_fixups;
+
+ // Capture snapshot of override state after update
+ for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++)
+ {
+ LLVector3OverrideMap pos_overrides;
+ LLJoint *joint = getJoint(joint_num);
+ if (joint)
+ {
+ pos_overrides_by_joint.push_back(joint->m_attachmentPosOverrides);
+ scale_overrides_by_joint.push_back(joint->m_attachmentScaleOverrides);
+ }
+ else
+ {
+ // No joint, use default constructed empty maps
+ pos_overrides_by_joint.push_back(LLVector3OverrideMap());
+ scale_overrides_by_joint.push_back(LLVector3OverrideMap());
+ }
+ }
+ pelvis_fixups = mPelvisFixups;
+ //dumpArchetypeXML(getFullname() + "_paranoid_updated");
+
+ // Rebuild and compare
+ rebuildAttachmentOverrides();
+ //dumpArchetypeXML(getFullname() + "_paranoid_rebuilt");
+ bool mismatched = false;
+ for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++)
+ {
+ LLJoint *joint = getJoint(joint_num);
+ if (joint)
+ {
+ if (pos_overrides_by_joint[joint_num] != joint->m_attachmentPosOverrides)
+ {
+ mismatched = true;
+ }
+ if (scale_overrides_by_joint[joint_num] != joint->m_attachmentScaleOverrides)
+ {
+ mismatched = true;
+ }
+ }
+ }
+ if (pelvis_fixups != mPelvisFixups)
+ {
+ mismatched = true;
+ }
+ if (mismatched)
+ {
+ LL_WARNS() << "MISMATCHED ATTACHMENT OVERRIDES" << LL_ENDL;
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// addAttachmentOverridesForObject
+//-----------------------------------------------------------------------------
+void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo, std::set<LLUUID>* meshes_seen, bool recursive)
+{
+ 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 " + getFullname());
+
+ LL_DEBUGS("AnimatedObjects") << "adding" << LL_ENDL;
+ dumpStack("AnimatedObjectsStack");
// Process all children
- LLViewerObject::const_child_list_t& children = vo->getChildren();
- for (LLViewerObject::const_child_list_t::const_iterator it = children.begin();
- it != children.end(); ++it)
- {
- LLViewerObject *childp = *it;
- addAttachmentOverridesForObject(childp);
- }
+ if (recursive)
+ {
+ LLViewerObject::const_child_list_t& children = vo->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator it = children.begin();
+ it != children.end(); ++it)
+ {
+ LLViewerObject *childp = *it;
+ addAttachmentOverridesForObject(childp, meshes_seen, true);
+ }
+ }
LLVOVolume *vobj = dynamic_cast<LLVOVolume*>(vo);
bool pelvisGotSet = false;
@@ -5625,15 +6155,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();
@@ -5645,8 +6178,26 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)
{
const F32 pelvisZOffset = pSkinData->mPelvisOffset;
const LLUUID& mesh_id = pSkinData->mMeshID;
+
+ if (meshes_seen)
+ {
+ meshes_seen->insert(mesh_id);
+ }
+ bool mesh_overrides_loaded = (mActiveOverrideMeshes.find(mesh_id) != mActiveOverrideMeshes.end());
+ if (mesh_overrides_loaded)
+ {
+ LL_DEBUGS("AnimatedObjects") << "skipping add attachment overrides for " << mesh_id
+ << " to root object " << root_object->getID()
+ << ", already loaded"
+ << LL_ENDL;
+ }
+ else
+ {
+ 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 )
+ if ( fullRig && !mesh_overrides_loaded )
{
for ( int i=0; i<jointCnt; ++i )
{
@@ -5690,9 +6241,15 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo)
}
}
+ mActiveOverrideMeshes.insert(mesh_id);
+ onActiveOverrideMeshesChanged();
}
}
}
+ 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 )
@@ -5815,14 +6372,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
@@ -5831,37 +6388,32 @@ 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();
- avatar_joint_list_t::iterator end = mSkeleton.end();
-
LLJoint* pJointPelvis = getJoint("mPelvis");
-
- for (; iter != end; ++iter)
- {
- LLJoint* pJoint = (*iter);
- //Reset joints except for pelvis
+ const std::string av_string = avString();
+ for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++)
+ {
+ LLJoint *pJoint = getJoint(joint_num);
if ( pJoint )
{
bool dummy; // unused
- pJoint->removeAttachmentPosOverride(mesh_id, avString(),dummy);
- pJoint->removeAttachmentScaleOverride(mesh_id, avString());
+ pJoint->removeAttachmentPosOverride(mesh_id, av_string, dummy);
+ pJoint->removeAttachmentScaleOverride(mesh_id, av_string);
}
if ( pJoint && pJoint == pJointPelvis)
{
@@ -5872,6 +6424,9 @@ void LLVOAvatar::resetJointsOnDetach(const LLUUID& mesh_id)
}
postPelvisSetRecalc();
+
+ mActiveOverrideMeshes.erase(mesh_id);
+ onActiveOverrideMeshesChanged();
}
//-----------------------------------------------------------------------------
// getCharacterPosition()
@@ -5923,7 +6478,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;
@@ -5952,7 +6507,7 @@ F32 LLVOAvatar::getTimeDilation()
//-----------------------------------------------------------------------------
F32 LLVOAvatar::getPixelArea() const
{
- if (mIsDummy)
+ if (isUIAvatar())
{
return 100000.f;
}
@@ -6386,7 +6941,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.
@@ -6449,6 +7004,11 @@ const LLViewerJointAttachment *LLVOAvatar::attachObject(LLViewerObject *viewer_o
return 0;
}
+ if (!viewer_object->isAnimatedObject())
+ {
+ updateAttachmentOverrides();
+ }
+
updateVisualComplexity();
if (viewer_object->isSelected())
@@ -6478,19 +7038,62 @@ 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;
+ 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();
}
//-----------------------------------------------------------------------------
@@ -6581,7 +7184,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();
@@ -6606,9 +7209,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)
+ {
+ updateAttachmentOverrides();
+ }
LL_DEBUGS() << "Detaching object " << viewer_object->mID << " from " << attachment->getName() << LL_ENDL;
return TRUE;
}
@@ -6742,7 +7349,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())
{
@@ -6900,6 +7510,18 @@ void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color)
updateMeshTextures();
}
+// virtual
+bool LLVOAvatar::shouldRenderRigged() const
+{
+ return true;
+}
+
+// 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()
@@ -6910,6 +7532,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)
@@ -7189,9 +7816,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;
@@ -8696,8 +9323,43 @@ void LLVOAvatar::dumpArchetypeXML(const std::string& prefix, bool group_by_weara
pelvis_fixup, mesh_id.asString().c_str());
}
- apr_file_printf( file, "\t</archetype>\n" );
- apr_file_printf( file, "\n</linden_genepool>\n" );
+ LLVector3 rp = getRootJoint()->getWorldPosition();
+ LLVector4a rpv;
+ rpv.load3(rp.mV);
+
+ for (S32 joint_num = 0; joint_num < LL_CHARACTER_MAX_ANIMATED_JOINTS; joint_num++)
+ {
+ LLJoint *joint = getJoint(joint_num);
+ if (joint_num < mJointRiggingInfoTab.size())
+ {
+ LLJointRiggingInfo& rig_info = mJointRiggingInfoTab[joint_num];
+ if (rig_info.isRiggedTo())
+ {
+ LLMatrix4a mat;
+ LLVector4a new_extents[2];
+ mat.loadu(joint->getWorldMatrix());
+ matMulBoundBox(mat, rig_info.getRiggedExtents(), new_extents);
+ LLVector4a rrp[2];
+ rrp[0].setSub(new_extents[0],rpv);
+ rrp[1].setSub(new_extents[1],rpv);
+ apr_file_printf( file, "\t\t<joint_rig_info num=\"%d\" name=\"%s\" min=\"%f %f %f\" max=\"%f %f %f\" tmin=\"%f %f %f\" tmax=\"%f %f %f\"/>\n",
+ joint_num,
+ joint->getName().c_str(),
+ rig_info.getRiggedExtents()[0][0],
+ rig_info.getRiggedExtents()[0][1],
+ rig_info.getRiggedExtents()[0][2],
+ rig_info.getRiggedExtents()[1][0],
+ rig_info.getRiggedExtents()[1][1],
+ rig_info.getRiggedExtents()[1][2],
+ rrp[0][0],
+ rrp[0][1],
+ rrp[0][2],
+ rrp[1][0],
+ rrp[1][1],
+ rrp[1][2] );
+ }
+ }
+ }
bool ultra_verbose = false;
if (isSelf() && ultra_verbose)
@@ -8705,6 +9367,10 @@ void LLVOAvatar::dumpArchetypeXML(const std::string& prefix, bool group_by_weara
// show the cloned params inside the wearables as well.
gAgentAvatarp->dumpWearableInfo(outfile);
}
+
+ apr_file_printf( file, "\t</archetype>\n" );
+ apr_file_printf( file, "\n</linden_genepool>\n" );
+
LLSD args;
args["PATH"] = fullpath;
LLNotificationsUtil::add("AppearanceToXMLSaved", args);
@@ -8836,6 +9502,7 @@ void LLVOAvatar::updateRegion(LLViewerRegion *regionp)
LLViewerObject::updateRegion(regionp);
}
+// virtual
std::string LLVOAvatar::getFullname() const
{
std::string name;
@@ -8882,6 +9549,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;
@@ -8907,11 +9579,156 @@ BOOL LLVOAvatar::updateLOD()
return res;
}
-void LLVOAvatar::updateLODRiggedAttachments( void )
+void LLVOAvatar::updateLODRiggedAttachments()
{
updateLOD();
rebuildRiggedAttachments();
}
+
+void showRigInfoTabExtents(LLVOAvatar *avatar, LLJointRiggingInfoTab& tab, S32& count_rigged, S32& count_box)
+{
+ count_rigged = count_box = 0;
+ LLVector4a zero_vec;
+ zero_vec.clear();
+ for (S32 i=0; i<tab.size(); i++)
+ {
+ if (tab[i].isRiggedTo())
+ {
+ count_rigged++;
+ LLJoint *joint = avatar->getJoint(i);
+ LL_DEBUGS("RigSpam") << "joint " << i << " name " << joint->getName() << " box "
+ << tab[i].getRiggedExtents()[0] << ", " << tab[i].getRiggedExtents()[1] << LL_ENDL;
+ if ((!tab[i].getRiggedExtents()[0].equals3(zero_vec)) ||
+ (!tab[i].getRiggedExtents()[1].equals3(zero_vec)))
+ {
+ count_box++;
+ }
+ }
+ }
+}
+
+void LLVOAvatar::getAssociatedVolumes(std::vector<LLVOVolume*>& volumes)
+{
+ for ( LLVOAvatar::attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter )
+ {
+ LLViewerJointAttachment* attachment = iter->second;
+ LLViewerJointAttachment::attachedobjs_vec_t::iterator attach_end = attachment->mAttachedObjects.end();
+
+ for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attach_iter = attachment->mAttachedObjects.begin();
+ attach_iter != attach_end; ++attach_iter)
+ {
+ LLViewerObject* attached_object = *attach_iter;
+ LLVOVolume *volume = dynamic_cast<LLVOVolume*>(attached_object);
+ if (volume)
+ {
+ volumes.push_back(volume);
+ if (volume->isAnimatedObject())
+ {
+ // For animated object attachment, don't need
+ // the children. Will just get bounding box
+ // from the control avatar.
+ continue;
+ }
+ }
+ LLViewerObject::const_child_list_t& children = attached_object->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator it = children.begin();
+ it != children.end(); ++it)
+ {
+ LLViewerObject *childp = *it;
+ LLVOVolume *volume = dynamic_cast<LLVOVolume*>(childp);
+ if (volume)
+ {
+ volumes.push_back(volume);
+ }
+ }
+ }
+ }
+
+ LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this);
+ if (control_av)
+ {
+ LLVOVolume *volp = control_av->mRootVolp;
+ if (volp)
+ {
+ volumes.push_back(volp);
+ LLViewerObject::const_child_list_t& children = volp->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator it = children.begin();
+ it != children.end(); ++it)
+ {
+ LLViewerObject *childp = *it;
+ LLVOVolume *volume = dynamic_cast<LLVOVolume*>(childp);
+ if (volume)
+ {
+ volumes.push_back(volume);
+ }
+ }
+ }
+ }
+}
+
+static LLTrace::BlockTimerStatHandle FTM_AVATAR_RIGGING_INFO_UPDATE("Av Upd Rig Info");
+static LLTrace::BlockTimerStatHandle FTM_AVATAR_RIGGING_KEY_UPDATE("Av Upd Rig Key");
+static LLTrace::BlockTimerStatHandle FTM_AVATAR_RIGGING_AVOL_UPDATE("Av Upd Avol");
+
+// virtual
+void LLVOAvatar::updateRiggingInfo()
+{
+ LL_RECORD_BLOCK_TIME(FTM_AVATAR_RIGGING_INFO_UPDATE);
+
+ LL_DEBUGS("RigSpammish") << getFullname() << " updating rig tab" << LL_ENDL;
+
+ std::vector<LLVOVolume*> volumes;
+
+ {
+ LL_RECORD_BLOCK_TIME(FTM_AVATAR_RIGGING_AVOL_UPDATE);
+ getAssociatedVolumes(volumes);
+ }
+
+ std::map<LLUUID,S32> curr_rigging_info_key;
+ {
+ LL_RECORD_BLOCK_TIME(FTM_AVATAR_RIGGING_KEY_UPDATE);
+ // Get current rigging info key
+ for (std::vector<LLVOVolume*>::iterator it = volumes.begin(); it != volumes.end(); ++it)
+ {
+ LLVOVolume *vol = *it;
+ if (vol->isMesh() && vol->getVolume())
+ {
+ const LLUUID& mesh_id = vol->getVolume()->getParams().getSculptID();
+ S32 max_lod = llmax(vol->getLOD(), vol->mLastRiggingInfoLOD);
+ curr_rigging_info_key[mesh_id] = max_lod;
+ }
+ }
+
+ // Check for key change, which indicates some change in volume composition or LOD.
+ if (curr_rigging_info_key == mLastRiggingInfoKey)
+ {
+ return;
+ }
+ }
+
+ // Something changed. Update.
+ mLastRiggingInfoKey = curr_rigging_info_key;
+ mJointRiggingInfoTab.clear();
+ for (std::vector<LLVOVolume*>::iterator it = volumes.begin(); it != volumes.end(); ++it)
+ {
+ LLVOVolume *vol = *it;
+ vol->updateRiggingInfo();
+ mJointRiggingInfoTab.merge(vol->mJointRiggingInfoTab);
+ }
+
+ //LL_INFOS() << "done update rig count is " << countRigInfoTab(mJointRiggingInfoTab) << LL_ENDL;
+ LL_DEBUGS("RigSpammish") << getFullname() << " after update rig tab:" << LL_ENDL;
+ S32 joint_count, box_count;
+ showRigInfoTabExtents(this, mJointRiggingInfoTab, joint_count, box_count);
+ LL_DEBUGS("RigSpammish") << "uses " << joint_count << " joints " << " nonzero boxes: " << box_count << LL_ENDL;
+}
+
+// virtual
+void LLVOAvatar::onActiveOverrideMeshesChanged()
+{
+ mJointRiggingInfoTab.setNeedsUpdate(true);
+}
+
U32 LLVOAvatar::getPartitionType() const
{
// Avatars merely exist as drawables in the bridge partition
@@ -8922,10 +9739,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()
@@ -8941,6 +9758,7 @@ void LLVOAvatar::updateImpostors()
LLCharacter::sAllowInstancesChange = TRUE;
}
+// virtual
BOOL LLVOAvatar::isImpostor()
{
return sUseImpostors && (isVisuallyMuted() || (mUpdatePeriod >= IMPOSTOR_PERIOD)) ? TRUE : FALSE;
@@ -9022,6 +9840,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
@@ -9069,10 +9898,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);
@@ -9091,22 +9926,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;
@@ -9114,6 +9940,135 @@ 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;
+ const F32 animated_object_attachment_surcharge = 1000;
+
+ 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()
{
@@ -9152,7 +10107,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)
@@ -9163,112 +10136,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..deb22617a4 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -49,7 +49,10 @@
#include "lldriverparam.h"
#include "llviewertexlayer.h"
#include "material_codes.h" // LL_MCODE_END
+#include "llrigginginfo.h"
#include "llviewerstats.h"
+#include "llvovolume.h"
+#include "llavatarrendernotifier.h"
extern const LLUUID ANIM_AGENT_BODY_NOISE;
extern const LLUUID ANIM_AGENT_BREATHE_ROT;
@@ -159,7 +162,7 @@ public:
/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
/*virtual*/ void updateRegion(LLViewerRegion *regionp);
/*virtual*/ void updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax);
- /*virtual*/ void getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
+ void calculateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
/*virtual*/ BOOL lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
S32 face = -1, // which face to check, -1 = ALL_SIDES
BOOL pick_transparent = FALSE,
@@ -169,7 +172,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,
@@ -200,18 +204,28 @@ public:
virtual LLJoint* getJoint(const std::string &name);
LLJoint* getJoint(S32 num);
-
- void addAttachmentOverridesForObject(LLViewerObject *vo);
- void resetJointsOnDetach(const LLUUID& mesh_id);
- void resetJointsOnDetach(LLViewerObject *vo);
- bool jointIsRiggedTo(const std::string& joint_name);
- bool jointIsRiggedTo(const std::string& joint_name, const LLViewerObject *vo);
+
+ void addAttachmentOverridesForObject(LLViewerObject *vo, std::set<LLUUID>* meshes_seen = NULL, bool recursive = true);
+ void removeAttachmentOverridesForObject(const LLUUID& mesh_id);
+ void removeAttachmentOverridesForObject(LLViewerObject *vo);
+ bool jointIsRiggedTo(const LLJoint *joint) const;
void clearAttachmentOverrides();
void rebuildAttachmentOverrides();
+ void updateAttachmentOverrides();
void showAttachmentOverrides(bool verbose = false) const;
void getAttachmentOverrideNames(std::set<std::string>& pos_names,
std::set<std::string>& scale_names) const;
+
+ void getAssociatedVolumes(std::vector<LLVOVolume*>& volumes);
+
+ // virtual
+ void updateRiggingInfo();
+ // This encodes mesh id and LOD, so we can see whether display is up-to-date.
+ std::map<LLUUID,S32> mLastRiggingInfoKey;
+ std::set<LLUUID> mActiveOverrideMeshes;
+ virtual void onActiveOverrideMeshesChanged();
+
/*virtual*/ const LLUUID& getID() const;
/*virtual*/ void addDebugText(const std::string& text);
/*virtual*/ F32 getTimeDilation();
@@ -233,6 +247,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 +257,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 +284,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 +450,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 +471,14 @@ public:
VisualMuteSettings mVisuallyMuteSetting; // Always or never visually mute this AV
//--------------------------------------------------------------------
+ // animated object status
+ //--------------------------------------------------------------------
+public:
+ bool mIsControlAvatar;
+ bool mIsUIAvatar;
+ bool mEnableDefaultMotions;
+
+ //--------------------------------------------------------------------
// Morph masks
//--------------------------------------------------------------------
public:
@@ -478,7 +516,7 @@ private:
// Impostors
//--------------------------------------------------------------------
public:
- BOOL isImpostor();
+ virtual BOOL isImpostor();
BOOL shouldImpostor(const U32 rank_factor = 1) const;
BOOL needsImpostorUpdate() const;
const LLVector3& getImpostorOffset() const;
@@ -490,14 +528,22 @@ public:
static void updateImpostors();
LLRenderTarget mImpostor;
BOOL mNeedsImpostorUpdate;
+ F32SecondsImplicit mLastImpostorUpdateFrameTime;
+ const LLVector3* getLastAnimExtents() const { return mLastAnimExtents; }
+ void setNeedsExtentUpdate(bool val) { mNeedsExtentUpdate = val; }
+
private:
LLVector3 mImpostorOffset;
LLVector2 mImpostorDim;
+ // This becomes true in the constructor and false after the first
+ // idleUpdateMisc(). Not clear it serves any purpose.
BOOL mNeedsAnimUpdate;
+ bool mNeedsExtentUpdate;
LLVector3 mImpostorAngle;
F32 mImpostorDistance;
F32 mImpostorPixelArea;
LLVector3 mLastAnimExtents[2];
+ LLVector3 mLastAnimBasePos;
LLCachedControl<bool> mRenderUnloadedAvatar;
@@ -716,7 +762,9 @@ private:
//--------------------------------------------------------------------
public:
BOOL isVisible() const;
+ virtual bool shouldRenderRigged() const;
void setVisibilityRank(U32 rank);
+ U32 getVisibilityRank() const { return mVisibilityRank; }
static S32 sNumVisibleAvatars; // Number of instances of this class
/** Appearance
** **
@@ -739,9 +787,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 +809,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 +947,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 b2954f4de2..dcaade55a6 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -235,6 +235,8 @@ void LLVOAvatarSelf::initInstance()
//doPeriodically(output_self_av_texture_diagnostics, 30.0);
doPeriodically(update_avatar_rez_metrics, 5.0);
doPeriodically(boost::bind(&LLVOAvatarSelf::checkStuckAppearance, this), 30.0);
+
+ mInitFlags |= 1<<2;
}
void LLVOAvatarSelf::setHoverIfRegionEnabled()
@@ -2699,6 +2701,12 @@ void LLVOAvatarSelf::onCustomizeEnd(bool disable_camera_switch)
}
}
+// virtual
+bool LLVOAvatarSelf::shouldRenderRigged() const
+{
+ return gAgent.needsRenderAvatar();
+}
+
// HACK: this will null out the avatar's local texture IDs before the TE message is sent
// to ensure local texture IDs are not sent to other clients in the area.
// this is a short-term solution. The long term solution will be to not set the texture
@@ -2793,7 +2801,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/llvoavatarself.h b/indra/newview/llvoavatarself.h
index f9f90bb323..b0fdae9bf0 100644
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -312,6 +312,9 @@ public:
//--------------------------------------------------------------------
// Visibility
//--------------------------------------------------------------------
+
+ /* virtual */ bool shouldRenderRigged() const;
+
public:
bool sendAppearanceMessage(LLMessageSystem *mesgsys) const;
diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp
index b5c90a8f60..d651d540b9 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 f046a903ed..0b1f1df4c8 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"
#include "llsculptidsize.h"
const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;
@@ -213,6 +219,9 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
mFaceMappingChanged = FALSE;
mLOD = MIN_LOD;
+ mLODDistance = 0.0f;
+ mLODAdjustedDistance = 0.0f;
+ mLODRadius = 0.0f;
mTextureAnimp = NULL;
mVolumeChanged = FALSE;
mVObjRadius = LLVector3(1,1,0.5f).length();
@@ -225,6 +234,7 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
mLastFetchedMediaVersion = -1;
memset(&mIndexInTex, 0, sizeof(S32) * LLRender::NUM_VOLUME_TEXTURE_CHANNELS);
mMDCImplCount = 0;
+ mLastRiggingInfoLOD = -1;
}
LLVOVolume::~LLVOVolume()
@@ -310,6 +320,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);
@@ -323,6 +334,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)
@@ -1106,16 +1120,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
@@ -1240,6 +1272,46 @@ S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius, F32 lod_factor)
return cur_detail;
}
+std::string get_debug_object_lod_text(LLVOVolume *rootp)
+{
+ std::string cam_dist_string = "";
+ cam_dist_string += LLStringOps::getReadableNumber(rootp->mLODDistance) + " ";
+ std::string lod_string = llformat("%d",rootp->getLOD());
+ F32 lod_radius = rootp->mLODRadius;
+ S32 cam_dist_count = 0;
+ LLViewerObject::const_child_list_t& child_list = rootp->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin();
+ iter != child_list.end(); ++iter)
+ {
+ LLViewerObject *childp = *iter;
+ LLVOVolume *volp = dynamic_cast<LLVOVolume*>(childp);
+ if (volp)
+ {
+ lod_string += llformat("%d",volp->getLOD());
+ if (volp->isRiggedMesh())
+ {
+ // Rigged/animatable mesh. This is computed from the
+ // avatar dynamic box, so value from any vol will be
+ // the same.
+ lod_radius = volp->mLODRadius;
+ }
+ if (volp->mDrawable)
+ {
+ if (cam_dist_count < 4)
+ {
+ cam_dist_string += LLStringOps::getReadableNumber(volp->mLODDistance) + " ";
+ cam_dist_count++;
+ }
+ }
+ }
+ }
+ std::string result = llformat("lod_radius %s dists %s lods %s",
+ LLStringOps::getReadableNumber(lod_radius).c_str(),
+ cam_dist_string.c_str(),
+ lod_string.c_str());
+ return result;
+}
+
BOOL LLVOVolume::calcLOD()
{
if (mDrawable.isNull())
@@ -1264,18 +1336,60 @@ BOOL LLVOVolume::calcLOD()
}
distance = avatar->mDrawable->mDistanceWRTCamera;
- radius = avatar->getBinRadius();
+
+
+ if (avatar->isControlAvatar())
+ {
+ // MAINT-7926 Handle volumes in an animated object as a special case
+ const LLVector3* box = avatar->getLastAnimExtents();
+ LLVector3 diag = box[1] - box[0];
+ radius = diag.magVec() * 0.5f;
+ LL_DEBUGS("DynamicBox") << avatar->getFullname() << " diag " << diag << " radius " << radius << LL_ENDL;
+ }
+ else
+ {
+ // Volume in a rigged mesh attached to a regular avatar.
+ // Note this isn't really a radius, so distance calcs are off by factor of 2
+ //radius = avatar->getBinRadius();
+ // SL-937: add dynamic box handling for rigged mesh on regular avatars.
+ const LLVector3* box = avatar->getLastAnimExtents();
+ LLVector3 diag = box[1] - box[0];
+ radius = diag.magVec(); // preserve old BinRadius behavior - 2x off
+ LL_DEBUGS("DynamicBox") << avatar->getFullname() << " diag " << diag << " radius " << radius << LL_ENDL;
+ }
+ if (distance <= 0.f || radius <= 0.f)
+ {
+ LL_DEBUGS("DynamicBox","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("DynamicBox","CalcLOD") << "non-avatar distance/radius uninitialized, skipping" << LL_ENDL;
+ return FALSE;
+ }
}
//hold onto unmodified distance for debugging
//F32 debug_distance = distance;
-
- distance *= sDistanceFactor;
+
+ mLODDistance = distance;
+ mLODRadius = radius;
+
+ if (gSavedSettings.getBOOL("DebugObjectLODs"))
+ {
+ if (getAvatar() && isRootEdit())
+ {
+ std::string debug_object_text = get_debug_object_lod_text(this);
+ setDebugText(debug_object_text);
+ }
+ }
+
+ distance *= sDistanceFactor;
F32 rampDist = LLVOVolume::sLODFactor * 2;
@@ -1296,24 +1410,47 @@ BOOL LLVOVolume::calcLOD()
lod_factor *= DEFAULT_FIELD_OF_VIEW / LLViewerCamera::getInstance()->getDefaultFOV();
}
- cur_detail = computeLODDetail(ll_round(distance, 0.01f),
- ll_round(radius, 0.01f),
- lod_factor);
+ mLODAdjustedDistance = distance;
+ if (isHUDAttachment())
+ {
+ // HUDs always show at highest detail
+ cur_detail = 3;
+ }
+ else
+ {
+ cur_detail = computeLODDetail(ll_round(distance, 0.01f), ll_round(radius, 0.01f), lod_factor);
+ }
+
+ 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("DynamicBox","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;
+ mLOD = cur_detail;
- return TRUE;
+ return TRUE;
}
return FALSE;
@@ -1339,6 +1476,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;
}
@@ -1413,7 +1560,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)
@@ -1421,6 +1569,7 @@ BOOL LLVOVolume::setParent(LLViewerObject* parent)
gPipeline.markMoved(mDrawable);
gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
}
+ onReparent(old_parent, parent);
}
return ret ;
@@ -1485,14 +1634,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++)
@@ -1502,15 +1666,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
{
@@ -1519,17 +1696,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;
}
@@ -1677,6 +1865,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;
@@ -3317,6 +3510,208 @@ 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()->updateAttachmentOverrides();
+ }
+ else
+ {
+ // Making an animated object into a rigged mesh
+ getAvatarAncestor()->updateAttachmentOverrides();
+ }
+ }
+}
+
+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()->updateAttachmentOverrides();
+ 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;
+ }
+}
+
+//----------------------------------------------------------------------------
+static LLTrace::BlockTimerStatHandle FTM_VOVOL_RIGGING_INFO("VOVol Rigging Info");
+
+void LLVOVolume::updateRiggingInfo()
+{
+ LL_RECORD_BLOCK_TIME(FTM_VOVOL_RIGGING_INFO);
+ if (isRiggedMesh())
+ {
+ const LLMeshSkinInfo* skin = getSkinInfo();
+ LLVOAvatar *avatar = getAvatar();
+ LLVolume *volume = getVolume();
+ if (skin && avatar && volume)
+ {
+ LL_DEBUGS("RigSpammish") << "starting, vovol " << this << " lod " << getLOD() << " last " << mLastRiggingInfoLOD << LL_ENDL;
+ if (getLOD()>mLastRiggingInfoLOD || getLOD()==3)
+ {
+ // Rigging info may need update
+ mJointRiggingInfoTab.clear();
+ for (S32 f = 0; f < volume->getNumVolumeFaces(); ++f)
+ {
+ LLVolumeFace& vol_face = volume->getVolumeFace(f);
+ LLSkinningUtil::updateRiggingInfo(skin, avatar, vol_face);
+ if (vol_face.mJointRiggingInfoTab.size()>0)
+ {
+ mJointRiggingInfoTab.merge(vol_face.mJointRiggingInfoTab);
+ }
+ }
+ // Keep the highest LOD info available.
+ mLastRiggingInfoLOD = getLOD();
+ LL_DEBUGS("RigSpammish") << "updated rigging info for LLVOVolume "
+ << this << " lod " << mLastRiggingInfoLOD
+ << LL_ENDL;
+ }
+ }
+ }
+}
+
//----------------------------------------------------------------------------
void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point)
@@ -3378,7 +3773,7 @@ void LLVOVolume::updateRadius()
BOOL LLVOVolume::isAttachment() const
{
- return mState != 0 ;
+ return mAttachmentState != 0 ;
}
BOOL LLVOVolume::isHUDAttachment() const
@@ -3386,7 +3781,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 );
}
@@ -3466,16 +3861,25 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const
path_params = volume_params.getPathParams();
profile_params = volume_params.getProfileParams();
- F32 weighted_triangles = -1.0;
- getStreamingCost(NULL, NULL, &weighted_triangles);
-
- if (weighted_triangles > 0.0)
+ LLMeshCostData costs;
+ if (getCostData(costs))
{
- num_triangles = (U32)(weighted_triangles);
+ if (isAnimatedObject() && isRiggedMesh())
+ {
+ // Scaling here is to make animated object vs
+ // non-animated object ARC proportional to the
+ // corresponding calculations for streaming cost.
+ num_triangles = (ANIMATED_OBJECT_COST_PER_KTRI * 0.001 * costs.getEstTrisForStreamingCost())/0.06;
+ }
+ else
+ {
+ F32 radius = getScale().length()*0.5f;
+ num_triangles = costs.getRadiusWeightedTris(radius);
+ }
}
}
- if (num_triangles == 0)
+ if (num_triangles <= 0)
{
num_triangles = 4;
}
@@ -3489,12 +3893,11 @@ 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;
}
-
}
else
{
@@ -3663,6 +4066,14 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const
shame += media_faces * ARC_MEDIA_FACE_COST;
}
+ // Streaming cost for animated objects includes a fixed cost
+ // per linkset. Add a corresponding charge here translated into
+ // triangles, but not weighted by any graphics properties.
+ if (isAnimatedObject() && isRootEdit())
+ {
+ shame += (ANIMATED_OBJECT_BASE_COST/0.06) * 5.0f;
+ }
+
if (shame > mRenderComplexity_current)
{
mRenderComplexity_current = (S32)shame;
@@ -3671,16 +4082,68 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const
return (U32)shame;
}
-F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const
+F32 LLVOVolume::getEstTrianglesMax() const
{
- F32 radius = getScale().length()*0.5f;
-
- if (isMesh())
+ if (isMesh() && getVolume())
{
- return gMeshRepo.getStreamingCost(getVolume()->getParams().getSculptID(), radius, bytes, visible_bytes, mLOD, unscaled_value);
+ return gMeshRepo.getEstTrianglesMax(getVolume()->getParams().getSculptID());
}
- else
+ return 0.f;
+}
+
+F32 LLVOVolume::getEstTrianglesStreamingCost() const
+{
+ if (isMesh() && getVolume())
{
+ return gMeshRepo.getEstTrianglesStreamingCost(getVolume()->getParams().getSculptID());
+ }
+ return 0.f;
+}
+
+F32 LLVOVolume::getStreamingCost() const
+{
+ F32 radius = getScale().length()*0.5f;
+ F32 linkset_base_cost = 0.f;
+
+ LLMeshCostData costs;
+ if (getCostData(costs))
+ {
+ if (isAnimatedObject() && isRootEdit())
+ {
+ // Root object of an animated object has this to account for skeleton overhead.
+ linkset_base_cost = ANIMATED_OBJECT_BASE_COST;
+ }
+ if (isMesh())
+ {
+ if (isAnimatedObject() && isRiggedMesh())
+ {
+ return linkset_base_cost + costs.getTriangleBasedStreamingCost();
+ }
+ else
+ {
+ return linkset_base_cost + costs.getRadiusBasedStreamingCost(radius);
+ }
+ }
+ else
+ {
+ return linkset_base_cost + costs.getRadiusBasedStreamingCost(radius);
+ }
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+// virtual
+bool LLVOVolume::getCostData(LLMeshCostData& costs) const
+{
+ if (isMesh())
+ {
+ return gMeshRepo.getCostData(getVolume()->getParams().getSculptID(), costs);
+ }
+ else
+ {
LLVolume* volume = getVolume();
S32 counts[4];
LLVolume::getLoDTriangleCounts(volume->getParams(), counts);
@@ -3691,8 +4154,8 @@ F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_v
header["medium_lod"]["size"] = counts[2] * 10;
header["high_lod"]["size"] = counts[3] * 10;
- return LLMeshRepository::getStreamingCost(header, radius, NULL, NULL, -1, unscaled_value);
- }
+ return gMeshRepo.getCostData(header, costs);
+ }
}
//static
@@ -3762,6 +4225,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();
@@ -3775,10 +4253,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)
@@ -3888,6 +4373,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();
@@ -4140,9 +4641,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()
@@ -4172,9 +4673,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();
@@ -4182,7 +4681,6 @@ void LLVOVolume::updateRiggedVolume(bool force_update)
}
LLVOAvatar* avatar = getAvatar();
-
if (!avatar)
{
clearRiggedVolume();
@@ -4197,7 +4695,6 @@ void LLVOVolume::updateRiggedVolume(bool force_update)
}
mRiggedVolume->update(skin, avatar, volume);
-
}
static LLTrace::BlockTimerStatHandle FTM_SKIN_RIGGED("Skin");
@@ -4227,6 +4724,19 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons
{
copyVolumeFaces(volume);
}
+ else
+ {
+ bool is_paused = avatar && avatar->areAnimationsPaused();
+ if (is_paused)
+ {
+ S32 frames_paused = LLFrameTimer::getFrameCount() - avatar->getMotionController().getPausedFrame();
+ if (frames_paused > 2)
+ {
+ return;
+ }
+ }
+ }
+
//build matrix palette
static const size_t kMaxJoints = LL_MAX_JOINTS_PER_MESH_OBJECT;
@@ -4235,6 +4745,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);
@@ -4256,6 +4769,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;
@@ -4270,11 +4785,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)
{
@@ -4282,6 +4803,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);
@@ -4300,6 +4824,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
@@ -4788,27 +5316,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;
@@ -4851,7 +5371,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();
@@ -4866,12 +5387,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()))
{
@@ -4885,29 +5408,46 @@ 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();
+
+ vobj->updateControlAvatar();
+
+ 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);
-
- bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic();
-
- bool is_rigged = false;
-
- if (rigged && pAvatarVO)
+ if (vobj->isRiggedMesh() &&
+ ((vobj->isAnimatedObject() && vobj->getControlAvatar()) ||
+ (!vobj->isAnimatedObject() && vobj->getAvatar())))
{
- pAvatarVO->addAttachmentOverridesForObject(vobj);
- if (!LLApp::isExiting() && pAvatarVO->isSelf() && debugLoggingEnabled("AvatarAttachments"))
- {
- bool verbose = true;
- pAvatarVO->showAttachmentOverrides(verbose);
- }
+ vobj->getAvatar()->addAttachmentOverridesForObject(vobj, NULL, false);
}
+
+ // 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);
+
+ bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic();
+ bool any_rigged_face = false;
//for each face
for (S32 i = 0; i < drawablep->getNumFaces(); i++)
@@ -4925,7 +5465,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
@@ -4933,7 +5473,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);
@@ -5277,7 +5817,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
}
}
- if (is_rigged)
+ if (any_rigged_face)
{
if (!drawablep->isState(LLDrawable::RIGGED))
{
@@ -5291,6 +5831,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
else
{
drawablep->clearState(LLDrawable::RIGGED);
+ vobj->updateRiggedVolume();
}
}
}
@@ -5350,9 +5891,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;
@@ -5365,11 +5906,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
}
mFaceList.clear();
-
- if (pAvatarVO)
- {
- pAvatarVO->addAttachmentArea( group->mSurfaceArea );
- }
}
static LLTrace::BlockTimerStatHandle FTM_REBUILD_MESH_FLUSH("Flush Mesh");
@@ -5398,6 +5934,15 @@ 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;
+ }
+ }
if (vobj->isNoLOD()) continue;
vobj->preRebuild();
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index a0342d31a2..0882fc095d 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -64,6 +64,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.
@@ -136,8 +138,10 @@ public:
/*virtual*/ const LLMatrix4 getRenderMatrix() const;
typedef std::map<LLUUID, S32> texture_cost_t;
U32 getRenderCost(texture_cost_t &textures) 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); }
+ /*virtual*/ F32 getEstTrianglesMax() const;
+ /*virtual*/ F32 getEstTrianglesStreamingCost() const;
+ /* virtual*/ F32 getStreamingCost() const;
+ /*virtual*/ bool getCostData(LLMeshCostData& costs) const;
/*virtual*/ U32 getTriangleCount(S32* vcount = NULL) const;
/*virtual*/ U32 getHighLODTriangleCount();
@@ -163,7 +167,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 markForUnload() { LLViewerObject::markForUnload(TRUE); mVolumeChanged = TRUE; }
void faceMappingChanged() { mFaceMappingChanged=TRUE; };
@@ -264,12 +268,29 @@ 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();
+
+ //virtual
+ void updateRiggingInfo();
+ S32 mLastRiggingInfoLOD;
+
// Functions that deal with media, or media navigation
// Update this object's media data with the given media data array
@@ -303,7 +324,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
@@ -332,7 +356,7 @@ public:
void clearRiggedVolume();
protected:
- S32 computeLODDetail(F32 distance, F32 radius, F32 lod_factor);
+ S32 computeLODDetail(F32 distance, F32 radius, F32 lod_factor);
BOOL calcLOD();
LLFace* addFace(S32 face_index);
void updateTEData();
@@ -356,6 +380,9 @@ public:
LLViewerTextureAnim *mTextureAnimp;
U8 mTexAnimMode;
+ F32 mLODDistance;
+ F32 mLODAdjustedDistance;
+ F32 mLODRadius;
private:
friend class LLDrawable;
friend class LLFace;
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index b8de59b6b8..5e597b85ff 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -3416,6 +3416,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;
@@ -6911,7 +6923,7 @@ bool LLPipeline::toggleRenderTypeControlNegated(S32 type)
}
//static
-void LLPipeline::toggleRenderDebug(U32 bit)
+void LLPipeline::toggleRenderDebug(U64 bit)
{
if (gPipeline.hasRenderDebugMask(bit))
{
@@ -6926,7 +6938,7 @@ void LLPipeline::toggleRenderDebug(U32 bit)
//static
-bool LLPipeline::toggleRenderDebugControl(U32 bit)
+bool LLPipeline::toggleRenderDebugControl(U64 bit)
{
return gPipeline.hasRenderDebugMask(bit);
}
@@ -11772,6 +11784,7 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar)
avatar->mNeedsImpostorUpdate = FALSE;
avatar->cacheImpostorValues();
+ avatar->mLastImpostorUpdateFrameTime = gFrameTimeSeconds;
LLVertexBuffer::unbind();
LLGLState::checkStates();
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 3646aaaf45..57d2331222 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -330,10 +330,10 @@ public:
void addTrianglesDrawn(S32 index_count, U32 render_type = LLRender::TRIANGLES);
bool hasRenderDebugFeatureMask(const U32 mask) const { return bool(mRenderDebugFeatureMask & mask); }
- bool hasRenderDebugMask(const U32 mask) const { return bool(mRenderDebugMask & mask); }
+ bool hasRenderDebugMask(const U64 mask) const { return bool(mRenderDebugMask & mask); }
void setAllRenderDebugFeatures() { mRenderDebugFeatureMask = 0xffffffff; }
void clearAllRenderDebugFeatures() { mRenderDebugFeatureMask = 0x0; }
- void setAllRenderDebugDisplays() { mRenderDebugMask = 0xffffffff; }
+ void setAllRenderDebugDisplays() { mRenderDebugMask = 0xffffffffffffffff; }
void clearAllRenderDebugDisplays() { mRenderDebugMask = 0x0; }
bool hasRenderType(const U32 type) const;
@@ -357,11 +357,11 @@ public:
// For UI control of render features
static bool hasRenderTypeControl(U32 data);
- static void toggleRenderDebug(U32 data);
+ static void toggleRenderDebug(U64 data);
static void toggleRenderDebugFeature(U32 data);
static void toggleRenderTypeControl(U32 data);
static bool toggleRenderTypeControlNegated(S32 data);
- static bool toggleRenderDebugControl(U32 data);
+ static bool toggleRenderDebugControl(U64 data);
static bool toggleRenderDebugFeatureControl(U32 data);
static void setRenderDebugFeatureControl(U32 bit, bool value);
@@ -500,39 +500,41 @@ public:
RENDER_DEBUG_FEATURE_FOOT_SHADOWS = 0x0100,
};
- enum LLRenderDebugMask
+ enum LLRenderDebugMask: U64
{
- RENDER_DEBUG_COMPOSITION = 0x00000001,
- RENDER_DEBUG_VERIFY = 0x00000002,
- RENDER_DEBUG_BBOXES = 0x00000004,
- RENDER_DEBUG_OCTREE = 0x00000008,
- RENDER_DEBUG_WIND_VECTORS = 0x00000010,
- RENDER_DEBUG_OCCLUSION = 0x00000020,
- RENDER_DEBUG_POINTS = 0x00000040,
- RENDER_DEBUG_TEXTURE_PRIORITY = 0x00000080,
- RENDER_DEBUG_TEXTURE_AREA = 0x00000100,
- RENDER_DEBUG_FACE_AREA = 0x00000200,
- RENDER_DEBUG_PARTICLES = 0x00000400,
- RENDER_DEBUG_GLOW = 0x00000800,
- RENDER_DEBUG_TEXTURE_ANIM = 0x00001000,
- RENDER_DEBUG_LIGHTS = 0x00002000,
- RENDER_DEBUG_BATCH_SIZE = 0x00004000,
- RENDER_DEBUG_ALPHA_BINS = 0x00008000,
- RENDER_DEBUG_RAYCAST = 0x00010000,
- RENDER_DEBUG_AVATAR_DRAW_INFO = 0x00020000,
- RENDER_DEBUG_SHADOW_FRUSTA = 0x00040000,
- RENDER_DEBUG_SCULPTED = 0x00080000,
- RENDER_DEBUG_AVATAR_VOLUME = 0x00100000,
- RENDER_DEBUG_AVATAR_JOINTS = 0x00200000,
- RENDER_DEBUG_BUILD_QUEUE = 0x00400000,
- RENDER_DEBUG_AGENT_TARGET = 0x00800000,
- RENDER_DEBUG_UPDATE_TYPE = 0x01000000,
- RENDER_DEBUG_PHYSICS_SHAPES = 0x02000000,
- 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_COMPOSITION = 0x00000001,
+ RENDER_DEBUG_VERIFY = 0x00000002,
+ RENDER_DEBUG_BBOXES = 0x00000004,
+ RENDER_DEBUG_OCTREE = 0x00000008,
+ RENDER_DEBUG_WIND_VECTORS = 0x00000010,
+ RENDER_DEBUG_OCCLUSION = 0x00000020,
+ RENDER_DEBUG_POINTS = 0x00000040,
+ RENDER_DEBUG_TEXTURE_PRIORITY = 0x00000080,
+ RENDER_DEBUG_TEXTURE_AREA = 0x00000100,
+ RENDER_DEBUG_FACE_AREA = 0x00000200,
+ RENDER_DEBUG_PARTICLES = 0x00000400,
+ 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, // not used
+ RENDER_DEBUG_RAYCAST = 0x00010000,
+ RENDER_DEBUG_AVATAR_DRAW_INFO = 0x00020000,
+ RENDER_DEBUG_SHADOW_FRUSTA = 0x00040000,
+ RENDER_DEBUG_SCULPTED = 0x00080000,
+ RENDER_DEBUG_AVATAR_VOLUME = 0x00100000,
+ RENDER_DEBUG_AVATAR_JOINTS = 0x00200000,
+ RENDER_DEBUG_BUILD_QUEUE = 0x00400000,
+ RENDER_DEBUG_AGENT_TARGET = 0x00800000,
+ RENDER_DEBUG_UPDATE_TYPE = 0x01000000,
+ RENDER_DEBUG_PHYSICS_SHAPES = 0x02000000,
+ RENDER_DEBUG_NORMALS = 0x04000000,
+ RENDER_DEBUG_LOD_INFO = 0x08000000,
+ RENDER_DEBUG_RENDER_COMPLEXITY = 0x10000000,
+ RENDER_DEBUG_ATTACHMENT_BYTES = 0x20000000, // not used
+ RENDER_DEBUG_TEXEL_DENSITY = 0x40000000,
+ RENDER_DEBUG_TRIANGLE_COUNT = 0x80000000,
+ RENDER_DEBUG_IMPOSTORS = 0x100000000
};
public:
@@ -677,10 +679,10 @@ protected:
std::stack<std::string> mRenderTypeEnableStack;
U32 mRenderDebugFeatureMask;
- U32 mRenderDebugMask;
+ U64 mRenderDebugMask;
+ U64 mOldRenderDebugMask;
std::stack<U32> mRenderDebugFeatureStack;
- U32 mOldRenderDebugMask;
/////////////////////////////////////////////
//
diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml
index 2750316f2e..3db431de1b 100644
--- a/indra/newview/skins/default/xui/en/floater_model_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml
@@ -14,6 +14,7 @@
<string name="status_idle"></string>
<string name="status_parse_error">Error: Dae parsing issue - see log for details.</string>
+ <string name="status_bind_shape_orientation">Warning: bind shape matrix is not in standard X-forward orientation.</string>
<string name="status_material_mismatch">Error: Material of model is not a subset of reference model.</string>
<string name="status_reading_file">Loading...</string>
<string name="status_generating_meshes">Generating Meshes...</string>
diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml
index bdcf3648fa..d9a15fed9e 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"
@@ -2135,7 +2135,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"
@@ -2173,13 +2173,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 8af754ffec..f676df5e8b 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -2641,6 +2641,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
@@ -3549,6 +3559,16 @@
function="Advanced.ToggleInfoDisplay"
parameter="agent target" />
</menu_item_check>
+ <menu_item_check
+ label="Show Impostor Extents"
+ name="Show Impostor Extents">
+ <menu_item_check.on_check
+ function="Advanced.CheckInfoDisplay"
+ parameter="impostors" />
+ <menu_item_check.on_click
+ function="Advanced.ToggleInfoDisplay"
+ parameter="impostors" />
+ </menu_item_check>
<!-- Appears not to exist anymore
<menu_item_check
label="Debug Rotation"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 23e1701dc6..54c209fb6a 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -10243,6 +10243,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 rigged triangle limit.
+ </notification>
+
<notification
icon="alertmodal.tga"
name="CantAttackMultipleObjOneSpot"
@@ -10373,6 +10381,46 @@ 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 rigged triangle 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 rigged triangle limit.
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="CantChangeAnimatedObjectStateInsufficientLand"
+ type="notify">
+ <tag>fail</tag>
+Can't change animated object state for this object because it would cause parcel limit to be exceeded.
+ </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/content_tools/anim_tool.py b/scripts/content_tools/anim_tool.py
index 3496617b21..3aef8cd5ab 100644
--- a/scripts/content_tools/anim_tool.py
+++ b/scripts/content_tools/anim_tool.py
@@ -610,12 +610,16 @@ def main(*argv):
parser = argparse.ArgumentParser(description="process SL animations")
parser.add_argument("--verbose", help="verbose flag", action="store_true")
- parser.add_argument("--dump", metavar="FILEPATH", help="dump to specified file")
+ parser.add_argument("--dump", help="dump to stdout", action="store_true")
parser.add_argument("--rot", help="specify sequence of rotations", type=float_triple, nargs="+")
parser.add_argument("--rand_pos", help="request NUM random positions (default %(default)s)",
metavar="NUM", type=int, default=2)
parser.add_argument("--reset_pos", help="request original positions", action="store_true")
parser.add_argument("--pos", help="specify sequence of positions", type=float_triple, nargs="+")
+ parser.add_argument("--duration", help="specify duration", type=float)
+ parser.add_argument("--loop_in", help="specify loop in time", type=float)
+ parser.add_argument("--loop_out", help="specify loop out time", type=float)
+ parser.add_argument("--num_pos", help="number of positions to create", type=int, default=2)
parser.add_argument("--delete_joints", help="specify joints to be deleted", nargs="+",
metavar="JOINT")
parser.add_argument("--joints", help="specify joints to be added or modified", nargs="+",
@@ -696,8 +700,17 @@ def main(*argv):
print "set joint priority",args.joint_priority
for joint in anim.joints:
joint.joint_priority = args.joint_priority
+ if args.duration is not None:
+ print "set duration",args.duration
+ anim.duration = args.duration
+ if args.loop_in is not None:
+ print "set loop_in",args.loop_in
+ anim.loop_in_point = args.loop_in
+ if args.loop_out is not None:
+ print "set loop_out",args.loop_out
+ anim.loop_out_point = args.loop_out
if args.dump:
- anim.dump(args.dump)
+ anim.dump("-")
if args.summary:
anim.summary()
if args.outfilename:
diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg
index 01397181e4..ed32804bef 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
@@ -7291,6 +7290,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 ae8937f461..db87ad5e77 100755
--- a/scripts/messages/message_template.msg.sha1
+++ b/scripts/messages/message_template.msg.sha1
@@ -1 +1 @@
-4c5ec7187d1af05b52b5c1bbac68d46fbc65da05 \ 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: