diff options
61 files changed, 3045 insertions, 700 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/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp index a3d5679f65..89335a20f5 100644 --- a/indra/llcharacter/lljoint.cpp +++ b/indra/llcharacter/lljoint.cpp @@ -881,7 +881,7 @@ void LLJoint::setWorldRotation( const LLQuaternion& rot ) //-------------------------------------------------------------------- const LLVector3& LLJoint::getScale() { - return mXform.getScale(); + return mXform.getScale(); } //-------------------------------------------------------------------- diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index 35e76f1d9d..4c3a676382 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -814,6 +814,10 @@ void LLMotionController::updateLoadingMotions() //----------------------------------------------------------------------------- void LLMotionController::updateMotions(bool force_update) { + // SL-763: "Distant animated objects run at super fast speed" + // The use_quantum optimization or possibly the associated code in setTimeStamp() + // does not work as implemented. + // Currently setting mTimeStep to nonzero is disabled elsewhere. BOOL use_quantum = (mTimeStep != 0.f); // Always update mPrevTimerElapsed diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index 9d9c64f4f0..bcd0bfc685 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -150,10 +150,13 @@ public: BOOL isPaused() const { return mPaused; } 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); 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 abe5fda603..627ca05f14 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/llvolume.cpp b/indra/llmath/llvolume.cpp index 5068c9c685..f9d24be055 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -1,5 +1,4 @@ /** - * @file llvolume.cpp * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index 1ae8a6ac15..f8e11e324e 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -818,6 +818,7 @@ char const* const _PREHASH_StateSave = LLMessageStringTable::getInstance()->getS char const* const _PREHASH_RoleData = LLMessageStringTable::getInstance()->getString("RoleData"); char const* const _PREHASH_AgentAnimation = LLMessageStringTable::getInstance()->getString("AgentAnimation"); char const* const _PREHASH_AvatarAnimation = LLMessageStringTable::getInstance()->getString("AvatarAnimation"); +char const* const _PREHASH_ObjectAnimation = LLMessageStringTable::getInstance()->getString("ObjectAnimation"); char const* const _PREHASH_LogDwellTime = LLMessageStringTable::getInstance()->getString("LogDwellTime"); char const* const _PREHASH_ParcelGodMarkAsContent = LLMessageStringTable::getInstance()->getString("ParcelGodMarkAsContent"); char const* const _PREHASH_UsePhysics = LLMessageStringTable::getInstance()->getString("UsePhysics"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 7910fde305..334236fb25 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -818,6 +818,7 @@ extern char const* const _PREHASH_StateSave; extern char const* const _PREHASH_RoleData; extern char const* const _PREHASH_AgentAnimation; extern char const* const _PREHASH_AvatarAnimation; +extern char const* const _PREHASH_ObjectAnimation; extern char const* const _PREHASH_LogDwellTime; extern char const* const _PREHASH_ParcelGodMarkAsContent; extern char const* const _PREHASH_UsePhysics; diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index bfa65666b5..c847cf653f 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1599,6 +1599,8 @@ BOOL LLNetworkData::isValid(U16 param_type, U32 size) return (size == 17); case PARAMS_LIGHT_IMAGE: return (size == 28); + case PARAMS_EXTENDED_MESH: + return (size == 4); } return FALSE; @@ -2026,3 +2028,67 @@ bool LLLightImageParams::fromLLSD(LLSD& sd) return false; } + +//============================================================================ + +LLExtendedMeshParams::LLExtendedMeshParams() +{ + mType = PARAMS_EXTENDED_MESH; + mFlags = 0; +} + +BOOL LLExtendedMeshParams::pack(LLDataPacker &dp) const +{ + dp.packU32(mFlags, "flags"); + + return TRUE; +} + +BOOL LLExtendedMeshParams::unpack(LLDataPacker &dp) +{ + dp.unpackU32(mFlags, "flags"); + + return TRUE; +} + +bool LLExtendedMeshParams::operator==(const LLNetworkData& data) const +{ + if (data.mType != PARAMS_EXTENDED_MESH) + { + return false; + } + + const LLExtendedMeshParams *param = (const LLExtendedMeshParams*)&data; + if ( (param->mFlags != mFlags) ) + { + return false; + } + + return true; +} + +void LLExtendedMeshParams::copy(const LLNetworkData& data) +{ + const LLExtendedMeshParams *param = (LLExtendedMeshParams*)&data; + mFlags = param->mFlags; +} + +LLSD LLExtendedMeshParams::asLLSD() const +{ + LLSD sd; + + sd["flags"] = LLSD::Integer(mFlags); + + return sd; +} + +bool LLExtendedMeshParams::fromLLSD(LLSD& sd) +{ + if (sd.has("flags")) + { + setFlags( sd["flags"].asInteger()); + return true; + } + + return false; +} diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h index 19d9d52817..9216c04229 100644 --- a/indra/llprimitive/llprimitive.h +++ b/indra/llprimitive/llprimitive.h @@ -106,6 +106,7 @@ public: PARAMS_LIGHT_IMAGE = 0x40, PARAMS_RESERVED = 0x50, // Used on server-side PARAMS_MESH = 0x60, + PARAMS_EXTENDED_MESH = 0x70, }; public: @@ -288,6 +289,27 @@ public: }; +class LLExtendedMeshParams : public LLNetworkData +{ +protected: + U32 mFlags; + +public: + static const U32 ANIMATED_MESH_ENABLED_FLAG = 0x1 << 0; + + LLExtendedMeshParams(); + /*virtual*/ BOOL pack(LLDataPacker &dp) const; + /*virtual*/ BOOL unpack(LLDataPacker &dp); + /*virtual*/ bool operator==(const LLNetworkData& data) const; + /*virtual*/ void copy(const LLNetworkData& data); + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd); + + void setFlags(const U32& flags) { mFlags = flags; } + U32 getFlags() const { return mFlags; } + +}; // This code is not naming-standards compliant. Leaving it like this for // now to make the connection to code in diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b4e930d062..3787a25a9b 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -149,6 +149,7 @@ set(viewer_SOURCE_FILES llcommunicationchannel.cpp llcompilequeue.cpp llconfirmationmanager.cpp + llcontrolavatar.cpp llconversationlog.cpp llconversationloglist.cpp llconversationloglistitem.cpp @@ -769,6 +770,7 @@ set(viewer_HEADER_FILES llcommunicationchannel.h llcompilequeue.h llconfirmationmanager.h + llcontrolavatar.h llconversationlog.h llconversationloglist.h llconversationloglistitem.h diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index ecd7c4bc36..5329b76390 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -51,6 +51,7 @@ <key>tags</key> <array> <!-- sample entry for debugging specific items + <string>AXON</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 9f82eb5b85..2fe01ab22f 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2182,6 +2182,28 @@ <key>Value</key> <string /> </map> + <key>DebugAnimatedObjects</key> + <map> + <key>Comment</key> + <string>Show info related to animated objects</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>AnimatedObjectsIgnoreLimits</key> + <map> + <key>Comment</key> + <string>Ignore server-enforced limits on animated objects. This is only useful for server testing.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>DebugAvatarAppearanceMessage</key> <map> <key>Comment</key> @@ -10393,6 +10415,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>RenderForceVolumeLOD</key> + <map> + <key>Comment</key> + <string>Override for all volume LODs</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>-1</integer> + </map> <key>RenderVolumeLODFactor</key> <map> <key>Comment</key> diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 0fb811a386..9f13a944ac 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -880,7 +880,10 @@ void LLWearableHoldingPattern::onAllComplete() ++it) { LLViewerObject *objectp = *it; - gAgentAvatarp->addAttachmentOverridesForObject(objectp); + if (!objectp->isAnimatedObject()) + { + gAgentAvatarp->addAttachmentOverridesForObject(objectp); + } } // Add new attachments to match those requested. diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 3f4a111a9a..fc41697367 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -592,6 +592,7 @@ static void settings_to_globals() LLVertexBuffer::sUseVAO = gSavedSettings.getBOOL("RenderUseVAO"); LLImageGL::sGlobalUseAnisotropic = gSavedSettings.getBOOL("RenderAnisotropic"); LLImageGL::sCompressTextures = gSavedSettings.getBOOL("RenderCompressTextures"); + LLVOVolume::sForceLOD = gSavedSettings.getS32("RenderForceVolumeLOD"); LLVOVolume::sLODFactor = gSavedSettings.getF32("RenderVolumeLODFactor"); LLVOVolume::sDistanceFactor = 1.f-LLVOVolume::sLODFactor * 0.1f; LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor"); diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp new file mode 100644 index 0000000000..7e89a97c03 --- /dev/null +++ b/indra/newview/llcontrolavatar.cpp @@ -0,0 +1,404 @@ +/** + * @file llcontrolavatar.cpp + * @brief Implementation for special dummy avatar used to drive rigged meshes. + * + * $LicenseInfo:firstyear=2017&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2017, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llcontrolavatar.h" +#include "llagent.h" // Get state values from here +#include "llviewerobjectlist.h" +#include "pipeline.h" +#include "llanimationstates.h" +#include "llviewercontrol.h" +#include "llmeshrepository.h" +#include "llviewerregion.h" + +LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) : + LLVOAvatar(id, pcode, regionp), + mPlaying(false), + mGlobalScale(1.0f), + mMarkedForDeath(false) +{ + mIsControlAvatar = true; + mEnableDefaultMotions = false; +} + +// virtual +LLControlAvatar::~LLControlAvatar() +{ +} + +// virtual +void LLControlAvatar::initInstance() +{ + // AXON - 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(); +} + +void LLControlAvatar::matchVolumeTransform() +{ + if (mRootVolp) + { + if (mRootVolp->isAttachment()) + { + LLVOAvatar *attached_av = mRootVolp->getAvatarAncestor(); + if (attached_av) + { + LLViewerJointAttachment *attach = attached_av->getTargetAttachmentPoint(mRootVolp); + setPositionAgent(mRootVolp->getRenderPosition()); + attach->updateWorldPRSParent(); + LLVector3 joint_pos = attach->getWorldPosition(); + LLQuaternion joint_rot = attach->getWorldRotation(); + LLVector3 obj_pos = mRootVolp->mDrawable->getPosition(); + LLQuaternion obj_rot = mRootVolp->mDrawable->getRotation(); + obj_pos.rotVec(joint_rot); + mRoot->setWorldPosition(obj_pos + joint_pos); + mRoot->setWorldRotation(obj_rot * joint_rot); + setRotation(mRoot->getRotation()); + } + else + { + LL_WARNS_ONCE() << "can't find attached av!" << LL_ENDL; + } + } + else + { + setPositionAgent(mRootVolp->getRenderPosition()); + LLQuaternion obj_rot = mRootVolp->getRotation(); + LLQuaternion result_rot = obj_rot; + setRotation(result_rot); + mRoot->setWorldRotation(result_rot); + mRoot->setPosition(mRootVolp->getRenderPosition()); + } + } +} + +void LLControlAvatar::setGlobalScale(F32 scale) +{ + if (scale <= 0.0) + { + LL_WARNS() << "invalid global scale " << scale << LL_ENDL; + return; + } + if (scale != mGlobalScale) + { + F32 adjust_scale = scale/mGlobalScale; + LL_INFOS() << "scale " << scale << " adjustment " << adjust_scale << LL_ENDL; + // AXON - 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(); + + // AXON testing scale + + // 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; + + cav->createDrawable(&gPipeline); + cav->mIsDummy = TRUE; + cav->mSpecialRenderMode = 1; + cav->updateJointLODs(); + cav->updateGeometry(cav->mDrawable); + cav->hideSkirt(); + + // Sync up position/rotation with object + cav->matchVolumeTransform(); + + return cav; +} + +void LLControlAvatar::markForDeath() +{ + mMarkedForDeath = true; +} + +void LLControlAvatar::idleUpdate(LLAgent &agent, const F64 &time) +{ + if (mMarkedForDeath) + { + markDead(); + mMarkedForDeath = false; + } + else + { + LLVOAvatar::idleUpdate(agent,time); + } +} + +BOOL LLControlAvatar::updateCharacter(LLAgent &agent) +{ + return LLVOAvatar::updateCharacter(agent); +} + +//virtual +void LLControlAvatar::updateDebugText() +{ + if (gSavedSettings.getBOOL("DebugAnimatedObjects")) + { + S32 total_linkset_count = 0; + if (mRootVolp) + { + total_linkset_count = 1 + mRootVolp->getChildren().size(); + } + std::vector<LLVOVolume*> volumes; + getAnimatedVolumes(volumes); + S32 animated_volume_count = volumes.size(); + std::string active_string; + std::string type_string; + std::string lod_string; + S32 total_tris = 0; + S32 total_verts = 0; + 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; + lod_string += llformat("%d",volp->getLOD()); + if (volp && volp->mDrawable) + { + if (volp->mDrawable->isActive()) + { + active_string += "A"; + } + else + { + active_string += "S"; + } + if (volp->isRiggedMesh()) + { + // Rigged/animateable mesh + type_string += "R"; + } + else if (volp->isMesh()) + { + // Static mesh + type_string += "M"; + } + else + { + // Any other prim + type_string += "P"; + } + } + else + { + active_string += "-"; + type_string += "-"; + } + } + addDebugText(llformat("CAV obj %d anim %d active %s", + total_linkset_count, animated_volume_count, active_string.c_str())); + addDebugText(llformat("types %s lods %s", type_string.c_str(), lod_string.c_str())); + addDebugText(llformat("tris %d verts %d", total_tris, total_verts)); + 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())); + //addDebugText(llformat("anim time %.1f (step %f factor %f)", + // mMotionController.getAnimTime(), + // mMotionController.getTimeStep(), + // mMotionController.getTimeFactor())); + + } + 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("AXON") << "No root vol" << LL_ENDL; + return; + } + + std::vector<LLVOVolume*> volumes; + getAnimatedVolumes(volumes); + + // Rebuild mSignaledAnimations from the associated volumes. + std::map<LLUUID, S32> anims; + for (std::vector<LLVOVolume*>::iterator vol_it = volumes.begin(); vol_it != volumes.end(); ++vol_it) + { + LLVOVolume *volp = *vol_it; + for (std::map<LLUUID,S32>::iterator anim_it = volp->mObjectSignaledAnimations.begin(); + anim_it != volp->mObjectSignaledAnimations.end(); + ++anim_it) + { + std::map<LLUUID,S32>::iterator found_anim_it = anims.find(anim_it->first); + if (found_anim_it != anims.end()) + { + // Animation already present, use the larger sequence id + anims[anim_it->first] = llmax(found_anim_it->second, anim_it->second); + } + else + { + // Animation not already present, use this sequence id. + anims[anim_it->first] = anim_it->second; + } + } + } + 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; +} diff --git a/indra/newview/llcontrolavatar.h b/indra/newview/llcontrolavatar.h new file mode 100644 index 0000000000..a0f7912d24 --- /dev/null +++ b/indra/newview/llcontrolavatar.h @@ -0,0 +1,83 @@ +/** + * @file llcontrolavatar.h + * @brief Special dummy avatar used to drive rigged meshes. + * + * $LicenseInfo:firstyear=2017&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2017, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_CONTROLAVATAR_H +#define LL_CONTROLAVATAR_H + +#include "llvoavatar.h" +#include "llvovolume.h" + +class LLControlAvatar: + public LLVOAvatar +{ + LOG_CLASS(LLControlAvatar); + +public: + LLControlAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); + virtual void initInstance(); // Called after construction to initialize the class. + virtual ~LLControlAvatar(); + + void matchVolumeTransform(); + void updateVolumeGeom(); + + void setGlobalScale(F32 scale); + void recursiveScaleJoint(LLJoint *joint, F32 factor); + static LLControlAvatar *createControlAvatar(LLVOVolume *obj); + + // Delayed kill so we don't make graphics pipeline unhappy calling + // markDead() inside other graphics pipeline operations. + void markForDeath(); + + virtual void idleUpdate(LLAgent &agent, const F64 &time); + virtual BOOL updateCharacter(LLAgent &agent); + + void getAnimatedVolumes(std::vector<LLVOVolume*>& volumes); + void updateAnimations(); + + virtual LLViewerObject* lineSegmentIntersectRiggedAttachments( + const LLVector4a& start, const LLVector4a& end, + S32 face = -1, // which face to check, -1 = ALL_SIDES + BOOL pick_transparent = FALSE, + BOOL pick_rigged = FALSE, + S32* face_hit = NULL, // which face was hit + LLVector4a* intersection = NULL, // return the intersection point + LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point + LLVector4a* normal = NULL, // return the surface normal at the intersection point + LLVector4a* tangent = NULL); // return the surface tangent at the intersection point + + virtual void updateDebugText(); + + bool mPlaying; + + F32 mGlobalScale; + + LLVOVolume *mRootVolp; + + bool mMarkedForDeath; + +}; + +#endif //LL_CONTROLAVATAR_H diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index f956023358..6799c3f862 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -50,6 +50,7 @@ #include "llviewerobjectlist.h" #include "llviewerwindow.h" #include "llvocache.h" +#include "llcontrolavatar.h" const F32 MIN_INTERPOLATE_DISTANCE_SQUARED = 0.001f * 0.001f; const F32 MAX_INTERPOLATE_DISTANCE_SQUARED = 10.f * 10.f; @@ -550,7 +551,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); diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 8128790eb6..0d8e302123 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -467,7 +467,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->mIsDummy && !avatarp->isControlAvatar()) || avatarp->mDrawable.isNull()) { return; } @@ -1707,13 +1707,7 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow) continue; } - LLUUID mesh_id = volume->getParams().getSculptID(); - if (mesh_id.isNull()) - { - continue; - } - - const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(mesh_id, vobj); + const LLMeshSkinInfo* skin = vobj->getSkinInfo(); if (!skin) { continue; @@ -1927,13 +1921,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; diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 3d5e2d356e..eebdb62aff 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -330,11 +330,7 @@ void LLFace::dirtyTexture() { vobj->mLODChanged = TRUE; - LLVOAvatar* avatar = vobj->getAvatar(); - if (avatar) - { //avatar render cost may have changed - avatar->updateVisualComplexity(); - } + vobj->updateVisualComplexity(); } gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_VOLUME, FALSE); } diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index da84a6b8f8..3989adf8d4 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -1755,9 +1755,17 @@ void LLModelPreview::getJointAliases( JointMap& joint_map) //Joint names and aliases come from avatar_skeleton.xml joint_map = av->getJointAliases(); - for (S32 i = 0; i < av->mNumCollisionVolumes; i++) + + std::vector<std::string> cv_names, attach_names; + av->getSortedJointNames(1, cv_names); + av->getSortedJointNames(2, attach_names); + for (std::vector<std::string>::iterator it = cv_names.begin(); it != cv_names.end(); ++it) + { + joint_map[*it] = *it; + } + for (std::vector<std::string>::iterator it = attach_names.begin(); it != attach_names.end(); ++it) { - joint_map[av->mCollisionVolumes[i].getName()] = av->mCollisionVolumes[i].getName(); + joint_map[*it] = *it; } } diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index df708013fc..7c48d9b233 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -4021,6 +4021,38 @@ void LLMeshRepository::uploadError(LLSD& args) mUploadErrorQ.push(args); } +F32 LLMeshRepository::getEstTrianglesMax(LLUUID mesh_id) +{ + F32 triangles_max = 0.f; + if (mThread && mesh_id.notNull()) + { + LLMutexLock lock(mThread->mHeaderMutex); + LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); + if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0) + { + LLSD& header = iter->second; + if (header.has("404") + || !header.has("lowest_lod") + || (header.has("version") && header["version"].asInteger() > MAX_MESH_VERSION)) + { + return 0.f; + } + + S32 bytes_high = header["high_lod"]["size"].asInteger(); + S32 bytes_med = header["medium_lod"]["size"].asInteger(); + S32 bytes_low = header["low_lod"]["size"].asInteger(); + S32 bytes_lowest = header["lowest_lod"]["size"].asInteger(); + S32 bytes_max = llmax(bytes_high, bytes_med, bytes_low, bytes_lowest); + + F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount"); //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead + F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize"); //make sure nothing is "free" + F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle"); + triangles_max = llmax((F32) bytes_max-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle; + } + } + return triangles_max; +} + F32 LLMeshRepository::getStreamingCost(LLUUID mesh_id, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value) { if (mThread && mesh_id.notNull()) diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 23af837f6f..d2eac449f7 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -472,6 +472,8 @@ public: static LLDeadmanTimer sQuiescentTimer; // Time-to-complete-mesh-downloads after significant events + // Estimated triangle count of the largest LOD + F32 getEstTrianglesMax(LLUUID mesh_id); F32 getStreamingCost(LLUUID mesh_id, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL); diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 0bf4d48421..8f2cf5bb89 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -342,9 +342,14 @@ void LLPanelObject::getState( ) } // can move or rotate only linked group with move permissions, or sub-object with move and modify perms - BOOL enable_move = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && !objectp->isAttachment() && (objectp->permModify() || !gSavedSettings.getBOOL("EditLinkedParts")); + + // AXON REVIEW BEFORE RELEASE, behavior during edit is glitchy. + // it's not entirely clear what the motivation is to have 3 + // different rules for enablement. At least the difference between + // move and rotate looks like just a parens error, have updated accordingly. + BOOL enable_move = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && ((objectp->permModify() && !objectp->isAttachment()) || !gSavedSettings.getBOOL("EditLinkedParts")); BOOL enable_scale = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && objectp->permModify(); - BOOL enable_rotate = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && ( (objectp->permModify() && !objectp->isAttachment()) || !gSavedSettings.getBOOL("EditLinkedParts")); + BOOL enable_rotate = objectp->permMove() && !objectp->isPermanentEnforced() && ((root_objectp == NULL) || !root_objectp->isPermanentEnforced()) && ((objectp->permModify() && !objectp->isAttachment()) || !gSavedSettings.getBOOL("EditLinkedParts")); S32 selected_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); BOOL single_volume = (LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME )) diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index b1895bfc9b..6c17fb9f8d 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -78,6 +78,8 @@ #include "llviewercontrol.h" #include "llmeshrepository.h" +#include "llvoavatarself.h" + #include <boost/bind.hpp> // "Features" Tab @@ -86,6 +88,7 @@ BOOL LLPanelVolume::postBuild() { // Flexible Objects Parameters { + childSetCommitCallback("Animated Mesh Checkbox Ctrl", boost::bind(&LLPanelVolume::onCommitAnimatedMeshCheckbox, this, _1, _2), NULL); childSetCommitCallback("Flexible1D Checkbox Ctrl", boost::bind(&LLPanelVolume::onCommitIsFlexible, this, _1, _2), NULL); childSetCommitCallback("FlexNumSections",onCommitFlexible,this); getChild<LLUICtrl>("FlexNumSections")->setValidateBeforeCommit(precommitValidate); @@ -237,6 +240,11 @@ void LLPanelVolume::getState( ) { volobjp = (LLVOVolume *)objectp; } + LLVOVolume *root_volobjp = NULL; + if (root_objectp && (root_objectp->getPCode() == LL_PCODE_VOLUME)) + { + root_volobjp = (LLVOVolume *)root_objectp; + } if( !objectp ) { @@ -260,6 +268,8 @@ void LLPanelVolume::getState( ) BOOL editable = root_objectp->permModify() && !root_objectp->isPermanentEnforced(); BOOL single_volume = LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME ) && LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1; + BOOL single_root_volume = LLSelectMgr::getInstance()->selectionAllPCode( LL_PCODE_VOLUME ) && + LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() == 1; // Select Single Message if (single_volume) @@ -347,7 +357,23 @@ void LLPanelVolume::getState( ) getChildView("Light Focus")->setEnabled(false); getChildView("Light Ambiance")->setEnabled(false); } - + + // Animated Mesh + BOOL is_animated_mesh = single_root_volume && root_volobjp && root_volobjp->isAnimatedObject(); + getChild<LLUICtrl>("Animated Mesh Checkbox Ctrl")->setValue(is_animated_mesh); + BOOL enabled_animated_object_box = FALSE; + if (root_volobjp && root_volobjp == volobjp) + { + enabled_animated_object_box = single_root_volume && root_volobjp && root_volobjp->canBeAnimatedObject() && editable; + if (enabled_animated_object_box && !is_animated_mesh && + root_volobjp->isAttachment() && !gAgentAvatarp->canAttachMoreAnimatedObjects()) + { + // Turning this attachment animated would cause us to exceed the limit. + enabled_animated_object_box = false; + } + } + getChildView("Animated Mesh Checkbox Ctrl")->setEnabled(enabled_animated_object_box); + // Flexible properties BOOL is_flexible = volobjp && volobjp->isFlexible(); getChild<LLUICtrl>("Flexible1D Checkbox Ctrl")->setValue(is_flexible); @@ -587,6 +613,7 @@ void LLPanelVolume::clearCtrls() getChildView("Light Radius")->setEnabled(false); getChildView("Light Falloff")->setEnabled(false); + getChildView("Animated Mesh Checkbox Ctrl")->setEnabled(false); getChildView("Flexible1D Checkbox Ctrl")->setEnabled(false); getChildView("FlexNumSections")->setEnabled(false); getChildView("FlexGravity")->setEnabled(false); @@ -896,6 +923,31 @@ void LLPanelVolume::onCommitFlexible( LLUICtrl* ctrl, void* userdata ) self->refresh(); } +void LLPanelVolume::onCommitAnimatedMeshCheckbox(LLUICtrl *, void*) +{ + LLViewerObject* objectp = mObject; + if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME)) + { + return; + } + LLVOVolume *volobjp = (LLVOVolume *)objectp; + BOOL animated_mesh = getChild<LLUICtrl>("Animated Mesh Checkbox Ctrl")->getValue(); + U32 flags = volobjp->getExtendedMeshFlags(); + U32 new_flags = flags; + if (animated_mesh) + { + new_flags |= LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG; + } + else + { + new_flags &= ~LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG; + } + if (new_flags != flags) + { + volobjp->setExtendedMeshFlags(new_flags); + } +} + void LLPanelVolume::onCommitIsFlexible(LLUICtrl *, void*) { if (mObject->flagObjectPermanent()) diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h index e3453ae99c..66117316cf 100644 --- a/indra/newview/llpanelvolume.h +++ b/indra/newview/llpanelvolume.h @@ -63,6 +63,7 @@ public: static void onCommitLight( LLUICtrl* ctrl, void* userdata); void onCommitIsFlexible( LLUICtrl* ctrl, void* userdata); static void onCommitFlexible( LLUICtrl* ctrl, void* userdata); + void onCommitAnimatedMeshCheckbox(LLUICtrl* ctrl, void* userdata); static void onCommitPhysicsParam( LLUICtrl* ctrl, void* userdata); static void onCommitMaterial( LLUICtrl* ctrl, void* userdata); diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 983a7ca1ae..2e489fbc09 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" @@ -662,6 +663,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; @@ -686,6 +691,10 @@ bool LLSelectMgr::enableLinkObjects() new_value = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly); } } + if (!LLSelectMgr::getInstance()->getSelection()->checkAnimatedObjectLinkable()) + { + new_value = false; + } return new_value; } @@ -6628,7 +6637,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 @@ -6646,23 +6658,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 @@ -6739,6 +6740,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() @@ -7375,6 +7429,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 7ef0032645..eaeeba576c 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -339,6 +339,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) @@ -742,6 +745,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 ¤t_zoom) const; @@ -843,7 +848,7 @@ private: LLFrameTimer mEffectsTimer; BOOL mForceSelection; - LLAnimPauseRequest mPauseRequest; + std::vector<LLAnimPauseRequest> mPauseRequests; }; // *DEPRECATED: For callbacks or observers, use diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index df5756cf11..b4116026fa 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -872,16 +872,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(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 89cc66cf33..acf5ce2f92 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2387,6 +2387,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/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index f473000657..0e169408ff 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -1751,8 +1751,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/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 7c1921b143..66f5bf3c37 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -208,6 +208,12 @@ static bool handleAnisotropicChanged(const LLSD& newvalue) return true; } +static bool handleForceLODChanged(const LLSD& newvalue) +{ + LLVOVolume::sForceLOD = (F32) newvalue.asReal(); + return true; +} + static bool handleVolumeLODChanged(const LLSD& newvalue) { LLVOVolume::sLODFactor = (F32) newvalue.asReal(); @@ -626,6 +632,7 @@ void settings_setup_listeners() gSavedSettings.getControl("RenderAvatarCloth")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); gSavedSettings.getControl("WindLightUseAtmosShaders")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); gSavedSettings.getControl("RenderGammaFull")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); + gSavedSettings.getControl("RenderForceVolumeLOD")->getSignal()->connect(boost::bind(&handleForceLODChanged, _2)); gSavedSettings.getControl("RenderVolumeLODFactor")->getSignal()->connect(boost::bind(&handleVolumeLODChanged, _2)); gSavedSettings.getControl("RenderAvatarLODFactor")->getSignal()->connect(boost::bind(&handleAvatarLODChanged, _2)); gSavedSettings.getControl("RenderAvatarPhysicsLODFactor")->getSignal()->connect(boost::bind(&handleAvatarPhysicsLODChanged, _2)); 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 912b0e0b04..f927f64542 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1077,6 +1077,10 @@ U32 info_display_from_string(std::string info_display) { return LLPipeline::RENDER_DEBUG_TEXEL_DENSITY; } + else if ("triangle count" == info_display) + { + return LLPipeline::RENDER_DEBUG_TRIANGLE_COUNT; + } else { LL_WARNS() << "unrecognized feature name '" << info_display << "'" << LL_ENDL; @@ -6908,7 +6912,7 @@ class LLAttachmentEnableDrop : public view_listener_t // Do not enable drop if all faces of object are not enabled if (object && LLSelectMgr::getInstance()->getSelection()->contains(object,SELECT_ALL_TES )) { - S32 attachmentID = ATTACHMENT_ID_FROM_STATE(object->getState()); + S32 attachmentID = ATTACHMENT_ID_FROM_STATE(object->getAttachmentState()); attachment = get_if_there(gAgentAvatarp->mAttachmentPoints, attachmentID, (LLViewerJointAttachment*)NULL); if (attachment) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 2144c7d481..24af491391 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -55,6 +55,7 @@ #include "llagentcamera.h" #include "llcallingcard.h" #include "llbuycurrencyhtml.h" +#include "llcontrolavatar.h" #include "llfirstuse.h" #include "llfloaterbump.h" #include "llfloaterbuyland.h" @@ -103,6 +104,7 @@ #include "llviewerwindow.h" #include "llvlmanager.h" #include "llvoavatarself.h" +#include "llvovolume.h" #include "llworld.h" #include "pipeline.h" #include "llfloaterworldmap.h" @@ -4982,12 +4984,15 @@ 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) { @@ -4999,6 +5004,7 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList); S32 num_source_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationSourceList); + //clear animation flags avatarp->mSignaledAnimations.clear(); if (avatarp->isSelf()) @@ -5069,6 +5075,95 @@ 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); + + LLViewerObject *objp = gObjectList.findObject(uuid); + if (!objp) + { + LL_WARNS("Messaging") << "AXON Received animation state for unknown object" << uuid << LL_ENDL; + return; + } + + LLVOVolume *volp = dynamic_cast<LLVOVolume*>(objp); + if (!volp) + { + LL_WARNS("Messaging") << "AXON Received animation state for non-volume object" << uuid << LL_ENDL; + return; + } + + if (!volp->isAnimatedObject()) + { + LL_WARNS("Messaging") << "AXON Received animation state for non-animated object" << uuid << LL_ENDL; + return; + } + + LLControlAvatar *avatarp = volp->getControlAvatar(); + if (!avatarp) + { + LL_WARNS("Messaging") << "AXON no control avatar, ignoring" << LL_ENDL; + return; + } + + S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList); + LL_DEBUGS("AXON") << "processing object animation requests, num_blocks " << num_blocks << LL_ENDL; + +#if 1 + // Here we go into skinned mode once, the first time we get an + // animation request, and then stay there. This is probably the + // normally desired behavior. + if (!avatarp->mPlaying) + { + avatarp->mPlaying = true; + if (!avatarp->mRootVolp->isAnySelected()) + { + avatarp->updateVolumeGeom(); + avatarp->mRootVolp->recursiveMarkForUpdate(TRUE); + } + } +#else// AXON REMOVE BEFORE RELEASE? + // In this block we switch back into static mode when no animations are + // playing. This is mostly useful for debugging. + if (num_blocks > 0 && !avatarp->mPlaying) + { + avatarp->mPlaying = true; + if (!avatarp->mRootVolp->isAnySelected()) + { + avatarp->updateVolumeGeom(); + avatarp->mRootVolp->recursiveMarkForUpdate(TRUE); + } + } + else if (num_blocks == 0 && avatarp->mPlaying) + { + avatarp->mPlaying = false; + if (!avatarp->mRootVolp->isAnySelected()) + { + avatarp->updateVolumeGeom(); + avatarp->mRootVolp->recursiveMarkForUpdate(TRUE); + } + } +#endif + + volp->mObjectSignaledAnimations.clear(); + + for( S32 i = 0; i < num_blocks; i++ ) + { + mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i); + mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i); + volp->mObjectSignaledAnimations[animation_id] = anim_sequence_id; + LL_DEBUGS("AXON") << "got object animation request for object " + << uuid << " animation id " << animation_id << LL_ENDL; + } + + avatarp->updateAnimations(); +} + + void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data) { LLUUID uuid; diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index b0eaa37541..cef6f79812 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -95,6 +95,7 @@ void process_health_message(LLMessageSystem *mesgsys, void **user_data); void process_sim_stats(LLMessageSystem *mesgsys, void **user_data); void process_shooter_agent_hit(LLMessageSystem* msg, void** user_data); void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data); +void process_object_animation(LLMessageSystem *mesgsys, void **user_data); void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data); void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data); void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 5de4029542..2e4c995be4 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -60,6 +60,7 @@ #include "llbbox.h" #include "llbox.h" #include "llcylinder.h" +#include "llcontrolavatar.h" #include "lldrawable.h" #include "llface.h" #include "llfloaterproperties.h" @@ -138,7 +139,7 @@ const F32 PHYSICS_TIMESTEP = 1.f / 45.f; static LLTrace::BlockTimerStatHandle FTM_CREATE_OBJECT("Create Object"); // static -LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) +LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp, S32 flags) { LLViewerObject *res = NULL; LL_RECORD_BLOCK_TIME(FTM_CREATE_OBJECT); @@ -166,6 +167,12 @@ LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pco } res = gAgentAvatarp; } + else if (flags & CO_FLAG_CONTROL_AVATAR) + { + LLControlAvatar *avatar = new LLControlAvatar(id, pcode, regionp); + avatar->initInstance(); + res = avatar; + } else { LLVOAvatar *avatar = new LLVOAvatar(id, pcode, regionp); @@ -235,6 +242,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mText(), mHudText(""), mHudTextColor(LLColor4::white), + mControlAvatar(NULL), mLastInterpUpdateSecs(0.f), mLastMessageUpdateSecs(0.f), mLatestRecvPacketID(0), @@ -259,7 +267,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mRotTime(0.f), mAngularVelocityRot(), mPreviousRotation(), - mState(0), + mAttachmentState(0), mMedia(NULL), mClickAction(0), mObjectCost(0), @@ -363,17 +371,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->removeAttachmentOverridesForObject(mesh_id); + } + } + if (getControlAvatar()) + { + unlinkControlAvatar(); + } // Mark itself as dead mDead = TRUE; @@ -674,6 +688,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; @@ -1378,7 +1404,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, U8 state; mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); - mState = state; + mAttachmentState = state; // ...new objects that should come in selected need to be added to the selected list mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0); @@ -1648,7 +1674,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, U8 state; mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num ); - mState = state; + mAttachmentState = state; break; } @@ -1671,7 +1697,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, U8 state; dp->unpackU8(state, "State"); - mState = state; + mAttachmentState = state; switch(update_type) { @@ -2904,6 +2930,63 @@ void LLViewerObject::fetchInventoryFromServer() } } +LLControlAvatar *LLViewerObject::getControlAvatar() +{ + return getRootEdit()->mControlAvatar.get(); +} + +LLControlAvatar *LLViewerObject::getControlAvatar() const +{ + return getRootEdit()->mControlAvatar.get(); +} + +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); + } + if (getControlAvatar()) + { + getControlAvatar()->addAttachmentOverridesForObject(this); + } + else + { + LL_WARNS() << "no control avatar found!" << LL_ENDL; + } +} + +void LLViewerObject::unlinkControlAvatar() +{ + if (getControlAvatar()) + { + getControlAvatar()->removeAttachmentOverridesForObject(this); + } + 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; @@ -3513,6 +3596,45 @@ F32 LLViewerObject::getLinksetPhysicsCost() return mLinksetPhysicsCost; } +F32 LLViewerObject::recursiveGetEstTrianglesMax() const +{ + F32 est_tris = getEstTrianglesMax(); + for (child_list_t::const_iterator iter = mChildList.begin(); + iter != mChildList.end(); iter++) + { + const LLViewerObject* child = *iter; + est_tris += child->recursiveGetEstTrianglesMax(); + } + return est_tris; +} + +S32 LLViewerObject::getAnimatedObjectMaxTris() const +{ + S32 max_tris = 0; + if (gSavedSettings.getBOOL("AnimatedObjectsIgnoreLimits")) // AXON REMOVE AFTER SERVER TESTING DONE + { + max_tris = S32_MAX; + } + else + { + if (gAgent.getRegion()) + { + LLSD features; + gAgent.getRegion()->getSimulatorFeatures(features); + if (features.has("AnimatedObjects")) + { + max_tris = features["AnimatedObjects"]["AnimatedObjectMaxTris"].asInteger(); + } + } + } + return max_tris; +} + +F32 LLViewerObject::getEstTrianglesMax() const +{ + return 0.f; +} + F32 LLViewerObject::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const { return 0.f; @@ -3528,6 +3650,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; @@ -3631,7 +3805,6 @@ void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */) } } - void LLViewerObject::setLineWidthForWindowSize(S32 window_width) { if (window_width < 700) @@ -3859,7 +4032,7 @@ const LLVector3 LLViewerObject::getRenderPosition() const if (mDrawable.notNull() && mDrawable->isState(LLDrawable::RIGGED)) { LLVOAvatar* avatar = getAvatar(); - if (avatar) + if (avatar && !getControlAvatar()) { return avatar->getPositionAgent(); } @@ -3883,7 +4056,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; } @@ -5121,7 +5294,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(); @@ -5460,6 +5639,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; @@ -5859,6 +6043,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()) @@ -5910,6 +6105,11 @@ void LLViewerObject::setRegion(LLViewerRegion *regionp) child->setRegion(regionp); } + if (mControlAvatar) + { + mControlAvatar->setRegion(regionp); + } + setChanged(MOVED | SILHOUETTE); updateDrawable(FALSE); } @@ -6358,6 +6558,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 7a490f6957..0bfc513dc9 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -47,6 +47,7 @@ class LLAgent; // TODO: Get rid of this. class LLAudioSource; class LLAudioSourceVO; class LLColor4; +class LLControlAvatar; class LLDataPacker; class LLDataPackerBinaryBuffer; class LLDrawable; @@ -220,6 +221,8 @@ public: LLViewerRegion* getRegion() const { return mRegionp; } BOOL isSelected() const { return mUserSelected; } + // Check whole linkset + BOOL isAnySelected() const; virtual void setSelected(BOOL sel); const LLUUID &getID() const { return mID; } @@ -231,6 +234,7 @@ public: virtual BOOL isFlexible() const { return FALSE; } virtual BOOL isSculpted() const { return FALSE; } virtual BOOL isMesh() const { return FALSE; } + virtual BOOL isRiggedMesh() const { return FALSE; } virtual BOOL hasLightTexture() const { return FALSE; } // This method returns true if the object is over land owned by @@ -356,9 +360,15 @@ public: virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE); + S32 getAnimatedObjectMaxTris() const; + F32 recursiveGetEstTrianglesMax() const; + virtual F32 getEstTrianglesMax() const; virtual F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL, F32* unscaled_value = NULL) const; virtual U32 getTriangleCount(S32* vcount = NULL) const; virtual U32 getHighLODTriangleCount(); + F32 recursiveGetScaledSurfaceArea() const; + + U32 recursiveGetTriangleCount(S32* vcount = NULL) const; void setObjectCost(F32 cost); F32 getObjectCost(); @@ -374,7 +384,7 @@ public: void sendShapeUpdate(); - U8 getState() { return mState; } + U8 getAttachmentState() { return mAttachmentState; } F32 getAppAngle() const { return mAppAngle; } F32 getPixelArea() const { return mPixelArea; } @@ -411,7 +421,8 @@ public: void setIcon(LLViewerTexture* icon_image); void clearIcon(); - void markForUpdate(BOOL priority); + void recursiveMarkForUpdate(BOOL priority); + virtual void markForUpdate(BOOL priority); void updateVolume(const LLVolumeParams& volume_params); virtual void updateSpatialExtents(LLVector4a& min, LLVector4a& max); virtual F32 getBinRadius(); @@ -683,6 +694,21 @@ 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(); + + virtual bool isAnimatedObject() const; + +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 @@ -693,8 +719,10 @@ protected: // updateInventory. void doUpdateInventory(LLPointer<LLViewerInventoryItem>& item, U8 key, bool is_new); + // Flags for createObject + static const S32 CO_FLAG_CONTROL_AVATAR = 1 << 1; - static LLViewerObject *createObject(const LLUUID &id, LLPCode pcode, LLViewerRegion *regionp); + static LLViewerObject *createObject(const LLUUID &id, LLPCode pcode, LLViewerRegion *regionp, S32 flags = 0); BOOL setData(const U8 *datap, const U32 data_size); @@ -782,7 +810,7 @@ protected: LLQuaternion mAngularVelocityRot; // accumulated rotation from the angular velocity computations LLQuaternion mPreviousRotation; - U8 mState; // legacy + U8 mAttachmentState; // this encodes the attachment id in a somewhat complex way. 0 if not an attachment. LLViewerObjectMedia* mMedia; // NULL if no media associated U8 mClickAction; F32 mObjectCost; //resource cost of this object or -1 if unknown diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 3c83e3a006..6b7e81cb65 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -862,8 +862,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; @@ -900,7 +900,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 @@ -1947,12 +1947,12 @@ void LLViewerObjectList::resetObjectBeacons() mDebugBeacons.clear(); } -LLViewerObject *LLViewerObjectList::createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp) +LLViewerObject *LLViewerObjectList::createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp, S32 flags) { LLUUID fullid; fullid.generate(); - LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp); + LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp, flags); if (!objectp) { // LL_WARNS() << "Couldn't create object of type " << LLPrimitive::pCodeToString(pcode) << LL_ENDL; diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index 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/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 3f479802aa..28a81981a2 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2861,6 +2861,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"); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index eae8f2cc56..bc5cca4a65 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -44,6 +44,7 @@ #include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" #include "llavatarrendernotifier.h" +#include "llcontrolavatar.h" #include "llexperiencecache.h" #include "llphysicsmotion.h" #include "llviewercontrol.h" @@ -109,6 +110,8 @@ #include "llcallstack.h" #include "llrendersphere.h" +#include <boost/lexical_cast.hpp> + extern F32 SPEED_ADJUST_MAX; extern F32 SPEED_ADJUST_MAX_SEC; extern F32 ANIM_SPEED_MAX; @@ -142,7 +145,7 @@ const LLUUID ANIM_AGENT_PHYSICS_MOTION = LLUUID("7360e029-3cb8-ebc4-863e-212df44 //----------------------------------------------------------------------------- // Constants //----------------------------------------------------------------------------- -const F32 DELTA_TIME_MIN = 0.01f; // we clamp measured deltaTime to this +const F32 DELTA_TIME_MIN = 0.01f; // we clamp measured delta_time to this const F32 DELTA_TIME_MAX = 0.2f; // range to insure stability of computations. const F32 PELVIS_LAG_FLYING = 0.22f;// pelvis follow half life while flying @@ -617,6 +620,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, LLViewerObject(id, pcode, regionp), mSpecialRenderMode(0), mAttachmentSurfaceArea(0.f), + mAttachmentVisibleTriangleCount(0), + mAttachmentEstTriangleCount(0.f), mReportedVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN), mTurning(FALSE), mLastSkeletonSerialNum( 0 ), @@ -663,7 +668,9 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mLastUpdateRequestCOFVersion(-1), mLastUpdateReceivedCOFVersion(-1), mCachedMuteListUpdateTime(0), - mCachedInMuteList(false) + mCachedInMuteList(false), + mIsControlAvatar(false), + mEnableDefaultMotions(true) { LL_DEBUGS("AvatarRender") << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << LL_ENDL; @@ -718,6 +725,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mCurrentGesticulationLevel = 0; + mRuthTimer.reset(); mRuthDebugTimer.reset(); mDebugExistenceTimer.reset(); @@ -1099,7 +1107,7 @@ void LLVOAvatar::cleanupClass() } // virtual -void LLVOAvatar::initInstance(void) +void LLVOAvatar::initInstance() { //------------------------------------------------------------------------- // register motions @@ -1222,8 +1230,6 @@ const LLVector3 LLVOAvatar::getRenderPosition() const { return getPosition() * mDrawable->getParent()->getRenderMatrix(); } - - } void LLVOAvatar::updateDrawable(BOOL force_damped) @@ -1240,6 +1246,10 @@ void LLVOAvatar::onShift(const LLVector4a& shift_vector) void LLVOAvatar::updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax) { + if (mDrawable.isNull()) + { + return; + } if (isImpostor() && !needsImpostorUpdate()) { LLVector3 delta = getRenderPosition() - @@ -1404,13 +1414,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 +1448,6 @@ void LLVOAvatar::renderCollisionVolumes() mNameText->lineSegmentIntersect(unused, unused, unused, TRUE); } - - mDebugText.clear(); - addDebugText(ostr.str()); } void LLVOAvatar::renderBones() @@ -1595,6 +1618,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 +1708,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 +1825,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 @@ -1942,7 +1975,8 @@ void LLVOAvatar::resetSkeleton(bool reset_animations) //----------------------------------------------------------------------------- void LLVOAvatar::releaseMeshData() { - if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || mIsDummy) + if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || + (mIsDummy && !isControlAvatar())) { return; } @@ -1962,15 +1996,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 +2028,10 @@ void LLVOAvatar::releaseMeshData() void LLVOAvatar::restoreMeshData() { llassert(!isSelf()); + if (mDrawable.isNull()) + { + return; + } //LL_INFOS() << "Restoring" << LL_ENDL; mMeshValid = TRUE; @@ -2303,7 +2341,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 +2507,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) // (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing) //-------------------------------------------------------------------------------------------- - if ( mIsSitting ) + if ( isSitting() ) { LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] ); mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot->getWorldPosition() + headOffset ); @@ -2593,13 +2631,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 +2801,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()); } @@ -3360,8 +3402,7 @@ bool LLVOAvatar::isInMuteList() void LLVOAvatar::updateDebugText() { - // clear debug text - mDebugText.clear(); + // Leave mDebugText uncleared here, in case a derived class has added some state first if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) { @@ -3406,7 +3447,7 @@ void LLVOAvatar::updateDebugText() 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", (isSitting() ? "S" : "T")); debug_line += llformat("%s", (isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED) ? "G" : "-")); } LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition(); @@ -3422,12 +3463,14 @@ void LLVOAvatar::updateDebugText() addDebugText(debug_line); } + 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(); @@ -3437,8 +3480,27 @@ void LLVOAvatar::updateDebugText() if (motionp->getMinPixelArea() < getPixelArea()) { std::string output; - if (motionp->getName().empty()) + 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; + if (volp) + { + volp->requestInventory(); + LLViewerInventoryItem* item = volp->getInventoryItemByAsset(motionp->getID()); + if (item) + { + motion_name = item->getName(); + } + } + } + } + if (motion_name.empty()) + { std::string name; if (gAgent.isGodlikeWithoutAdminMenuFakery() || isSelf()) { @@ -3492,7 +3554,6 @@ void LLVOAvatar::updateDebugText() { name = LLUUID::null.asString(); } - output = llformat("%s - %d", name.c_str(), (U32)motionp->getPriority()); @@ -3500,8 +3561,8 @@ void LLVOAvatar::updateDebugText() else { output = llformat("%s - %d", - motionp->getName().c_str(), - (U32)motionp->getPriority()); + motion_name.c_str(), + (U32)motionp->getPriority()); } addDebugText(output); } @@ -3517,51 +3578,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) + && !mIsDummy + && 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 +3720,215 @@ 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() +{ + bool is_pure_dummy = mIsDummy && !isControlAvatar(); + if (!isSelf() && !is_pure_dummy) // ie, non-self avatars, and animated objects will be affected. { + // AXON 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 +3942,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 +3951,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 +3972,8 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) root_pos = gAgent.getPosGlobalFromAgent(getRenderPosition()); root_pos.mdV[VZ] += getVisualParamWeight(AVATAR_HOVER); - + // AXON need to review mInAir calcs for animated objects, if the value even matters. + 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 +3995,156 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) // correct for the fact that the pelvis is not necessarily the center // of the agent's physical representation root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot; - if (!mIsSitting && !was_sit_ground_constrained) + if (!isSitting() && !was_sit_ground_constrained) { root_pos += LLVector3d(getHoverOffset()); } - - LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos); - - - if (newPosition != mRoot->getXform()->getWorldPosition()) - { - mRoot->touch(); - // SL-315 - mRoot->setWorldPosition( newPosition ); // regular update - } + LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this); + if (cav) + { + cav->matchVolumeTransform(); + } + else + { + LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos); + if (newPosition != mRoot->getXform()->getWorldPosition()) + { + mRoot->touch(); + // SL-315 + mRoot->setWorldPosition( newPosition ); // regular update + } + } //-------------------------------------------------------------------- // Propagate viewer object rotation to root of avatar //-------------------------------------------------------------------- - if (!isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS)) + if (!isControlAvatar() && !isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS)) { - LLQuaternion iQ; - LLVector3 upDir( 0.0f, 0.0f, 1.0f ); - - // Compute a forward direction vector derived from the primitive rotation - // and the velocity vector. When walking or jumping, don't let body deviate - // more than 90 from the view, if necessary, flip the velocity vector. - - LLVector3 primDir; - if (isSelf()) - { - primDir = agent.getAtAxis() - projected_vec(agent.getAtAxis(), agent.getReferenceUpVector()); - primDir.normalize(); - } - else - { - primDir = getRotation().getMatrix3().getFwdRow(); - } - LLVector3 velDir = getVelocity(); - velDir.normalize(); - if ( mSignaledAnimations.find(ANIM_AGENT_WALK) != mSignaledAnimations.end()) - { - F32 vpD = velDir * primDir; - if (vpD < -0.5f) - { - velDir *= -1.0f; - } - } - LLVector3 fwdDir = lerp(primDir, velDir, clamp_rescale(speed, 0.5f, 2.0f, 0.0f, 1.0f)); - if (isSelf() && gAgentCamera.cameraMouselook()) - { - // make sure fwdDir stays in same general direction as primdir - if (gAgent.getFlying()) - { - fwdDir = LLViewerCamera::getInstance()->getAtAxis(); - } - else - { - LLVector3 at_axis = LLViewerCamera::getInstance()->getAtAxis(); - LLVector3 up_vector = gAgent.getReferenceUpVector(); - at_axis -= up_vector * (at_axis * up_vector); - at_axis.normalize(); - - F32 dot = fwdDir * at_axis; - if (dot < 0.f) - { - fwdDir -= 2.f * at_axis * dot; - fwdDir.normalize(); - } - } - } - - LLQuaternion root_rotation = mRoot->getWorldMatrix().quaternion(); - F32 root_roll, root_pitch, root_yaw; - root_rotation.getEulerAngles(&root_roll, &root_pitch, &root_yaw); - - // When moving very slow, the pelvis is allowed to deviate from the - // forward direction to allow it to hold it's position while the torso - // and head turn. Once in motion, it must conform however. - BOOL self_in_mouselook = isSelf() && gAgentCamera.cameraMouselook(); - - LLVector3 pelvisDir( mRoot->getWorldMatrix().getFwdRow4().mV ); - - static LLCachedControl<F32> s_pelvis_rot_threshold_slow(gSavedSettings, "AvatarRotateThresholdSlow", 60.0); - static LLCachedControl<F32> s_pelvis_rot_threshold_fast(gSavedSettings, "AvatarRotateThresholdFast", 2.0); - - F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, s_pelvis_rot_threshold_slow, s_pelvis_rot_threshold_fast); - - if (self_in_mouselook) - { - pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR; - } - pelvis_rot_threshold *= DEG_TO_RAD; - - F32 angle = angle_between( pelvisDir, fwdDir ); - - // The avatar's root is allowed to have a yaw that deviates widely - // from the forward direction, but if roll or pitch are off even - // a little bit we need to correct the rotation. - if(root_roll < 1.f * DEG_TO_RAD - && root_pitch < 5.f * DEG_TO_RAD) - { - // smaller correction vector means pelvis follows prim direction more closely - if (!mTurning && angle > pelvis_rot_threshold*0.75f) - { - mTurning = TRUE; - } - - // use tighter threshold when turning - if (mTurning) - { - pelvis_rot_threshold *= 0.4f; - } - - // am I done turning? - if (angle < pelvis_rot_threshold) - { - mTurning = FALSE; - } - - LLVector3 correction_vector = (pelvisDir - fwdDir) * clamp_rescale(angle, pelvis_rot_threshold*0.75f, pelvis_rot_threshold, 1.0f, 0.0f); - fwdDir += correction_vector; - } - else - { - mTurning = FALSE; - } - - // Now compute the full world space rotation for the whole body (wQv) - LLVector3 leftDir = upDir % fwdDir; - leftDir.normalize(); - fwdDir = leftDir % upDir; - LLQuaternion wQv( fwdDir, leftDir, upDir ); - - if (isSelf() && mTurning) - { - if ((fwdDir % pelvisDir) * upDir > 0.f) - { - gAgent.setControlFlags(AGENT_CONTROL_TURN_RIGHT); - } - else - { - gAgent.setControlFlags(AGENT_CONTROL_TURN_LEFT); - } - } - - // Set the root rotation, but do so incrementally so that it - // lags in time by some fixed amount. - //F32 u = LLSmoothInterpolation::getInterpolant(PELVIS_LAG); - F32 pelvis_lag_time = 0.f; - if (self_in_mouselook) - { - pelvis_lag_time = PELVIS_LAG_MOUSELOOK; - } - else if (mInAir) - { - pelvis_lag_time = PELVIS_LAG_FLYING; - // increase pelvis lag time when moving slowly - pelvis_lag_time *= clamp_rescale(mSpeedAccum, 0.f, 15.f, 3.f, 1.f); - } - else - { - pelvis_lag_time = PELVIS_LAG_WALKING; - } - - F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f); - - mRoot->setWorldRotation( slerp(u, mRoot->getWorldRotation(), wQv) ); - + // AXON - should we always skip for control avatars? + // Rotation fixups for avatars in motion, some may be + // relevant. + 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 "dummy" avatar used in some areas of the UI, such as +// when previewing uploaded animations. Class is LLVOAvatar, 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 + // AXON how should control avs be handled here? + //-------------------------------------------------------------------- + // 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 +4163,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 +4180,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 +4206,7 @@ void LLVOAvatar::updateHeadOffset() { midEyePt = midEyePt * ~mDrawable->getWorldRotation(); } - if (mIsSitting) + if (isSitting()) { mHeadOffset = midEyePt; } @@ -4115,7 +4302,7 @@ void LLVOAvatar::updateVisibility() if (mIsDummy) { - visible = TRUE; + visible = FALSE; } else if (mDrawable.isNull()) { @@ -4229,7 +4416,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 +4452,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); @@ -4426,11 +4619,12 @@ U32 LLVOAvatar::renderSkinned() } BOOL first_pass = TRUE; + bool is_pure_dummy = mIsDummy && !isControlAvatar(); if (!LLDrawPoolAvatar::sSkipOpaque) { if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender) { - if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy) + if (isTextureVisible(TEX_HEAD_BAKED) || is_pure_dummy) { LLViewerJoint* head_mesh = getViewerJoint(MESH_ID_HEAD); if (head_mesh) @@ -4440,7 +4634,7 @@ U32 LLVOAvatar::renderSkinned() first_pass = FALSE; } } - if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy) + if (isTextureVisible(TEX_UPPER_BAKED) || is_pure_dummy) { LLViewerJoint* upper_mesh = getViewerJoint(MESH_ID_UPPER_BODY); if (upper_mesh) @@ -4450,7 +4644,7 @@ U32 LLVOAvatar::renderSkinned() first_pass = FALSE; } - if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy) + if (isTextureVisible(TEX_LOWER_BAKED) || is_pure_dummy) { LLViewerJoint* lower_mesh = getViewerJoint(MESH_ID_LOWER_BODY); if (lower_mesh) @@ -4507,18 +4701,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 +4749,9 @@ U32 LLVOAvatar::renderRigid() gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); } - if (isTextureVisible(TEX_EYES_BAKED) || mIsDummy) + bool is_pure_dummy = mIsDummy && !isControlAvatar(); + + if (isTextureVisible(TEX_EYES_BAKED) || is_pure_dummy) { LLViewerJoint* eyeball_left = getViewerJoint(MESH_ID_EYEBALL_LEFT); LLViewerJoint* eyeball_right = getViewerJoint(MESH_ID_EYEBALL_RIGHT); @@ -5105,10 +5298,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 +5314,19 @@ void LLVOAvatar::processAnimationStateChanges() if ( isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) ) { - startMotion(ANIM_AGENT_TARGET); + if (mEnableDefaultMotions) + { + startMotion(ANIM_AGENT_TARGET); + } stopMotion(ANIM_AGENT_BODY_NOISE); } else { stopMotion(ANIM_AGENT_TARGET); - startMotion(ANIM_AGENT_BODY_NOISE); + if (mEnableDefaultMotions) + { + startMotion(ANIM_AGENT_BODY_NOISE); + } } // clear all current animations @@ -5479,14 +5681,14 @@ bool LLVOAvatar::getRiggedMeshID(LLViewerObject* pVO, LLUUID& mesh_id) LLVOVolume* pVObj = pVO->mDrawable->getVOVolume(); if ( pVObj ) { - const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID(), pVObj ); + const LLMeshSkinInfo* pSkinData = pVObj->getSkinInfo(); if (pSkinData && pSkinData->mJointNames.size() > JOINT_COUNT_REQUIRED_FOR_FULLRIG // full rig && pSkinData->mAlternateBindMatrix.size() > 0 ) - { - mesh_id = pSkinData->mMeshID; - return true; - } + { + mesh_id = pSkinData->mMeshID; + return true; + } } } return false; @@ -5533,8 +5735,7 @@ bool LLVOAvatar::jointIsRiggedTo(const std::string& joint_name, const LLViewerOb return false; } - LLUUID currentId = vobj->getVolume()->getParams().getSculptID(); - const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( currentId, vobj ); + const LLMeshSkinInfo* pSkinData = vobj->getSkinInfo(); if ( vobj && vobj->isAttachment() && vobj->isMesh() && pSkinData ) { @@ -5570,7 +5771,18 @@ void LLVOAvatar::rebuildAttachmentOverrides() { LLScopedContextString str("rebuildAttachmentOverrides " + getFullname()); - // Attachment points + // 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) + { + addAttachmentOverridesForObject(volp); + } + } + + // Attached objects for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) @@ -5581,24 +5793,30 @@ 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) { - LLVOAvatar *av = vo->getAvatarAncestor(); - if (!av || (av != this)) - { + if (vo->getAvatar() != this && vo->getAvatarAncestor() != this) + { LL_WARNS("Avatar") << "called with invalid avatar" << LL_ENDL; return; - } + } - LLScopedContextString str("addAttachmentOverridesForObject " + av->getFullname()); + LLScopedContextString str("addAttachmentOverridesForObject " + vo->getAvatar()->getFullname()); // Process all children LLViewerObject::const_child_list_t& children = vo->getChildren(); @@ -5621,10 +5839,9 @@ void LLVOAvatar::addAttachmentOverridesForObject(LLViewerObject *vo) { 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(); @@ -5806,14 +6023,14 @@ void LLVOAvatar::showAttachmentOverrides(bool verbose) const } //----------------------------------------------------------------------------- -// resetJointsOnDetach +// removeAttachmentOverridesForObject //----------------------------------------------------------------------------- -void LLVOAvatar::resetJointsOnDetach(LLViewerObject *vo) +void LLVOAvatar::removeAttachmentOverridesForObject(LLViewerObject *vo) { - LLVOAvatar *av = vo->getAvatarAncestor(); - if (!av || (av != this)) + if (vo->getAvatar() != this && vo->getAvatarAncestor() != this) { LL_WARNS("Avatar") << "called with invalid avatar" << LL_ENDL; + return; } // Process all children @@ -5822,21 +6039,21 @@ void LLVOAvatar::resetJointsOnDetach(LLViewerObject *vo) it != children.end(); ++it) { LLViewerObject *childp = *it; - resetJointsOnDetach(childp); + removeAttachmentOverridesForObject(childp); } // Process self. LLUUID mesh_id; if (getRiggedMeshID(vo,mesh_id)) { - resetJointsOnDetach(mesh_id); + removeAttachmentOverridesForObject(mesh_id); } } //----------------------------------------------------------------------------- -// resetJointsOnDetach +// removeAttachmentOverridesForObject //----------------------------------------------------------------------------- -void LLVOAvatar::resetJointsOnDetach(const LLUUID& mesh_id) +void LLVOAvatar::removeAttachmentOverridesForObject(const LLUUID& mesh_id) { //Subsequent joints are relative to pelvis avatar_joint_list_t::iterator iter = mSkeleton.begin(); @@ -5914,6 +6131,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; + // AXON UPDATE FOR CONTROL AVS? if (mIsDummy) { outNorm.setVec(z_vec); @@ -5943,6 +6161,7 @@ F32 LLVOAvatar::getTimeDilation() //----------------------------------------------------------------------------- F32 LLVOAvatar::getPixelArea() const { + // AXON UPDATE FOR CONTROL AVATARS if (mIsDummy) { return 100000.f; @@ -6358,7 +6577,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. @@ -6450,19 +6669,63 @@ U32 LLVOAvatar::getNumAttachments() const //----------------------------------------------------------------------------- // canAttachMoreObjects() +// Returns true if we can attach <n> more objects. //----------------------------------------------------------------------------- -BOOL LLVOAvatar::canAttachMoreObjects() const +BOOL LLVOAvatar::canAttachMoreObjects(U32 n) const { - return (getNumAttachments() < MAX_AGENT_ATTACHMENTS); + return (getNumAttachments() + n) <= MAX_AGENT_ATTACHMENTS; } //----------------------------------------------------------------------------- -// canAttachMoreObjects() -// Returns true if we can attach <n> more objects. +// getNumAnimatedObjectAttachments() //----------------------------------------------------------------------------- -BOOL LLVOAvatar::canAttachMoreObjects(U32 n) const +U32 LLVOAvatar::getNumAnimatedObjectAttachments() const { - return (getNumAttachments() + n) <= MAX_AGENT_ATTACHMENTS; + U32 num_attachments = 0; + for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); + ++iter) + { + const LLViewerJointAttachment *attachment_pt = (*iter).second; + num_attachments += attachment_pt->getNumAnimatedObjects(); + } + return num_attachments; +} + +//----------------------------------------------------------------------------- +// getMaxAnimatedObjectAttachments() +// Gets from simulator feature if available, otherwise 0. +//----------------------------------------------------------------------------- +S32 LLVOAvatar::getMaxAnimatedObjectAttachments() const +{ + S32 max_attach = 0; + // AXON REMOVE AFTER SERVER TESTING DONE + if (gSavedSettings.getBOOL("AnimatedObjectsIgnoreLimits")) + { + max_attach = MAX_AGENT_ATTACHMENTS; + } + else + { + if (gAgent.getRegion()) + { + LLSD features; + gAgent.getRegion()->getSimulatorFeatures(features); + if (features.has("AnimatedObjects")) + { + max_attach = features["AnimatedObjects"]["MaxAgentAnimatedObjectAttachments"].asInteger(); + } + } + } + return max_attach; +} + +//----------------------------------------------------------------------------- +// canAttachMoreAnimatedObjects() +// Returns true if we can attach <n> more animated objects. +//----------------------------------------------------------------------------- +BOOL LLVOAvatar::canAttachMoreAnimatedObjects(U32 n) const +{ + return (getNumAnimatedObjectAttachments() + n) <= getMaxAnimatedObjectAttachments(); } //----------------------------------------------------------------------------- @@ -6553,7 +6816,7 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) LLUUID mesh_id; if (getRiggedMeshID(pVO, mesh_id)) { - resetJointsOnDetach(mesh_id); + removeAttachmentOverridesForObject(mesh_id); if ( gAgentCamera.cameraCustomizeAvatar() ) { gAgent.unpauseAnimation(); @@ -6714,7 +6977,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()) { @@ -6874,6 +7140,7 @@ void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color) BOOL LLVOAvatar::isVisible() const { + // AXON should we flag control avs as invisible? return mDrawable.notNull() && (!mOrphaned || isSelf()) && (mDrawable->isVisible() || mIsDummy); @@ -6882,6 +7149,11 @@ BOOL LLVOAvatar::isVisible() const // Determine if we have enough avatar data to render bool LLVOAvatar::getIsCloud() const { + if (mIsDummy) + { + return false; + } + return ( ((const_cast<LLVOAvatar*>(this))->visualParamWeightsAreDefault())// Do we have a shape? || ( !isTextureDefined(TEX_LOWER_BAKED) || !isTextureDefined(TEX_UPPER_BAKED) @@ -7161,9 +7433,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; @@ -8846,6 +9118,11 @@ void LLVOAvatar::updateFreezeCounter(S32 counter) BOOL LLVOAvatar::updateLOD() { + if (mDrawable.isNull()) + { + return FALSE; + } + if (isImpostor() && 0 != mDrawable->getNumFaces() && mDrawable->getFace(0)->hasGeometry()) { return TRUE; @@ -8886,10 +9163,10 @@ U32 LLVOAvatar::getPartitionType() const void LLVOAvatar::updateImpostors() { LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD; - LLCharacter::sAllowInstancesChange = FALSE; - for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin(); - iter != LLCharacter::sInstances.end(); ++iter) + std::vector<LLCharacter*> instances_copy = LLCharacter::sInstances; + for (std::vector<LLCharacter*>::iterator iter = instances_copy.begin(); + iter != instances_copy.end(); ++iter) { LLVOAvatar* avatar = (LLVOAvatar*) *iter; if (!avatar->isDead() && avatar->isVisible() @@ -8986,6 +9263,17 @@ void LLVOAvatar::updateImpostorRendering(U32 newMaxNonImpostorsValue) void LLVOAvatar::idleUpdateRenderComplexity() { + if (isControlAvatar()) + { + LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(this); + bool is_attachment = cav && cav->mRootVolp && cav->mRootVolp->isAttachment(); // For attached animated objects + if (is_attachment) + { + // ARC for animated object attachments is accounted with the avatar they're attached to. + return; + } + } + // Render Complexity calculateUpdateRenderComplexity(); // Update mVisualComplexity if needed @@ -9033,10 +9321,16 @@ void LLVOAvatar::idleUpdateRenderComplexity() // Visual rank info_line = llformat("%d rank", mVisibilityRank); // Use grey for imposters, white for normal rendering or no impostors - info_color.set(isImpostor() ? LLColor4::grey : LLColor4::white); + info_color.set(isImpostor() ? LLColor4::grey : (isControlAvatar() ? LLColor4::yellow : LLColor4::white)); info_style = LLFontGL::NORMAL; mText->addLine(info_line, info_color, info_style); + // Triangle count + mText->addLine(std::string("VisTris ") + LLStringOps::getReadableNumber(mAttachmentVisibleTriangleCount), + info_color, info_style); + mText->addLine(std::string("EstMaxTris ") + LLStringOps::getReadableNumber(mAttachmentEstTriangleCount), + info_color, info_style); + // Attachment Surface Area static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit", 1000.0f); info_line = llformat("%.0f m^2", mAttachmentSurfaceArea); @@ -9055,22 +9349,13 @@ void LLVOAvatar::idleUpdateRenderComplexity() info_color.set(LLColor4::grey); info_style = LLFontGL::NORMAL; } + mText->addLine(info_line, info_color, info_style); updateText(); // corrects position } } -void LLVOAvatar::addAttachmentArea(F32 delta_area) -{ - mAttachmentSurfaceArea += delta_area; -} - -void LLVOAvatar::subtractAttachmentArea(F32 delta_area) -{ - mAttachmentSurfaceArea = delta_area > mAttachmentSurfaceArea ? 0.0 : mAttachmentSurfaceArea - delta_area; -} - void LLVOAvatar::updateVisualComplexity() { LL_DEBUGS("AvatarRender") << "avatar " << getID() << " appearance changed" << LL_ENDL; @@ -9078,6 +9363,136 @@ void LLVOAvatar::updateVisualComplexity() mVisualComplexityStale = true; } +// Account for the complexity of a single top-level object associated +// with an avatar. This will be either an attached object or an animated +// object. +void LLVOAvatar::accountRenderComplexityForObject( + const LLViewerObject *attached_object, + const F32 max_attachment_complexity, + LLVOVolume::texture_cost_t& textures, + U32& cost, + hud_complexity_list_t& hud_complexity_list) +{ + if (attached_object && !attached_object->isHUDAttachment()) + { + mAttachmentVisibleTriangleCount += attached_object->recursiveGetTriangleCount(); + mAttachmentEstTriangleCount += attached_object->recursiveGetEstTrianglesMax(); + mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea(); + + textures.clear(); + const LLDrawable* drawable = attached_object->mDrawable; + if (drawable) + { + const LLVOVolume* volume = drawable->getVOVolume(); + if (volume) + { + F32 attachment_total_cost = 0; + F32 attachment_volume_cost = 0; + F32 attachment_texture_cost = 0; + F32 attachment_children_cost = 0; + // AXON placeholder value, will revisit in testing. + const F32 animated_object_attachment_surcharge = 20000; + + if (attached_object->isAnimatedObject()) + { + attachment_volume_cost += animated_object_attachment_surcharge; + } + attachment_volume_cost += volume->getRenderCost(textures); + + const_child_list_t children = volume->getChildren(); + for (const_child_list_t::const_iterator child_iter = children.begin(); + child_iter != children.end(); + ++child_iter) + { + LLViewerObject* child_obj = *child_iter; + LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj ); + if (child) + { + attachment_children_cost += child->getRenderCost(textures); + } + } + + for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin(); + volume_texture != textures.end(); + ++volume_texture) + { + // add the cost of each individual texture in the linkset + attachment_texture_cost += volume_texture->second; + } + attachment_total_cost = attachment_volume_cost + attachment_texture_cost + attachment_children_cost; + LL_DEBUGS("ARCdetail") << "Attachment costs " << attached_object->getAttachmentItemID() + << " total: " << attachment_total_cost + << ", volume: " << attachment_volume_cost + << ", textures: " << attachment_texture_cost + << ", " << volume->numChildren() + << " children: " << attachment_children_cost + << LL_ENDL; + // Limit attachment complexity to avoid signed integer flipping of the wearer's ACI + cost += (U32)llclamp(attachment_total_cost, MIN_ATTACHMENT_COMPLEXITY, max_attachment_complexity); + } + } + } + if (isSelf() + && attached_object + && attached_object->isHUDAttachment() + && !attached_object->isTempAttachment() + && attached_object->mDrawable) + { + textures.clear(); + + mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea(); + + const LLVOVolume* volume = attached_object->mDrawable->getVOVolume(); + if (volume) + { + LLHUDComplexity hud_object_complexity; + hud_object_complexity.objectName = attached_object->getAttachmentItemName(); + hud_object_complexity.objectId = attached_object->getAttachmentItemID(); + std::string joint_name; + gAgentAvatarp->getAttachedPointName(attached_object->getAttachmentItemID(), joint_name); + hud_object_complexity.jointName = joint_name; + // get cost and individual textures + hud_object_complexity.objectsCost += volume->getRenderCost(textures); + hud_object_complexity.objectsCount++; + + LLViewerObject::const_child_list_t& child_list = attached_object->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); ++iter) + { + LLViewerObject* childp = *iter; + const LLVOVolume* chld_volume = dynamic_cast<LLVOVolume*>(childp); + if (chld_volume) + { + // get cost and individual textures + hud_object_complexity.objectsCost += chld_volume->getRenderCost(textures); + hud_object_complexity.objectsCount++; + } + } + + hud_object_complexity.texturesCount += textures.size(); + + for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin(); + volume_texture != textures.end(); + ++volume_texture) + { + // add the cost of each individual texture (ignores duplicates) + hud_object_complexity.texturesCost += volume_texture->second; + LLViewerFetchedTexture *tex = LLViewerTextureManager::getFetchedTexture(volume_texture->first); + if (tex) + { + // Note: Texture memory might be incorect since texture might be still loading. + hud_object_complexity.texturesMemoryTotal += tex->getTextureMemory(); + if (tex->getOriginalHeight() * tex->getOriginalWidth() >= HUD_OVERSIZED_TEXTURE_DATA_SIZE) + { + hud_object_complexity.largeTexturesCount++; + } + } + } + hud_complexity_list.push_back(hud_object_complexity); + } + } +} + // Calculations for mVisualComplexity value void LLVOAvatar::calculateUpdateRenderComplexity() { @@ -9116,7 +9531,25 @@ void LLVOAvatar::calculateUpdateRenderComplexity() } LL_DEBUGS("ARCdetail") << "Avatar body parts complexity: " << cost << LL_ENDL; + mAttachmentVisibleTriangleCount = 0; + mAttachmentEstTriangleCount = 0.f; + mAttachmentSurfaceArea = 0.f; + + // A standalone animated object needs to be accounted for + // using its associated volume. Attached animated objects + // will be covered by the subsequent loop over attachments. + LLControlAvatar *control_av = dynamic_cast<LLControlAvatar*>(this); + if (control_av) + { + LLVOVolume *volp = control_av->mRootVolp; + if (volp && !volp->isAttachment()) + { + accountRenderComplexityForObject(volp, max_attachment_complexity, + textures, cost, hud_complexity_list); + } + } + // Account for complexity of all attachments. for (attachment_map_t::const_iterator attachment_point = mAttachmentPoints.begin(); attachment_point != mAttachmentPoints.end(); ++attachment_point) @@ -9127,112 +9560,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..730373d79c 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -50,6 +50,8 @@ #include "llviewertexlayer.h" #include "material_codes.h" // LL_MCODE_END #include "llviewerstats.h" +#include "llvovolume.h" +#include "llavatarrendernotifier.h" extern const LLUUID ANIM_AGENT_BODY_NOISE; extern const LLUUID ANIM_AGENT_BREATHE_ROT; @@ -169,7 +171,8 @@ public: LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point LLVector4a* normal = NULL, // return the surface normal at the intersection point LLVector4a* tangent = NULL); // return the surface tangent at the intersection point - LLViewerObject* lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end, + virtual LLViewerObject* lineSegmentIntersectRiggedAttachments( + const LLVector4a& start, const LLVector4a& end, S32 face = -1, // which face to check, -1 = ALL_SIDES BOOL pick_transparent = FALSE, BOOL pick_rigged = FALSE, @@ -202,8 +205,8 @@ public: LLJoint* getJoint(S32 num); void addAttachmentOverridesForObject(LLViewerObject *vo); - void resetJointsOnDetach(const LLUUID& mesh_id); - void resetJointsOnDetach(LLViewerObject *vo); + void removeAttachmentOverridesForObject(const LLUUID& mesh_id); + void removeAttachmentOverridesForObject(LLViewerObject *vo); bool jointIsRiggedTo(const std::string& joint_name); bool jointIsRiggedTo(const std::string& joint_name, const LLViewerObject *vo); void clearAttachmentOverrides(); @@ -233,6 +236,8 @@ 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) + private: //aligned members LL_ALIGN_16(LLVector4a mImpostorExtents[2]); @@ -240,8 +245,14 @@ private: //aligned members // Updates //-------------------------------------------------------------------- public: - void updateDebugText(); + 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 +270,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 +436,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 +457,13 @@ public: VisualMuteSettings mVisuallyMuteSetting; // Always or never visually mute this AV //-------------------------------------------------------------------- + // animated object status + //-------------------------------------------------------------------- +public: + bool mIsControlAvatar; + bool mEnableDefaultMotions; + + //-------------------------------------------------------------------- // Morph masks //-------------------------------------------------------------------- public: @@ -739,9 +762,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 +784,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 ** ** diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 10af524ee7..a2144d6466 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -2791,7 +2791,7 @@ BOOL LLVOAvatarSelf::needsRenderBeam() // don't render selection beam on hud objects is_touching_or_grabbing = FALSE; } - return is_touching_or_grabbing || (mState & AGENT_STATE_EDITING && LLSelectMgr::getInstance()->shouldShowSelection()); + return is_touching_or_grabbing || (getAttachmentState() & AGENT_STATE_EDITING && LLSelectMgr::getInstance()->shouldShowSelection()); } // static diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp index de63a3963c..5def0f0c0f 100644 --- a/indra/newview/llvograss.cpp +++ b/indra/newview/llvograss.cpp @@ -92,7 +92,7 @@ LLVOGrass::~LLVOGrass() void LLVOGrass::updateSpecies() { - mSpecies = mState; + mSpecies = getAttachmentState(); if (!sSpeciesTable.count(mSpecies)) { diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index f77b48ff80..fbbf0634ff 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -76,8 +76,13 @@ #include "lldatapacker.h" #include "llviewershadermgr.h" #include "llvoavatar.h" +#include "llcontrolavatar.h" +#include "llvoavatarself.h" #include "llvocache.h" #include "llmaterialmgr.h" +#include "llanimationstates.h" +#include "llinventorytype.h" +#include "llviewerinventory.h" const F32 FORCE_SIMPLE_RENDER_AREA = 512.f; const F32 FORCE_CULL_AREA = 8.f; @@ -86,6 +91,7 @@ U32 JOINT_COUNT_REQUIRED_FOR_FULLRIG = 1; BOOL gAnimateTextures = TRUE; //extern BOOL gHideSelectedObjects; +S32 LLVOVolume::sForceLOD = -1; F32 LLVOVolume::sLODFactor = 1.f; F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop F32 LLVOVolume::sDistanceFactor = 1.0f; @@ -1096,16 +1102,26 @@ 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(); - } + updateVisualComplexity(); } // sculpt replaces generate() for sculpted surfaces @@ -1217,16 +1233,24 @@ void LLVOVolume::sculpt() S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius) { S32 cur_detail; - if (LLPipeline::sDynamicLOD) - { - // We've got LOD in the profile, and in the twist. Use radius. - F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance; - cur_detail = LLVolumeLODGroup::getDetailFromTan(ll_round(tan_angle, 0.01f)); - } - else - { - cur_detail = llclamp((S32) (sqrtf(radius)*LLVOVolume::sLODFactor*4.f), 0, 3); - } + // AXON TEMP REMOVE + if (LLVOVolume::sForceLOD>=0 && LLVOVolume::sForceLOD<=3) + { + cur_detail = LLVOVolume::sForceLOD; + } + else + { + if (LLPipeline::sDynamicLOD) + { + // We've got LOD in the profile, and in the twist. Use radius. + F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance; + cur_detail = LLVolumeLODGroup::getDetailFromTan(ll_round(tan_angle, 0.01f)); + } + else + { + cur_detail = llclamp((S32) (sqrtf(radius)*LLVOVolume::sLODFactor*4.f), 0, 3); + } + } return cur_detail; } @@ -1280,22 +1304,34 @@ BOOL LLVOVolume::calcLOD() distance *= F_PI/3.f; cur_detail = computeLODDetail(ll_round(distance, 0.01f), - ll_round(radius, 0.01f)); - + ll_round(radius, 0.01f)); + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TRIANGLE_COUNT) && mDrawable->getFace(0)) + { + if (isRootEdit() && getChildren().size()>0) + { + S32 total_tris = recursiveGetTriangleCount(); + setDebugText(llformat("TRIS %d TOTAL %d", getTriangleCount(), total_tris)); + } + else + { + setDebugText(llformat("TRIS %d", getTriangleCount())); + } + + } 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) { mAppAngle = ll_round((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f); mLOD = cur_detail; - return TRUE; + + return TRUE; } return FALSE; @@ -1312,6 +1348,15 @@ BOOL LLVOVolume::updateLOD() if (lod_changed) { +#if 1 + // AXON debugging + if (isAnimatedObject() && isRiggedMesh()) + { + std::string vobj_name = llformat("Vol%u", (U32) this); + F32 est_tris = getEstTrianglesMax(); + LL_DEBUGS("AXONLinkset") << vobj_name << " updateLOD to " << getLOD() << ", tris " << est_tris << LL_ENDL; + } +#endif gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE); mLODChanged = TRUE; } @@ -1386,7 +1431,8 @@ void LLVOVolume::updateFaceFlags() BOOL LLVOVolume::setParent(LLViewerObject* parent) { BOOL ret = FALSE ; - if (parent != getParent()) + LLViewerObject *old_parent = (LLViewerObject*) getParent(); + if (parent != old_parent) { ret = LLViewerObject::setParent(parent); if (ret && mDrawable) @@ -1394,6 +1440,7 @@ BOOL LLVOVolume::setParent(LLViewerObject* parent) gPipeline.markMoved(mDrawable); gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); } + updateAnimatedObjectStateOnReparent(old_parent, parent); } return ret ; @@ -1651,6 +1698,11 @@ bool LLVOVolume::lodOrSculptChanged(LLDrawable *drawable, BOOL &compiled) if ((new_lod != old_lod) || mSculptChanged) { + if (mDrawable->isState(LLDrawable::RIGGED)) + { + updateVisualComplexity(); + } + compiled = TRUE; sNumLODChanges += new_num_faces; @@ -3269,6 +3321,152 @@ 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) +{ + // AXON - the check against isAnySelected() is "empirically + // derived": doing rebuildGeom() while in selection trashes the + // graphics state of animated objects. Skipping this update is OK + // because we get another one on deselect. + + 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()->removeAttachmentOverridesForObject(this); + } + else + { + // Making an animated object into a rigged mesh + getAvatarAncestor()->addAttachmentOverridesForObject(this); + } + } +} + +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("AXON") << (U32) 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; +#if 0 + if (root_is_animated_flag) + { + bool root_can_be_animated = root_vol->canBeAnimatedObject(); + bool this_can_be_animated = (root_vol == this) ? root_can_be_animated : canBeAnimatedObject(); + if (this_can_be_animated && root_can_be_animated) + { + return true; + } + } + return false; +#endif +} + +// 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. +void LLVOVolume::updateAnimatedObjectStateOnReparent(LLViewerObject *old_parent, LLViewerObject *new_parent) +{ + LLVOVolume *old_volp = dynamic_cast<LLVOVolume*>(old_parent); + + // AXON - depending on whether animated objects can be attached, + // we may want to include or remove the isAvatar() check. + // BUG?? + if (new_parent && !new_parent->isAvatar()) + { + if (mControlAvatar.notNull()) + { + LLControlAvatar *av = mControlAvatar; + mControlAvatar = NULL; + av->markForDeath(); + } + // If this succeeds now, it's because the new_parent is an animated object + if (isAnimatedObject() && getControlAvatar()) + { + getControlAvatar()->addAttachmentOverridesForObject(this); + } + } + 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()->removeAttachmentOverridesForObject(this); + } + } +} + //---------------------------------------------------------------------------- void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point) @@ -3330,7 +3528,7 @@ void LLVOVolume::updateRadius() BOOL LLVOVolume::isAttachment() const { - return mState != 0 ; + return mAttachmentState != 0 ; } BOOL LLVOVolume::isHUDAttachment() const @@ -3338,7 +3536,7 @@ BOOL LLVOVolume::isHUDAttachment() const // *NOTE: we assume hud attachment points are in defined range // since this range is constant for backwards compatibility // reasons this is probably a reasonable assumption to make - S32 attachment_id = ATTACHMENT_ID_FROM_STATE(mState); + S32 attachment_id = ATTACHMENT_ID_FROM_STATE(mAttachmentState); return ( attachment_id >= 31 && attachment_id <= 38 ); } @@ -3441,7 +3639,7 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const S32 size = gMeshRepo.getMeshSize(volume_params.getSculptID(), getLOD()); if ( size > 0) { - if (gMeshRepo.getSkinInfo(volume_params.getSculptID(), this)) + if (isRiggedMesh()) { // weighted attachment - 1 point for every 3 bytes weighted_mesh = 1; @@ -3623,6 +3821,15 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const return (U32)shame; } +F32 LLVOVolume::getEstTrianglesMax() const +{ + if (isMesh() && getVolume()) + { + return gMeshRepo.getEstTrianglesMax(getVolume()->getParams().getSculptID()); + } + return 0.f; +} + F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const { F32 radius = getScale().length()*0.5f; @@ -3714,6 +3921,24 @@ 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); + // AXON This is kind of a guess. Better if we could compare + // the before and after flags directly. What about cases where + // there's no control avatar for optimization reasons? + bool was_enabled = (getControlAvatar() != NULL); + if (enabled != was_enabled) + { + LL_DEBUGS("AXON") << (U32) this + << " calling onSetExtendedMeshFlags, enabled " << (U32) enabled + << " was_enabled " << (U32) was_enabled + << " local_origin " << (U32) local_origin + << LL_ENDL; + onSetExtendedMeshFlags(extended_mesh_flags); + } + } if (mDrawable.notNull()) { BOOL is_light = getIsLight(); @@ -3727,10 +3952,17 @@ void LLVOVolume::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_u void LLVOVolume::setSelected(BOOL sel) { LLViewerObject::setSelected(sel); - if (mDrawable.notNull()) - { - markForUpdate(TRUE); - } + if (isAnimatedObject()) + { + getRootEdit()->recursiveMarkForUpdate(TRUE); + } + else + { + if (mDrawable.notNull()) + { + markForUpdate(TRUE); + } + } } void LLVOVolume::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) @@ -3840,6 +4072,21 @@ const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const return xform->getWorldMatrix(); } +void LLVOVolume::markForUpdate(BOOL priority) +{ +#if 1 + // AXON debugging + if (isAnimatedObject() && isRiggedMesh()) + { + std::string vobj_name = llformat("Vol%u", (U32) this); + F32 est_tris = getEstTrianglesMax(); + LL_DEBUGS("AXONLinkset") << vobj_name << " markForUpdate, tris " << est_tris << LL_ENDL; + } +#endif + LLViewerObject::markForUpdate(priority); + mVolumeChanged = TRUE; +} + LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const { LLVector3 ret = pos - getRenderPosition(); @@ -4092,9 +4339,9 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& bool LLVOVolume::treatAsRigged() { return isSelected() && - isAttachment() && - mDrawable.notNull() && - mDrawable->isState(LLDrawable::RIGGED); + (isAttachment() || isAnimatedObject()) && + mDrawable.notNull() && + mDrawable->isState(LLDrawable::RIGGED); } LLRiggedVolume* LLVOVolume::getRiggedVolume() @@ -4124,9 +4371,7 @@ void LLVOVolume::updateRiggedVolume(bool force_update) } LLVolume* volume = getVolume(); - - const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(volume->getParams().getSculptID(), this); - + const LLMeshSkinInfo* skin = getSkinInfo(); if (!skin) { clearRiggedVolume(); @@ -4134,7 +4379,6 @@ void LLVOVolume::updateRiggedVolume(bool force_update) } LLVOAvatar* avatar = getAvatar(); - if (!avatar) { clearRiggedVolume(); @@ -4149,7 +4393,6 @@ void LLVOVolume::updateRiggedVolume(bool force_update) } mRiggedVolume->update(skin, avatar, volume); - } static LLTrace::BlockTimerStatHandle FTM_SKIN_RIGGED("Skin"); @@ -4187,6 +4430,9 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons U32 maxJoints = LLSkinningUtil::getMeshJointCount(skin); LLSkinningUtil::initSkinningMatrixPalette((LLMatrix4*)mat, maxJoints, skin, avatar); + S32 rigged_vert_count = 0; + S32 rigged_face_count = 0; + LLVector4a box_min, box_max; for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i) { const LLVolumeFace& vol_face = volume->getVolumeFace(i); @@ -4208,6 +4454,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; @@ -4227,12 +4475,19 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons min = pos[0]; max = pos[1]; + if (i==0) + { + box_min = min; + box_max = max; + } for (U32 j = 1; j < dst_face.mNumVertices; ++j) { min.setMin(min, pos[j]); max.setMax(max, pos[j]); } + box_min.setMin(min,box_min); + box_max.setMax(max,box_max); dst_face.mCenter->setAdd(dst_face.mExtents[0], dst_face.mExtents[1]); dst_face.mCenter->mul(0.5f); @@ -4252,6 +4507,10 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons } } } + mExtraDebugText = llformat("rigged %d/%d - box (%f %f %f) (%f %f %f)", + rigged_face_count, rigged_vert_count, + box_min[0], box_min[1], box_min[2], + box_max[0], box_max[1], box_max[2]); } U32 LLVOVolume::getPartitionType() const @@ -4643,8 +4902,6 @@ static LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj) void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) { - - if (group->changeLOD()) { group->mLastUpdateDistance = group->mDistance; @@ -4665,27 +4922,29 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) group->mBuilt = 1.f; - LLVOAvatar* pAvatarVO = NULL; + LLVOAvatar *rigged_av = NULL; LLSpatialBridge* bridge = group->getSpatialPartition()->asBridge(); + LLViewerObject *vobj = NULL; + LLVOVolume *vol_obj = NULL; + if (bridge) { + vobj = bridge->mDrawable->getVObj(); + vol_obj = dynamic_cast<LLVOVolume*>(vobj); if (bridge->mAvatar.isNull()) { - LLViewerObject* vobj = bridge->mDrawable->getVObj(); if (vobj) { bridge->mAvatar = vobj->getAvatar(); } } - - pAvatarVO = bridge->mAvatar; - } - - if (pAvatarVO) - { - pAvatarVO->subtractAttachmentArea( group->mSurfaceArea ); + rigged_av = bridge->mAvatar; } + if (vol_obj) + { + vol_obj->updateVisualComplexity(); + } group->mGeometryBytes = 0; group->mSurfaceArea = 0; @@ -4728,7 +4987,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) LL_RECORD_BLOCK_TIME(FTM_REBUILD_VOLUME_FACE_LIST); //get all the faces into a list - for (LLSpatialGroup::element_iter drawable_iter = group->getDataBegin(); drawable_iter != group->getDataEnd(); ++drawable_iter) + for (LLSpatialGroup::element_iter drawable_iter = group->getDataBegin(); + drawable_iter != group->getDataEnd(); ++drawable_iter) { LLDrawable* drawablep = (LLDrawable*)(*drawable_iter)->getDrawable(); @@ -4743,12 +5003,14 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } LLVOVolume* vobj = drawablep->getVOVolume(); - + if (!vobj) { continue; } + std::string vobj_name = llformat("Vol%u", (U32) vobj); + if (vobj->isMesh() && ((vobj->getVolume() && !vobj->getVolume()->isMeshAssetLoaded()) || !gMeshRepo.meshRezEnabled())) { @@ -4762,27 +5024,77 @@ 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(); + +#if 1 + LL_DEBUGS("AXONLinkset") << 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() + << " playing " << (U32) (vobj->getControlAvatar() ? vobj->getControlAvatar()->mPlaying : false) + << " frame " << LLFrameTimer::getFrameCount() + << LL_ENDL; +#endif + llassert_always(vobj); vobj->updateTextureVirtualSize(true); vobj->preRebuild(); drawablep->clearState(LLDrawable::HAS_ALPHA); - bool rigged = vobj->isAttachment() && - vobj->isMesh() && - gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID(), vobj); + // Standard rigged mesh attachments: + bool rigged = !vobj->isAnimatedObject() && vobj->isRiggedMesh() && vobj->isAttachment(); + // Animated objects. Have to check for isRiggedMesh() to + // exclude static objects in animated object linksets. + rigged = rigged || (vobj->isAnimatedObject() && vobj->isRiggedMesh() && + vobj->getControlAvatar() && vobj->getControlAvatar()->mPlaying); + + if (vobj->isAnimatedObject()) + { + if (!vobj->getControlAvatar()) + { + F32 tri_count = vobj->getRootEdit()->recursiveGetEstTrianglesMax(); + if (tri_count <= 0.f) + { + LL_DEBUGS("AXON") << vobj_name << " not calling linkControlAvatar(), because no tris" << LL_ENDL; + } + else + { + LL_DEBUGS("AXON") << vobj_name << " calling linkControlAvatar()" << LL_ENDL; + vobj->linkControlAvatar(); + } + } + if (vobj->getControlAvatar()) + { + rigged_av = vobj->getControlAvatar(); + rigged_av->rebuildAttachmentOverrides(); + } + } + else + { + // Not animated but has a control avatar - probably + // the checkbox has changed since the last rebuild. + if (vobj->getControlAvatar()) + { + LL_DEBUGS("AXON") << vobj_name << " calling unlinkControlAvatar()" << LL_ENDL; + vobj->unlinkControlAvatar(); + } + } bool bake_sunlight = LLPipeline::sBakeSunlight && drawablep->isStatic(); - bool is_rigged = false; + bool any_rigged_face = false; - if (rigged && pAvatarVO) + if (rigged && rigged_av) { - pAvatarVO->addAttachmentOverridesForObject(vobj); - if (!LLApp::isExiting() && pAvatarVO->isSelf() && debugLoggingEnabled("AvatarAttachments")) + rigged_av->addAttachmentOverridesForObject(vobj); + if (!LLApp::isExiting() && rigged_av->isSelf() && debugLoggingEnabled("AvatarAttachments")) { bool verbose = true; - pAvatarVO->showAttachmentOverrides(verbose); + rigged_av->showAttachmentOverrides(verbose); } } @@ -4802,7 +5114,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) //sum up face verts and indices drawablep->updateFaceSize(i); - if (rigged) + if (rigged) { if (!facep->isState(LLFace::RIGGED)) { //completely reset vertex buffer @@ -4810,7 +5122,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } facep->setState(LLFace::RIGGED); - is_rigged = true; + any_rigged_face = true; //get drawpool of avatar with rigged face LLDrawPoolAvatar* pool = get_avatar_drawpool(vobj); @@ -5154,7 +5466,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } } - if (is_rigged) + if (any_rigged_face) { if (!drawablep->isState(LLDrawable::RIGGED)) { @@ -5168,6 +5480,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) else { drawablep->clearState(LLDrawable::RIGGED); + vobj->updateRiggedVolume(); } } } @@ -5221,9 +5534,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) LLDrawable* drawablep = (LLDrawable*)(*drawable_iter)->getDrawable(); if(drawablep) { - drawablep->clearState(LLDrawable::REBUILD_ALL); - } - } + drawablep->clearState(LLDrawable::REBUILD_ALL); + } + } } group->mLastUpdateTime = gFrameTimeSeconds; @@ -5236,11 +5549,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } mFaceList.clear(); - - if (pAvatarVO) - { - pAvatarVO->addAttachmentArea( group->mSurfaceArea ); - } } static LLTrace::BlockTimerStatHandle FTM_REBUILD_MESH_FLUSH("Flush Mesh"); @@ -5269,6 +5577,15 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) if (drawablep && !drawablep->isDead() && drawablep->isState(LLDrawable::REBUILD_ALL) && !drawablep->isState(LLDrawable::RIGGED) ) { LLVOVolume* vobj = drawablep->getVOVolume(); +#if 1 + // AXON debugging + if (vobj->isAnimatedObject() && vobj->isRiggedMesh()) + { + std::string vobj_name = llformat("Vol%u", (U32) vobj); + F32 est_tris = vobj->getEstTrianglesMax(); + LL_DEBUGS("AXONLinkset") << vobj_name << " rebuildMesh, tris " << est_tris << LL_ENDL; + } +#endif vobj->preRebuild(); if (drawablep->isState(LLDrawable::ANIMATED_CHILD)) diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index a331908320..afd6c234ee 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -62,6 +62,8 @@ public: } void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume); + + std::string mExtraDebugText; }; // Base class for implementations of the volume - Primitive, Flexible Object, etc. @@ -132,6 +134,7 @@ public: /*virtual*/ const LLMatrix4 getRenderMatrix() const; typedef std::map<LLUUID, S32> texture_cost_t; U32 getRenderCost(texture_cost_t &textures) const; + /*virtual*/ F32 getEstTrianglesMax() const; F32 getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_value) const; /*virtual*/ F32 getStreamingCost(S32* bytes = NULL, S32* visible_bytes = NULL) { return getStreamingCost(bytes, visible_bytes, NULL); } @@ -159,7 +162,7 @@ public: /*virtual*/ F32 getRadius() const { return mVObjRadius; }; const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const; - void markForUpdate(BOOL priority) { LLViewerObject::markForUpdate(priority); mVolumeChanged = TRUE; } + void markForUpdate(BOOL priority); void faceMappingChanged() { mFaceMappingChanged=TRUE; }; /*virtual*/ void onShift(const LLVector4a &shift_vector); // Called when the drawable shifts @@ -259,12 +262,26 @@ 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; + void updateAnimatedObjectStateOnReparent(LLViewerObject *old_parent, LLViewerObject *new_parent); + + std::map<LLUUID, S32> mObjectSignaledAnimations; // requested state of Animation name/value + // Functions that deal with media, or media navigation // Update this object's media data with the given media data array @@ -298,7 +315,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 @@ -379,6 +399,7 @@ private: public: static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop static F32 sLODFactor; // LOD scale factor + static S32 sForceLOD; // LOD override static F32 sDistanceFactor; // LOD distance factor static LLPointer<LLObjectMediaDataClient> sObjectMediaClient; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index d3be5fea1a..900c749ee0 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -3350,6 +3350,18 @@ void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags f { if (drawablep && !drawablep->isDead() && assertInitialized()) { +#if 1 + // AXON debugging + LLVOVolume *vol_obj = drawablep->getVOVolume(); + if (vol_obj && vol_obj->isAnimatedObject() && vol_obj->isRiggedMesh()) + { + std::string vobj_name = llformat("Vol%u", (U32) vol_obj); + F32 est_tris = vol_obj->getEstTrianglesMax(); + LL_DEBUGS("AXONLinkset") << vobj_name << " markRebuild, tris " << est_tris + << " priority " << (S32) priority << " flag " << std::hex << flag << LL_ENDL; + } +#endif + if (!drawablep->isState(LLDrawable::BUILT)) { priority = TRUE; diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index bba36351d9..4cc4b821bf 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -513,11 +513,11 @@ public: RENDER_DEBUG_TEXTURE_AREA = 0x00000100, RENDER_DEBUG_FACE_AREA = 0x00000200, RENDER_DEBUG_PARTICLES = 0x00000400, - RENDER_DEBUG_GLOW = 0x00000800, + RENDER_DEBUG_GLOW = 0x00000800, // not used RENDER_DEBUG_TEXTURE_ANIM = 0x00001000, RENDER_DEBUG_LIGHTS = 0x00002000, RENDER_DEBUG_BATCH_SIZE = 0x00004000, - RENDER_DEBUG_ALPHA_BINS = 0x00008000, + RENDER_DEBUG_ALPHA_BINS = 0x00008000, // not used RENDER_DEBUG_RAYCAST = 0x00010000, RENDER_DEBUG_AVATAR_DRAW_INFO = 0x00020000, RENDER_DEBUG_SHADOW_FRUSTA = 0x00040000, @@ -531,8 +531,9 @@ public: RENDER_DEBUG_NORMALS = 0x04000000, RENDER_DEBUG_LOD_INFO = 0x08000000, RENDER_DEBUG_RENDER_COMPLEXITY = 0x10000000, - RENDER_DEBUG_ATTACHMENT_BYTES = 0x20000000, - RENDER_DEBUG_TEXEL_DENSITY = 0x40000000 + RENDER_DEBUG_ATTACHMENT_BYTES = 0x20000000, // not used + RENDER_DEBUG_TEXEL_DENSITY = 0x40000000, + RENDER_DEBUG_TRIANGLE_COUNT = 0x80000000 }; public: diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index ed3cc26851..63e2ed4bb9 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -2,7 +2,7 @@ <floater positioning="cascading" legacy_header_height="18" - height="590" + height="600" layout="topleft" bg_opaque_image="Window_NoTitle_Foreground" bg_alpha_image="Window_NoTitle_Background" @@ -2131,7 +2131,7 @@ even though the user gets a free copy. <panel border="false" follows="all" - height="367" + height="387" label="Features" layout="topleft" left_delta="0" @@ -2169,13 +2169,23 @@ even though the user gets a free copy. Edit object features: </text> <check_box - height="19" + height="15" + label="Animated Mesh" + layout="topleft" + left="10" + name="Animated Mesh Checkbox Ctrl" + tool_tip="Allows rigged mesh objects to be animated independently" + top_pad="10" + width="121" /> + <check_box + height="10" label="Flexible Path" + follows="left|top" layout="topleft" left="10" name="Flexible1D Checkbox Ctrl" tool_tip="Allows object to flex about the Z axis (Client-side only)" - top_pad="20" + top_pad="15" width="121" /> <spinner follows="left|top" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 697d27907d..f8b4948697 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2654,6 +2654,16 @@ parameter="lod info" /> </menu_item_check> <menu_item_check + label="Triangle Count" + name="Triangle Count"> + <menu_item_check.on_check + function="Advanced.CheckInfoDisplay" + parameter="triangle count" /> + <menu_item_check.on_click + function="Advanced.ToggleInfoDisplay" + parameter="triangle count" /> + </menu_item_check> + <menu_item_check label="Build Queue" name="Build Queue"> <menu_item_check.on_check diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 9af4b299de..f773ae6845 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -10622,6 +10622,38 @@ You are not allowed to change this shape. <notification icon="alertmodal.tga" + name="NoPermsTooManyAttachedAnimatedObjects" + type="notify"> + <tag>fail</tag> +Operation would cause the number of attached animated objects to exceed the limit. + </notification> + + <notification + icon="alertmodal.tga" + name="NoPermsLinkAnimatedObjectTooLarge" + type="notify"> + <tag>fail</tag> +Can't link these objects because the resulting animated object would exceed the size limit. + </notification> + + <notification + icon="alertmodal.tga" + name="NoPermsSetFlagAnimatedObjectTooLarge" + type="notify"> + <tag>fail</tag> +Can't make this object into an animated object because it would exceed the size limit. + </notification> + + <notification + icon="alertmodal.tga" + name="ErrorNoMeshData" + type="notify"> + <tag>fail</tag> +Server error: cannot complete this operation because mesh data is not loaded. + </notification> + + <notification + icon="alertmodal.tga" name="NoAccessToClaimObjects" type="notify"> <tag>fail</tag> diff --git a/indra/test/test.cpp b/indra/test/test.cpp index 1c0317df1d..6160d9492b 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -34,7 +34,6 @@ * */ - #include "linden_common.h" #include "llerrorcontrol.h" #include "lltut.h" @@ -685,5 +684,4 @@ int main(int argc, char **argv) return retval; //delete mycallback; - } diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg index c56eaae6fe..18264ee59c 100755 --- a/scripts/messages/message_template.msg +++ b/scripts/messages/message_template.msg @@ -8,7 +8,7 @@ version 2.0 // for each type is listed below: // Low: 423 // Medium: 18 -// High: 29 +// High: 30 // PLEASE UPDATE THIS WHEN YOU ADD A NEW MESSAGE! @@ -3571,7 +3571,6 @@ version 2.0 } } - // AvatarAppearance - Update visual params { AvatarAppearance Low 158 Trusted Zerocoded @@ -7286,6 +7285,29 @@ version 2.0 // ************************************************************************* +// Object animation messages +// ************************************************************************* + +// Note this is basically identical to AvatarAnimation. +// Needs to be a different message because existing viewers +// have insufficiently smart handler functions. + +// ObjectAnimation - Update animation state +// simulator --> viewer +{ + ObjectAnimation High 30 Trusted Unencoded + { + Sender Single + { ID LLUUID } + } + { + AnimationList Variable + { AnimID LLUUID } + { AnimSequenceID S32 } + } +} + +// ************************************************************************* // Asset storage messages // ************************************************************************* diff --git a/scripts/messages/message_template.msg.sha1 b/scripts/messages/message_template.msg.sha1 index 5bc06ec042..db87ad5e77 100755 --- a/scripts/messages/message_template.msg.sha1 +++ b/scripts/messages/message_template.msg.sha1 @@ -1 +1 @@ -337f351910b0c8821cb3d447bc6578516a043c80
\ No newline at end of file +55df2c7135733c5da64ef8f01859b83a433a3a09
\ No newline at end of file diff --git a/scripts/testing/lsl/axon_test_region_driver.lsl b/scripts/testing/lsl/axon_test_region_driver.lsl new file mode 100644 index 0000000000..dcf146a9cf --- /dev/null +++ b/scripts/testing/lsl/axon_test_region_driver.lsl @@ -0,0 +1,54 @@ +list buttons = ["anim start", "anim stop", "step", "verbose on", "verbose off", " "]; +string dialogInfo = "\nPlease make a choice."; + +key ToucherID; +integer dialogChannel; +integer listenHandle; +integer commandChannel; + +default +{ + state_entry() + { + dialogChannel = -1 - (integer)("0x" + llGetSubString( (string)llGetKey(), -7, -1) ); + commandChannel = -2001; + } + + touch_start(integer num_detected) + { + ToucherID = llDetectedKey(0); + llListenRemove(listenHandle); + listenHandle = llListen(dialogChannel, "", ToucherID, ""); + llDialog(ToucherID, dialogInfo, buttons, dialogChannel); + //llSetTimerEvent(60.0); // Here we set a time limit for responses + } + + listen(integer channel, string name, key id, string message) + { + if (message == "-") + { + llDialog(ToucherID, dialogInfo, buttons, dialogChannel); + return; + } + + llListenRemove(listenHandle); + // stop timer since the menu was clicked + llSetTimerEvent(0); + + //llOwnerSay("Sending message " + message + " on channel " + (string)commandChannel); + llRegionSay(commandChannel, message); + } + + timer() + { + // stop timer + llSetTimerEvent(0); + + llListenRemove(listenHandle); + //llWhisper(0, "Sorry. You snooze; you lose."); + } +} + +// Local Variables: +// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/axon_test_region_driver.lsl" +// End: diff --git a/scripts/testing/lsl/cycle_object_animations.lsl b/scripts/testing/lsl/cycle_object_animations.lsl new file mode 100644 index 0000000000..46910e3656 --- /dev/null +++ b/scripts/testing/lsl/cycle_object_animations.lsl @@ -0,0 +1,118 @@ +integer listenHandle; +integer verbose; +integer current_animation_number; +string NowPlaying; + +say_if_verbose(integer channel, string message) +{ + if (verbose) + { + llSay(channel, message); + } +} + +stop_all_animations() +{ + integer count = llGetInventoryNumber(INVENTORY_ANIMATION); + string ItemName; + string NowPlaying; + while (count--) + { + ItemName = llGetInventoryName(INVENTORY_ANIMATION, count); + say_if_verbose(0, "Stopping " + ItemName); + llStopObjectAnimation(ItemName); + } +} + +start_cycle_animations() +{ + current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION); + next_animation(); // Do first iteration without waiting for timer + llSetTimerEvent(5.0); +} + +next_animation() +{ + string ItemName; + if (NowPlaying != "") + { + say_if_verbose(0, "Stopping " + NowPlaying); + llStopObjectAnimation(NowPlaying); + } + if (current_animation_number--) + { + ItemName = llGetInventoryName(INVENTORY_ANIMATION, current_animation_number); + say_if_verbose(0, "Starting " + ItemName); + llStartObjectAnimation(ItemName); + NowPlaying = ItemName; + } + else + { + // Start again at the top + current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION); + } +} + +stop_cycle_animations() +{ + llSetTimerEvent(0); +} + +default +{ + state_entry() + { + say_if_verbose(0, "Animated Object here"); + listenHandle = llListen(-2001,"","",""); + verbose = 0; + + stop_all_animations(); + } + + listen(integer channel, string name, key id, string message) + { + //llOwnerSay("got message " + name + " " + (string) id + " " + message); + list words = llParseString2List(message,[" "],[]); + string command = llList2String(words,0); + string option = llList2String(words,1); + if (command=="anim") + { + stop_all_animations(); + if (option=="start") + { + start_cycle_animations(); + } + else if (option=="stop") + { + stop_cycle_animations(); + } + } + if (command=="verbose") + { + if (option=="on") + { + verbose = 1; + } + else if (option=="off") + { + verbose = 0; + } + } + } + + timer() + { + say_if_verbose(0, "timer triggered"); + next_animation(); + } + + touch_start(integer total_number) + { + say_if_verbose(0, "Touch started."); + start_cycle_animations(); + } +} + +// Local Variables: +// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/cycle_object_animations.lsl" +// End: diff --git a/scripts/testing/lsl/cycle_object_animations_v2.lsl b/scripts/testing/lsl/cycle_object_animations_v2.lsl new file mode 100644 index 0000000000..60879fcce2 --- /dev/null +++ b/scripts/testing/lsl/cycle_object_animations_v2.lsl @@ -0,0 +1,133 @@ +integer listenHandle; +integer verbose; +integer current_animation_number; +string NowPlaying; + +say_if_verbose(integer channel, string message) +{ + if (verbose) + { + llSay(channel, message); + } +} + +stop_all_animations() +{ + list curr_anims = llGetObjectAnimationNames(); + say_if_verbose(0,"stopping all, curr_anims are " + (string) curr_anims); + integer length = llGetListLength(curr_anims); + integer index = 0; + while (index < length) + { + string anim = llList2String(curr_anims, index); + say_if_verbose(0, "Stopping " + anim); + llStopObjectAnimation(anim); + // This check isn't really needed, just included to demonstrate is_animation_running() + if (is_animation_running(anim)) + { + say_if_verbose(0, "ERROR - failed to stop " + anim + "!"); + } + ++index; + } +} + +integer is_animation_running(string anim) +{ + list curr_anims = llGetObjectAnimationNames(); + return ~llListFindList(curr_anims, (list)anim); +} + +start_cycle_animations() +{ + current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION); + next_animation(); // Do first iteration without waiting for timer + llSetTimerEvent(5.0); +} + +next_animation() +{ + string ItemName; + if (NowPlaying != "") + { + say_if_verbose(0, "Stopping " + NowPlaying); + llStopObjectAnimation(NowPlaying); + } + if (current_animation_number--) + { + ItemName = llGetInventoryName(INVENTORY_ANIMATION, current_animation_number); + say_if_verbose(0, "Starting " + ItemName); + llStartObjectAnimation(ItemName); + key anim_id = llGetInventoryKey(ItemName); + say_if_verbose(0, "Started item " + ItemName + " inv key " + (string) anim_id); + NowPlaying = ItemName; + } + else + { + // Start again at the top + current_animation_number = llGetInventoryNumber(INVENTORY_ANIMATION); + } +} + +stop_cycle_animations() +{ + llSetTimerEvent(0); +} + +default +{ + state_entry() + { + say_if_verbose(0, "Animated Object here"); + listenHandle = llListen(-2001,"","",""); + verbose = 0; + + stop_all_animations(); + } + + listen(integer channel, string name, key id, string message) + { + //llOwnerSay("got message " + name + " " + (string) id + " " + message); + list words = llParseString2List(message,[" "],[]); + string command = llList2String(words,0); + string option = llList2String(words,1); + if (command=="anim") + { + stop_all_animations(); + if (option=="start") + { + start_cycle_animations(); + } + else if (option=="stop") + { + stop_cycle_animations(); + } + } + if (command=="verbose") + { + if (option=="on") + { + verbose = 1; + } + else if (option=="off") + { + verbose = 0; + } + } + } + + timer() + { + say_if_verbose(0, "timer triggered"); + next_animation(); + } + + touch_start(integer total_number) + { + say_if_verbose(0, "Touch started."); + start_cycle_animations(); + } +} + +// Local Variables: +// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/cycle_object_animations_v2.lsl" +// End: diff --git a/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl b/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl new file mode 100644 index 0000000000..cc5b899b67 --- /dev/null +++ b/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl @@ -0,0 +1,90 @@ +integer listenHandle; +integer verbose; +integer num_steps = 12; +float circle_time = 5.0; +integer circle_step; +vector circle_pos; +vector circle_center; +float circle_radius; + +start_circle(vector center, float radius) +{ + vector currentPosition = llGetPos(); + circle_center = center; + circle_radius = radius; + circle_step = 0; + llSetTimerEvent(circle_time/num_steps); + llTargetOmega(<0.0, 0.0, 1.0>, TWO_PI/circle_time, 1.0); +} + +stop_circle() +{ + llSetTimerEvent(0); + llTargetOmega(<0.0, 0.0, 1.0>, TWO_PI/circle_time, 0.0); + llSetRegionPos(circle_center); +} + +next_circle() +{ + float rad = (circle_step * TWO_PI)/num_steps; + float x = circle_center.x + llCos(rad)*circle_radius; + float y = circle_center.y + llSin(rad)*circle_radius; + float z = circle_center.z; + llSetRegionPos(<x,y,z>); + circle_step = (circle_step+1)%num_steps; +} + +default +{ + state_entry() + { + //llSay(0, "Hello, Avatar!"); + listenHandle = llListen(-2001,"","",""); + verbose = 0; + circle_center = llGetPos(); + } + + listen(integer channel, string name, key id, string message) + { + //llOwnerSay("got message " + name + " " + (string) id + " " + message); + list words = llParseString2List(message,[" "],[]); + string command = llList2String(words,0); + string option = llList2String(words,1); + if (command=="anim") + { + if (option=="start") + { + start_circle(llGetPos(), 3.0); + } + else if (option=="stop") + { + stop_circle(); + } + } + if (command=="verbose") + { + if (option=="on") + { + verbose = 1; + } + else if (option=="off") + { + verbose = 0; + } + } + if (command=="step") + { + llSetTimerEvent(0); + next_circle(); + } + } + + timer() + { + next_circle(); + } +} + +// Local Variables: +// shadow-file-name: "$SW_HOME/axon/scripts/testing/lsl/move_in_circle_using_llSetRegionPos.lsl" +// End: |