summaryrefslogtreecommitdiff
path: root/indra/newview/llcontrolavatar.cpp
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
committerAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
commit1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch)
treeab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llcontrolavatar.cpp
parent6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff)
parente1623bb276f83a43ce7a197e388720c05bdefe61 (diff)
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts: # autobuild.xml # indra/cmake/CMakeLists.txt # indra/cmake/GoogleMock.cmake # indra/llaudio/llaudioengine_fmodstudio.cpp # indra/llaudio/llaudioengine_fmodstudio.h # indra/llaudio/lllistener_fmodstudio.cpp # indra/llaudio/lllistener_fmodstudio.h # indra/llaudio/llstreamingaudio_fmodstudio.cpp # indra/llaudio/llstreamingaudio_fmodstudio.h # indra/llcharacter/llmultigesture.cpp # indra/llcharacter/llmultigesture.h # indra/llimage/llimage.cpp # indra/llimage/llimagepng.cpp # indra/llimage/llimageworker.cpp # indra/llimage/tests/llimageworker_test.cpp # indra/llmessage/tests/llmockhttpclient.h # indra/llprimitive/llgltfmaterial.h # indra/llrender/llfontfreetype.cpp # indra/llui/llcombobox.cpp # indra/llui/llfolderview.cpp # indra/llui/llfolderviewmodel.h # indra/llui/lllineeditor.cpp # indra/llui/lllineeditor.h # indra/llui/lltextbase.cpp # indra/llui/lltextbase.h # indra/llui/lltexteditor.cpp # indra/llui/lltextvalidate.cpp # indra/llui/lltextvalidate.h # indra/llui/lluictrl.h # indra/llui/llview.cpp # indra/llwindow/llwindowmacosx.cpp # indra/newview/app_settings/settings.xml # indra/newview/llappearancemgr.cpp # indra/newview/llappearancemgr.h # indra/newview/llavatarpropertiesprocessor.cpp # indra/newview/llavatarpropertiesprocessor.h # indra/newview/llbreadcrumbview.cpp # indra/newview/llbreadcrumbview.h # indra/newview/llbreastmotion.cpp # indra/newview/llbreastmotion.h # indra/newview/llconversationmodel.h # indra/newview/lldensityctrl.cpp # indra/newview/lldensityctrl.h # indra/newview/llface.inl # indra/newview/llfloatereditsky.cpp # indra/newview/llfloatereditwater.cpp # indra/newview/llfloateremojipicker.h # indra/newview/llfloaterimsessiontab.cpp # indra/newview/llfloaterprofiletexture.cpp # indra/newview/llfloaterprofiletexture.h # indra/newview/llgesturemgr.cpp # indra/newview/llgesturemgr.h # indra/newview/llimpanel.cpp # indra/newview/llimpanel.h # indra/newview/llinventorybridge.cpp # indra/newview/llinventorybridge.h # indra/newview/llinventoryclipboard.cpp # indra/newview/llinventoryclipboard.h # indra/newview/llinventoryfunctions.cpp # indra/newview/llinventoryfunctions.h # indra/newview/llinventorygallery.cpp # indra/newview/lllistbrowser.cpp # indra/newview/lllistbrowser.h # indra/newview/llpanelobjectinventory.cpp # indra/newview/llpanelprofile.cpp # indra/newview/llpanelprofile.h # indra/newview/llpreviewgesture.cpp # indra/newview/llsavedsettingsglue.cpp # indra/newview/llsavedsettingsglue.h # indra/newview/lltooldraganddrop.cpp # indra/newview/llurllineeditorctrl.cpp # indra/newview/llvectorperfoptions.cpp # indra/newview/llvectorperfoptions.h # indra/newview/llviewerparceloverlay.cpp # indra/newview/llviewertexlayer.cpp # indra/newview/llviewertexturelist.cpp # indra/newview/macmain.h # indra/test/test.cpp
Diffstat (limited to 'indra/newview/llcontrolavatar.cpp')
-rw-r--r--indra/newview/llcontrolavatar.cpp1440
1 files changed, 719 insertions, 721 deletions
diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp
index f63f9d7ce7..909e0f9e95 100644
--- a/indra/newview/llcontrolavatar.cpp
+++ b/indra/newview/llcontrolavatar.cpp
@@ -1,721 +1,719 @@
-/**
- * @file llcontrolavatar.cpp
- * @brief Implementation for special dummy avatar used to drive rigged meshes.
- *
- * $LicenseInfo:firstyear=2017&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2017, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-#include "llcontrolavatar.h"
-#include "llagent.h" // Get state values from here
-#include "llviewerobjectlist.h"
-#include "pipeline.h"
-#include "llanimationstates.h"
-#include "llviewercontrol.h"
-#include "llmeshrepository.h"
-#include "llviewerregion.h"
-#include "llskinningutil.h"
-
-const F32 LLControlAvatar::MAX_LEGAL_OFFSET = 3.0f;
-const F32 LLControlAvatar::MAX_LEGAL_SIZE = 64.0f;
-
-//static
-boost::signals2::connection LLControlAvatar::sRegionChangedSlot;
-
-LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) :
- LLVOAvatar(id, pcode, regionp),
- mPlaying(false),
- mGlobalScale(1.0f),
- mMarkedForDeath(false),
- mRootVolp(NULL),
- mControlAVBridge(NULL),
- mScaleConstraintFixup(1.0),
- mRegionChanged(false)
-{
- mIsDummy = true;
- mIsControlAvatar = true;
- mEnableDefaultMotions = false;
-}
-
-// virtual
-LLControlAvatar::~LLControlAvatar()
-{
- // Should already have been unlinked before destruction
- llassert(!mRootVolp);
-}
-
-// virtual
-void LLControlAvatar::initInstance()
-{
- // Potential optimizations here: avoid creating system
- // avatar mesh content since it's not used. For now we just clean some
- // things up after the fact in releaseMeshData().
- LLVOAvatar::initInstance();
-
- createDrawable(&gPipeline);
- updateJointLODs();
- updateGeometry(mDrawable);
- hideSkirt();
-
- mInitFlags |= 1<<4;
-}
-
-const LLVOAvatar *LLControlAvatar::getAttachedAvatar() const
-{
- if (mRootVolp && mRootVolp->isAttachment())
- {
- return mRootVolp->getAvatarAncestor();
- }
- return NULL;
-}
-
-LLVOAvatar *LLControlAvatar::getAttachedAvatar()
-{
- if (mRootVolp && mRootVolp->isAttachment())
- {
- return mRootVolp->getAvatarAncestor();
- }
- return NULL;
-}
-
-void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_scale_fixup) const
-{
- F32 max_legal_offset = MAX_LEGAL_OFFSET;
- if (gSavedSettings.getControl("AnimatedObjectsMaxLegalOffset"))
- {
- max_legal_offset = gSavedSettings.getF32("AnimatedObjectsMaxLegalOffset");
- }
- max_legal_offset = llmax(max_legal_offset,0.f);
-
- F32 max_legal_size = MAX_LEGAL_SIZE;
- if (gSavedSettings.getControl("AnimatedObjectsMaxLegalSize"))
- {
- max_legal_size = gSavedSettings.getF32("AnimatedObjectsMaxLegalSize");
- }
- max_legal_size = llmax(max_legal_size, 1.f);
-
- new_pos_fixup = LLVector3();
- new_scale_fixup = 1.0f;
- LLVector3 vol_pos = mRootVolp->getRenderPosition();
-
- // Fix up position if needed to prevent visual encroachment
- if (box_valid_and_non_zero(getLastAnimExtents())) // wait for state to settle down
- {
- // The goal here is to ensure that the extent of the avatar's
- // bounding box does not wander too far from the
- // official position of the corresponding volume. We
- // do this by tracking the distance and applying a
- // correction to the control avatar position if
- // needed.
- const LLVector3 *extents = getLastAnimExtents();
- LLVector3 unshift_extents[2];
- unshift_extents[0] = extents[0] - mPositionConstraintFixup;
- unshift_extents[1] = extents[1] - mPositionConstraintFixup;
- LLVector3 box_dims = extents[1]-extents[0];
- F32 box_size = llmax(box_dims[0],box_dims[1],box_dims[2]);
-
- if (!mRootVolp->isAttachment())
- {
- LLVector3 pos_box_offset = point_to_box_offset(vol_pos, unshift_extents);
- F32 offset_dist = pos_box_offset.length();
- if (offset_dist > max_legal_offset && offset_dist > 0.f)
- {
- F32 target_dist = (offset_dist - max_legal_offset);
- new_pos_fixup = (target_dist/offset_dist)*pos_box_offset;
- }
- if (new_pos_fixup != mPositionConstraintFixup)
- {
- LL_DEBUGS("ConstraintFix") << getFullname() << " pos fix, offset_dist " << offset_dist << " pos fixup "
- << new_pos_fixup << " was " << mPositionConstraintFixup << LL_ENDL;
- LL_DEBUGS("ConstraintFix") << "vol_pos " << vol_pos << LL_ENDL;
- LL_DEBUGS("ConstraintFix") << "extents " << extents[0] << " " << extents[1] << LL_ENDL;
- LL_DEBUGS("ConstraintFix") << "unshift_extents " << unshift_extents[0] << " " << unshift_extents[1] << LL_ENDL;
-
- }
- }
- if (box_size/mScaleConstraintFixup > max_legal_size)
- {
- new_scale_fixup = mScaleConstraintFixup*max_legal_size/box_size;
- LL_DEBUGS("ConstraintFix") << getFullname() << " scale fix, box_size " << box_size << " fixup "
- << mScaleConstraintFixup << " max legal " << max_legal_size
- << " -> new scale " << new_scale_fixup << LL_ENDL;
- }
- }
-}
-
-void LLControlAvatar::matchVolumeTransform()
-{
- if (mRootVolp)
- {
- LLVector3 new_pos_fixup;
- F32 new_scale_fixup;
- if (mRegionChanged)
- {
- new_scale_fixup = mScaleConstraintFixup;
- new_pos_fixup = mPositionConstraintFixup;
- mRegionChanged = false;
- }
- else
- {
- getNewConstraintFixups(new_pos_fixup, new_scale_fixup);
- }
- mPositionConstraintFixup = new_pos_fixup;
- mScaleConstraintFixup = new_scale_fixup;
-
- if (mRootVolp->isAttachment())
- {
- LLVOAvatar *attached_av = getAttachedAvatar();
- if (attached_av)
- {
- LLViewerJointAttachment *attach = attached_av->getTargetAttachmentPoint(mRootVolp);
- if (getRegion() && !isDead())
- {
- setPositionAgent(mRootVolp->getRenderPosition());
- }
- attach->updateWorldPRSParent();
- LLVector3 joint_pos = attach->getWorldPosition();
- LLQuaternion joint_rot = attach->getWorldRotation();
- LLVector3 obj_pos = mRootVolp->mDrawable->getPosition();
- LLQuaternion obj_rot = mRootVolp->mDrawable->getRotation();
- obj_pos.rotVec(joint_rot);
- mRoot->setWorldPosition(obj_pos + joint_pos);
- mRoot->setWorldRotation(obj_rot * joint_rot);
- setRotation(mRoot->getRotation());
-
- F32 global_scale = gSavedSettings.getF32("AnimatedObjectsGlobalScale");
- setGlobalScale(global_scale * mScaleConstraintFixup);
- }
- else
- {
- LL_WARNS_ONCE() << "can't find attached av!" << LL_ENDL;
- }
- }
- else
- {
- LLVector3 vol_pos = mRootVolp->getRenderPosition();
-
- // FIXME: Currently if you're doing something like playing an
- // animation that moves the pelvis (on an avatar or
- // animated object), the name tag and debug text will be
- // left behind. Ideally setPosition() would follow the
- // skeleton around in a smarter way, so name tags,
- // complexity info and such line up better. Should defer
- // this until avatars also get fixed.
-
- LLQuaternion obj_rot;
- if (mRootVolp->mDrawable)
- {
- obj_rot = mRootVolp->mDrawable->getRotation();
- }
- else
- {
- obj_rot = mRootVolp->getRotation();
- }
-
- LLMatrix3 bind_mat;
-
- LLQuaternion bind_rot;
-#define MATCH_BIND_SHAPE
-#ifdef MATCH_BIND_SHAPE
- // MAINT-8671 - based on a patch from Beq Janus
- const LLMeshSkinInfo* skin_info = mRootVolp->getSkinInfo();
- if (skin_info)
- {
- LL_DEBUGS("BindShape") << getFullname() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL;
- bind_rot = LLSkinningUtil::getUnscaledQuaternion(LLMatrix4(skin_info->mBindShapeMatrix));
- }
-#endif
- setRotation(bind_rot*obj_rot);
- mRoot->setWorldRotation(bind_rot*obj_rot);
- if (getRegion() && !isDead())
- {
- setPositionAgent(vol_pos);
- }
- mRoot->setPosition(vol_pos + mPositionConstraintFixup);
-
- F32 global_scale = gSavedSettings.getF32("AnimatedObjectsGlobalScale");
- setGlobalScale(global_scale * mScaleConstraintFixup);
- }
- }
-}
-
-void LLControlAvatar::setGlobalScale(F32 scale)
-{
- if (scale <= 0.0)
- {
- LL_WARNS() << "invalid global scale " << scale << LL_ENDL;
- return;
- }
- if (scale != mGlobalScale)
- {
- F32 adjust_scale = scale/mGlobalScale;
- LL_INFOS() << "scale " << scale << " adjustment " << adjust_scale << LL_ENDL;
- // should we be scaling from the pelvis or the root?
- recursiveScaleJoint(mPelvisp,adjust_scale);
- mGlobalScale = scale;
- }
-}
-
-void LLControlAvatar::recursiveScaleJoint(LLJoint* joint, F32 factor)
-{
- joint->setScale(factor * joint->getScale());
-
- for (LLJoint::joints_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
-
- 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())
- {
- 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);
- mRootVolp->markForUpdate();
-
- // Note that attachment overrides aren't needed here, have already
- // been applied at the time the mControlAvatar was created, in
- // llvovolume.cpp.
-
- matchVolumeTransform();
-
- // Initial exploration of allowing scaling skeleton to match root
- // prim bounding box. If enabled, would probably be controlled by
- // an additional checkbox and default to off. Not enabled for
- // initial release.
-
- // What should the scale be? What we really want is the ratio
- // between the scale at which the object was originally designed
- // and rigged, and the scale to which it has been subsequently
- // modified - for example, if the object has been scaled down by a
- // factor of 2 then we should use 0.5 as the global scale. But we
- // don't have the original scale stored anywhere, just the current
- // scale. Possibilities - 1) remember the original scale
- // somewhere, 2) add another field to let the user specify the
- // global scale, 3) approximate the original scale by looking at
- // the proportions of the skeleton after joint positions have
- // been applied
-
- //LLVector3 obj_scale = obj->getScale();
- //F32 obj_scale_z = llmax(obj_scale[2],0.1f);
- //setGlobalScale(obj_scale_z/2.0f); // roughly fit avatar height range (2m) into object height
-}
-
-LLControlAvatar *LLControlAvatar::createControlAvatar(LLVOVolume *obj)
-{
- LLControlAvatar *cav = (LLControlAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), CO_FLAG_CONTROL_AVATAR);
-
- if (cav)
- {
- cav->mRootVolp = obj;
-
- // Sync up position/rotation with object
- cav->matchVolumeTransform();
- }
-
- return cav;
-}
-
-void LLControlAvatar::markForDeath()
-{
- mMarkedForDeath = true;
- // object unlinked cav and might be dead already
- // might need to clean mControlAVBridge here as well
- mRootVolp = NULL;
-}
-
-void LLControlAvatar::idleUpdate(LLAgent &agent, const F64 &time)
-{
- if (mMarkedForDeath)
- {
- markDead();
- mMarkedForDeath = false;
- }
- else
- {
- LLVOAvatar::idleUpdate(agent,time);
- }
-}
-
-void LLControlAvatar::markDead()
-{
- mRootVolp = NULL;
- super::markDead();
- mControlAVBridge = NULL;
-}
-
-bool LLControlAvatar::computeNeedsUpdate()
-{
- computeUpdatePeriod();
-
- // Animesh attachments are a special case. Should have the same update cadence as their attached parent avatar.
- LLVOAvatar *attached_av = getAttachedAvatar();
- if (attached_av)
- {
- // Have to run computeNeedsUpdate() for attached av in
- // case it hasn't run updateCharacter() already this
- // frame. Note this means that the attached av will
- // run computeNeedsUpdate() multiple times per frame
- // if it has animesh attachments. Results will be
- // consistent except for the corner case of exceeding
- // MAX_IMPOSTOR_INTERVAL in one call but not another,
- // which should be rare.
- attached_av->computeNeedsUpdate();
- mNeedsImpostorUpdate = attached_av->mNeedsImpostorUpdate;
- if (mNeedsImpostorUpdate)
- {
- mLastImpostorUpdateReason = 12;
- }
- return mNeedsImpostorUpdate;
- }
- return LLVOAvatar::computeNeedsUpdate();
-}
-
-bool LLControlAvatar::updateCharacter(LLAgent &agent)
-{
- return LLVOAvatar::updateCharacter(agent);
-}
-
-//virtual
-void LLControlAvatar::updateDebugText()
-{
- if (gSavedSettings.getBOOL("DebugAnimatedObjects"))
- {
- S32 total_linkset_count = 0;
- if (mRootVolp)
- {
- total_linkset_count = 1 + mRootVolp->getChildren().size();
- }
- std::vector<LLVOVolume*> volumes;
- getAnimatedVolumes(volumes);
- S32 animated_volume_count = volumes.size();
- std::string active_string;
- std::string type_string;
- std::string lod_string;
- std::string animated_object_flag_string;
- S32 total_tris = 0;
- S32 total_verts = 0;
- F32 est_tris = 0.f;
- F32 est_streaming_tris = 0.f;
- F32 streaming_cost = 0.f;
- std::string cam_dist_string = "";
- S32 cam_dist_count = 0;
- F32 lod_radius = mRootVolp ? mRootVolp->mLODRadius : 0.f;
-
- for (std::vector<LLVOVolume*>::iterator it = volumes.begin();
- it != volumes.end(); ++it)
- {
- LLVOVolume *volp = *it;
- S32 verts = 0;
- total_tris += volp->getTriangleCount(&verts);
- total_verts += verts;
- est_tris += volp->getEstTrianglesMax();
- est_streaming_tris += volp->getEstTrianglesStreamingCost();
- streaming_cost += volp->getStreamingCost();
- lod_string += llformat("%d",volp->getLOD());
- if (volp && volp->mDrawable)
- {
- bool is_animated_flag = volp->getExtendedMeshFlags() & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG;
- if (is_animated_flag)
- {
- animated_object_flag_string += "1";
- }
- else
- {
- animated_object_flag_string += "0";
- }
- if (volp->mDrawable->isActive())
- {
- active_string += "A";
- }
- else
- {
- active_string += "S";
- }
- if (volp->isRiggedMesh())
- {
- // Rigged/animatable mesh
- type_string += "R";
- lod_radius = volp->mLODRadius;
- }
- else if (volp->isMesh())
- {
- // Static mesh
- type_string += "M";
- }
- else
- {
- // Any other prim
- type_string += "P";
- }
- if (cam_dist_count < 4)
- {
- cam_dist_string += LLStringOps::getReadableNumber(volp->mLODDistance) + "/" +
- LLStringOps::getReadableNumber(volp->mLODAdjustedDistance) + " ";
- cam_dist_count++;
- }
- }
- else
- {
- active_string += "-";
- type_string += "-";
- }
- }
- addDebugText(llformat("CAV obj %d anim %d active %s impost %d upprd %d strcst %f",
- total_linkset_count, animated_volume_count,
- active_string.c_str(), (S32) isImpostor(), getUpdatePeriod(), streaming_cost));
- addDebugText(llformat("types %s lods %s", type_string.c_str(), lod_string.c_str()));
- addDebugText(llformat("flags %s", animated_object_flag_string.c_str()));
- addDebugText(llformat("tris %d (est %.1f, streaming %.1f), verts %d", total_tris, est_tris, est_streaming_tris, total_verts));
- addDebugText(llformat("pxarea %s rank %d", LLStringOps::getReadableNumber(getPixelArea()).c_str(), getVisibilityRank()));
- addDebugText(llformat("lod_radius %s dists %s", LLStringOps::getReadableNumber(lod_radius).c_str(),cam_dist_string.c_str()));
- if (mPositionConstraintFixup.length() > 0.0f || mScaleConstraintFixup != 1.0f)
- {
- addDebugText(llformat("pos fix (%.1f %.1f %.1f) scale %f",
- mPositionConstraintFixup[0],
- mPositionConstraintFixup[1],
- mPositionConstraintFixup[2],
- mScaleConstraintFixup));
- }
-
-#if 0
- std::string region_name = "no region";
- if (mRootVolp->getRegion())
- {
- region_name = mRootVolp->getRegion()->getName();
- }
- std::string skel_region_name = "skel no region";
- if (getRegion())
- {
- skel_region_name = getRegion()->getName();
- }
- addDebugText(llformat("region %x %s skel %x %s",
- mRootVolp->getRegion(), region_name.c_str(),
- getRegion(), skel_region_name.c_str()));
-#endif
-
- }
- LLVOAvatar::updateDebugText();
-}
-
-void LLControlAvatar::getAnimatedVolumes(std::vector<LLVOVolume*>& volumes)
-{
- if (!mRootVolp)
- {
- return;
- }
-
- volumes.push_back(mRootVolp);
-
- LLViewerObject::const_child_list_t& child_list = mRootVolp->getChildren();
- for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin();
- iter != child_list.end(); ++iter)
- {
- LLViewerObject* childp = *iter;
- LLVOVolume *child_volp = dynamic_cast<LLVOVolume*>(childp);
- if (child_volp && child_volp->isAnimatedObject())
- {
- volumes.push_back(child_volp);
- }
- }
-}
-
-// This is called after an associated object receives an animation
-// message. Combine the signaled animations for all associated objects
-// and process any resulting state changes.
-void LLControlAvatar::updateAnimations()
-{
- if (!mRootVolp)
- {
- LL_WARNS_ONCE("AnimatedObjectsNotify") << "No root vol" << LL_ENDL;
- return;
- }
-
- std::vector<LLVOVolume*> volumes;
- getAnimatedVolumes(volumes);
-
- // Rebuild mSignaledAnimations from the associated volumes.
- std::map<LLUUID, S32> anims;
- for (std::vector<LLVOVolume*>::iterator vol_it = volumes.begin(); vol_it != volumes.end(); ++vol_it)
- {
- LLVOVolume *volp = *vol_it;
- //LL_INFOS("AnimatedObjects") << "updating anim for vol " << volp->getID() << " root " << mRootVolp->getID() << LL_ENDL;
- signaled_animation_map_t& signaled_animations = LLObjectSignaledAnimationMap::instance().getMap()[volp->getID()];
- for (std::map<LLUUID,S32>::iterator anim_it = signaled_animations.begin();
- anim_it != signaled_animations.end();
- ++anim_it)
- {
- std::map<LLUUID,S32>::iterator found_anim_it = anims.find(anim_it->first);
- if (found_anim_it != anims.end())
- {
- // Animation already present, use the larger sequence id
- anims[anim_it->first] = llmax(found_anim_it->second, anim_it->second);
- }
- else
- {
- // Animation not already present, use this sequence id.
- anims[anim_it->first] = anim_it->second;
- }
- LL_DEBUGS("AnimatedObjectsNotify") << "found anim for vol " << volp->getID() << " anim " << anim_it->first << " root " << mRootVolp->getID() << LL_ENDL;
- }
- }
- if (!mPlaying)
- {
- mPlaying = true;
- //if (!mRootVolp->isAnySelected())
- {
- updateVolumeGeom();
- mRootVolp->recursiveMarkForUpdate();
- }
- }
-
- mSignaledAnimations = anims;
- processAnimationStateChanges();
-}
-
-// virtual
-LLViewerObject* LLControlAvatar::lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end,
- S32 face,
- bool pick_transparent,
- bool pick_rigged,
- bool pick_unselectable,
- S32* face_hit,
- LLVector4a* intersection,
- LLVector2* tex_coord,
- LLVector4a* normal,
- LLVector4a* tangent)
-{
- if (!mRootVolp)
- {
- return NULL;
- }
-
- LLViewerObject* hit = NULL;
-
- if (lineSegmentBoundingBox(start, end))
- {
- LLVector4a local_end = end;
- LLVector4a local_intersection;
- if (mRootVolp->lineSegmentIntersect(start, local_end, face, pick_transparent, pick_rigged, pick_unselectable, face_hit, &local_intersection, tex_coord, normal, tangent))
- {
- local_end = local_intersection;
- if (intersection)
- {
- *intersection = local_intersection;
- }
- hit = mRootVolp;
- }
- else
- {
- std::vector<LLVOVolume*> volumes;
- getAnimatedVolumes(volumes);
-
- for (std::vector<LLVOVolume*>::iterator vol_it = volumes.begin(); vol_it != volumes.end(); ++vol_it)
- {
- LLVOVolume *volp = *vol_it;
- if (mRootVolp != volp && volp->lineSegmentIntersect(start, local_end, face, pick_transparent, pick_rigged, pick_unselectable, face_hit, &local_intersection, tex_coord, normal, tangent))
- {
- local_end = local_intersection;
- if (intersection)
- {
- *intersection = local_intersection;
- }
- hit = volp;
- break;
- }
- }
- }
- }
-
- return hit;
-}
-
-// virtual
-std::string LLControlAvatar::getFullname() const
-{
- if (mRootVolp)
- {
- return "AO_" + mRootVolp->getID().getString();
- }
- else
- {
- return "AO_no_root_vol";
- }
-}
-
-// virtual
-bool LLControlAvatar::shouldRenderRigged() const
-{
- const LLVOAvatar *attached_av = getAttachedAvatar();
- if (attached_av)
- {
- return attached_av->shouldRenderRigged();
- }
- return true;
-}
-
-// virtual
-bool LLControlAvatar::isImpostor()
-{
- // Attached animated objects should match state of their attached av.
- LLVOAvatar *attached_av = getAttachedAvatar();
- if (attached_av)
- {
- return attached_av->isImpostor();
- }
- return LLVOAvatar::isImpostor();
-}
-
-//static
-void LLControlAvatar::onRegionChanged()
-{
- std::vector<LLCharacter*>::iterator it = LLCharacter::sInstances.begin();
- for ( ; it != LLCharacter::sInstances.end(); ++it)
- {
- LLControlAvatar* cav = dynamic_cast<LLControlAvatar*>(*it);
- if (!cav) continue;
- cav->mRegionChanged = true;
- }
-}
+/**
+ * @file llcontrolavatar.cpp
+ * @brief Implementation for special dummy avatar used to drive rigged meshes.
+ *
+ * $LicenseInfo:firstyear=2017&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2017, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llcontrolavatar.h"
+#include "llagent.h" // Get state values from here
+#include "llviewerobjectlist.h"
+#include "pipeline.h"
+#include "llanimationstates.h"
+#include "llviewercontrol.h"
+#include "llmeshrepository.h"
+#include "llviewerregion.h"
+#include "llskinningutil.h"
+
+const F32 LLControlAvatar::MAX_LEGAL_OFFSET = 3.0f;
+const F32 LLControlAvatar::MAX_LEGAL_SIZE = 64.0f;
+
+//static
+boost::signals2::connection LLControlAvatar::sRegionChangedSlot;
+
+LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewerRegion* regionp) :
+ LLVOAvatar(id, pcode, regionp),
+ mPlaying(false),
+ mGlobalScale(1.0f),
+ mMarkedForDeath(false),
+ mRootVolp(NULL),
+ mControlAVBridge(NULL),
+ mScaleConstraintFixup(1.0),
+ mRegionChanged(false)
+{
+ mIsDummy = true;
+ mIsControlAvatar = true;
+ mEnableDefaultMotions = false;
+}
+
+// virtual
+LLControlAvatar::~LLControlAvatar()
+{
+ // Should already have been unlinked before destruction
+ llassert(!mRootVolp);
+}
+
+// virtual
+void LLControlAvatar::initInstance()
+{
+ // Potential optimizations here: avoid creating system
+ // avatar mesh content since it's not used. For now we just clean some
+ // things up after the fact in releaseMeshData().
+ LLVOAvatar::initInstance();
+
+ createDrawable(&gPipeline);
+ updateJointLODs();
+ updateGeometry(mDrawable);
+ hideSkirt();
+
+ mInitFlags |= 1<<4;
+}
+
+const LLVOAvatar *LLControlAvatar::getAttachedAvatar() const
+{
+ if (mRootVolp && mRootVolp->isAttachment())
+ {
+ return mRootVolp->getAvatarAncestor();
+ }
+ return NULL;
+}
+
+LLVOAvatar *LLControlAvatar::getAttachedAvatar()
+{
+ if (mRootVolp && mRootVolp->isAttachment())
+ {
+ return mRootVolp->getAvatarAncestor();
+ }
+ return NULL;
+}
+
+void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_scale_fixup) const
+{
+ F32 max_legal_offset = MAX_LEGAL_OFFSET;
+ if (gSavedSettings.getControl("AnimatedObjectsMaxLegalOffset"))
+ {
+ max_legal_offset = gSavedSettings.getF32("AnimatedObjectsMaxLegalOffset");
+ }
+ max_legal_offset = llmax(max_legal_offset,0.f);
+
+ F32 max_legal_size = MAX_LEGAL_SIZE;
+ if (gSavedSettings.getControl("AnimatedObjectsMaxLegalSize"))
+ {
+ max_legal_size = gSavedSettings.getF32("AnimatedObjectsMaxLegalSize");
+ }
+ max_legal_size = llmax(max_legal_size, 1.f);
+
+ new_pos_fixup = LLVector3();
+ new_scale_fixup = 1.0f;
+ LLVector3 vol_pos = mRootVolp->getRenderPosition();
+
+ // Fix up position if needed to prevent visual encroachment
+ if (box_valid_and_non_zero(getLastAnimExtents())) // wait for state to settle down
+ {
+ // The goal here is to ensure that the extent of the avatar's
+ // bounding box does not wander too far from the
+ // official position of the corresponding volume. We
+ // do this by tracking the distance and applying a
+ // correction to the control avatar position if
+ // needed.
+ const LLVector3 *extents = getLastAnimExtents();
+ LLVector3 unshift_extents[2];
+ unshift_extents[0] = extents[0] - mPositionConstraintFixup;
+ unshift_extents[1] = extents[1] - mPositionConstraintFixup;
+ LLVector3 box_dims = extents[1]-extents[0];
+ F32 box_size = llmax(box_dims[0],box_dims[1],box_dims[2]);
+
+ if (!mRootVolp->isAttachment())
+ {
+ LLVector3 pos_box_offset = point_to_box_offset(vol_pos, unshift_extents);
+ F32 offset_dist = pos_box_offset.length();
+ if (offset_dist > MAX_LEGAL_OFFSET && offset_dist > 0.f)
+ {
+ F32 target_dist = (offset_dist - MAX_LEGAL_OFFSET);
+ new_pos_fixup = (target_dist/offset_dist)*pos_box_offset;
+ }
+ if (new_pos_fixup != mPositionConstraintFixup)
+ {
+ LL_DEBUGS("ConstraintFix") << getFullname() << " pos fix, offset_dist " << offset_dist << " pos fixup "
+ << new_pos_fixup << " was " << mPositionConstraintFixup << LL_ENDL;
+ LL_DEBUGS("ConstraintFix") << "vol_pos " << vol_pos << LL_ENDL;
+ LL_DEBUGS("ConstraintFix") << "extents " << extents[0] << " " << extents[1] << LL_ENDL;
+ LL_DEBUGS("ConstraintFix") << "unshift_extents " << unshift_extents[0] << " " << unshift_extents[1] << LL_ENDL;
+
+ }
+ }
+ if (box_size/mScaleConstraintFixup > MAX_LEGAL_SIZE)
+ {
+ new_scale_fixup = mScaleConstraintFixup* MAX_LEGAL_SIZE /box_size;
+ LL_DEBUGS("ConstraintFix") << getFullname() << " scale fix, box_size " << box_size << " fixup "
+ << mScaleConstraintFixup << " max legal " << MAX_LEGAL_SIZE
+ << " -> new scale " << new_scale_fixup << LL_ENDL;
+ }
+ }
+}
+
+void LLControlAvatar::matchVolumeTransform()
+{
+ if (mRootVolp)
+ {
+ LLVector3 new_pos_fixup;
+ F32 new_scale_fixup;
+ if (mRegionChanged)
+ {
+ new_scale_fixup = mScaleConstraintFixup;
+ new_pos_fixup = mPositionConstraintFixup;
+ mRegionChanged = false;
+ }
+ else
+ {
+ getNewConstraintFixups(new_pos_fixup, new_scale_fixup);
+ }
+ mPositionConstraintFixup = new_pos_fixup;
+ mScaleConstraintFixup = new_scale_fixup;
+
+ if (mRootVolp->isAttachment())
+ {
+ LLVOAvatar *attached_av = getAttachedAvatar();
+ if (attached_av)
+ {
+ LLViewerJointAttachment *attach = attached_av->getTargetAttachmentPoint(mRootVolp);
+ if (getRegion() && !isDead())
+ {
+ 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());
+
+ setGlobalScale(mScaleConstraintFixup);
+ }
+ else
+ {
+ LL_WARNS_ONCE() << "can't find attached av!" << LL_ENDL;
+ }
+ }
+ else
+ {
+ LLVector3 vol_pos = mRootVolp->getRenderPosition();
+
+ // FIXME: Currently if you're doing something like playing an
+ // animation that moves the pelvis (on an avatar or
+ // animated object), the name tag and debug text will be
+ // left behind. Ideally setPosition() would follow the
+ // skeleton around in a smarter way, so name tags,
+ // complexity info and such line up better. Should defer
+ // this until avatars also get fixed.
+
+ LLQuaternion obj_rot;
+ if (mRootVolp->mDrawable)
+ {
+ obj_rot = mRootVolp->mDrawable->getRotation();
+ }
+ else
+ {
+ obj_rot = mRootVolp->getRotation();
+ }
+
+ LLMatrix3 bind_mat;
+
+ LLQuaternion bind_rot;
+#define MATCH_BIND_SHAPE
+#ifdef MATCH_BIND_SHAPE
+ // MAINT-8671 - based on a patch from Beq Janus
+ const LLMeshSkinInfo* skin_info = mRootVolp->getSkinInfo();
+ if (skin_info)
+ {
+ LL_DEBUGS("BindShape") << getFullname() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL;
+ bind_rot = LLSkinningUtil::getUnscaledQuaternion(LLMatrix4(skin_info->mBindShapeMatrix));
+ }
+#endif
+ setRotation(bind_rot*obj_rot);
+ mRoot->setWorldRotation(bind_rot*obj_rot);
+ if (getRegion() && !isDead())
+ {
+ setPositionAgent(vol_pos);
+ }
+ mRoot->setPosition(vol_pos + mPositionConstraintFixup);
+
+ setGlobalScale(mScaleConstraintFixup);
+ }
+ }
+}
+
+void LLControlAvatar::setGlobalScale(F32 scale)
+{
+ if (scale <= 0.0)
+ {
+ LL_WARNS() << "invalid global scale " << scale << LL_ENDL;
+ return;
+ }
+ if (scale != mGlobalScale)
+ {
+ F32 adjust_scale = scale/mGlobalScale;
+ LL_INFOS() << "scale " << scale << " adjustment " << adjust_scale << LL_ENDL;
+ // should we be scaling from the pelvis or the root?
+ recursiveScaleJoint(mPelvisp,adjust_scale);
+ mGlobalScale = scale;
+ }
+}
+
+void LLControlAvatar::recursiveScaleJoint(LLJoint* joint, F32 factor)
+{
+ joint->setScale(factor * joint->getScale());
+
+ for (LLJoint::joints_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
+
+ 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())
+ {
+ 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);
+ mRootVolp->markForUpdate();
+
+ // Note that attachment overrides aren't needed here, have already
+ // been applied at the time the mControlAvatar was created, in
+ // llvovolume.cpp.
+
+ matchVolumeTransform();
+
+ // Initial exploration of allowing scaling skeleton to match root
+ // prim bounding box. If enabled, would probably be controlled by
+ // an additional checkbox and default to off. Not enabled for
+ // initial release.
+
+ // What should the scale be? What we really want is the ratio
+ // between the scale at which the object was originally designed
+ // and rigged, and the scale to which it has been subsequently
+ // modified - for example, if the object has been scaled down by a
+ // factor of 2 then we should use 0.5 as the global scale. But we
+ // don't have the original scale stored anywhere, just the current
+ // scale. Possibilities - 1) remember the original scale
+ // somewhere, 2) add another field to let the user specify the
+ // global scale, 3) approximate the original scale by looking at
+ // the proportions of the skeleton after joint positions have
+ // been applied
+
+ //LLVector3 obj_scale = obj->getScale();
+ //F32 obj_scale_z = llmax(obj_scale[2],0.1f);
+ //setGlobalScale(obj_scale_z/2.0f); // roughly fit avatar height range (2m) into object height
+}
+
+LLControlAvatar *LLControlAvatar::createControlAvatar(LLVOVolume *obj)
+{
+ LLControlAvatar *cav = (LLControlAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), CO_FLAG_CONTROL_AVATAR);
+
+ if (cav)
+ {
+ cav->mRootVolp = obj;
+
+ // Sync up position/rotation with object
+ cav->matchVolumeTransform();
+ }
+
+ return cav;
+}
+
+void LLControlAvatar::markForDeath()
+{
+ mMarkedForDeath = true;
+ // object unlinked cav and might be dead already
+ // might need to clean mControlAVBridge here as well
+ mRootVolp = NULL;
+}
+
+void LLControlAvatar::idleUpdate(LLAgent &agent, const F64 &time)
+{
+ if (mMarkedForDeath)
+ {
+ markDead();
+ mMarkedForDeath = false;
+ }
+ else
+ {
+ LLVOAvatar::idleUpdate(agent,time);
+ }
+}
+
+void LLControlAvatar::markDead()
+{
+ mRootVolp = NULL;
+ super::markDead();
+ mControlAVBridge = NULL;
+}
+
+bool LLControlAvatar::computeNeedsUpdate()
+{
+ computeUpdatePeriod();
+
+ // Animesh attachments are a special case. Should have the same update cadence as their attached parent avatar.
+ LLVOAvatar *attached_av = getAttachedAvatar();
+ if (attached_av)
+ {
+ // Have to run computeNeedsUpdate() for attached av in
+ // case it hasn't run updateCharacter() already this
+ // frame. Note this means that the attached av will
+ // run computeNeedsUpdate() multiple times per frame
+ // if it has animesh attachments. Results will be
+ // consistent except for the corner case of exceeding
+ // MAX_IMPOSTOR_INTERVAL in one call but not another,
+ // which should be rare.
+ attached_av->computeNeedsUpdate();
+ mNeedsImpostorUpdate = attached_av->mNeedsImpostorUpdate;
+ if (mNeedsImpostorUpdate)
+ {
+ mLastImpostorUpdateReason = 12;
+ }
+ return mNeedsImpostorUpdate;
+ }
+ return LLVOAvatar::computeNeedsUpdate();
+}
+
+bool LLControlAvatar::updateCharacter(LLAgent &agent)
+{
+ return LLVOAvatar::updateCharacter(agent);
+}
+
+//virtual
+void LLControlAvatar::updateDebugText()
+{
+ if (gSavedSettings.getBOOL("DebugAnimatedObjects"))
+ {
+ S32 total_linkset_count = 0;
+ if (mRootVolp)
+ {
+ total_linkset_count = 1 + mRootVolp->getChildren().size();
+ }
+ std::vector<LLVOVolume*> volumes;
+ getAnimatedVolumes(volumes);
+ S32 animated_volume_count = volumes.size();
+ std::string active_string;
+ std::string type_string;
+ std::string lod_string;
+ std::string animated_object_flag_string;
+ S32 total_tris = 0;
+ S32 total_verts = 0;
+ F32 est_tris = 0.f;
+ F32 est_streaming_tris = 0.f;
+ F32 streaming_cost = 0.f;
+ std::string cam_dist_string = "";
+ S32 cam_dist_count = 0;
+ F32 lod_radius = mRootVolp ? mRootVolp->mLODRadius : 0.f;
+
+ for (std::vector<LLVOVolume*>::iterator it = volumes.begin();
+ it != volumes.end(); ++it)
+ {
+ LLVOVolume *volp = *it;
+ S32 verts = 0;
+ total_tris += volp->getTriangleCount(&verts);
+ total_verts += verts;
+ est_tris += volp->getEstTrianglesMax();
+ est_streaming_tris += volp->getEstTrianglesStreamingCost();
+ streaming_cost += volp->getStreamingCost();
+ lod_string += llformat("%d",volp->getLOD());
+ if (volp && volp->mDrawable)
+ {
+ bool is_animated_flag = volp->getExtendedMeshFlags() & LLExtendedMeshParams::ANIMATED_MESH_ENABLED_FLAG;
+ if (is_animated_flag)
+ {
+ animated_object_flag_string += "1";
+ }
+ else
+ {
+ animated_object_flag_string += "0";
+ }
+ if (volp->mDrawable->isActive())
+ {
+ active_string += "A";
+ }
+ else
+ {
+ active_string += "S";
+ }
+ if (volp->isRiggedMesh())
+ {
+ // Rigged/animatable mesh
+ type_string += "R";
+ lod_radius = volp->mLODRadius;
+ }
+ else if (volp->isMesh())
+ {
+ // Static mesh
+ type_string += "M";
+ }
+ else
+ {
+ // Any other prim
+ type_string += "P";
+ }
+ if (cam_dist_count < 4)
+ {
+ cam_dist_string += LLStringOps::getReadableNumber(volp->mLODDistance) + "/" +
+ LLStringOps::getReadableNumber(volp->mLODAdjustedDistance) + " ";
+ cam_dist_count++;
+ }
+ }
+ else
+ {
+ active_string += "-";
+ type_string += "-";
+ }
+ }
+ addDebugText(llformat("CAV obj %d anim %d active %s impost %d upprd %d strcst %f",
+ total_linkset_count, animated_volume_count,
+ active_string.c_str(), (S32) isImpostor(), getUpdatePeriod(), streaming_cost));
+ addDebugText(llformat("types %s lods %s", type_string.c_str(), lod_string.c_str()));
+ addDebugText(llformat("flags %s", animated_object_flag_string.c_str()));
+ addDebugText(llformat("tris %d (est %.1f, streaming %.1f), verts %d", total_tris, est_tris, est_streaming_tris, total_verts));
+ addDebugText(llformat("pxarea %s rank %d", LLStringOps::getReadableNumber(getPixelArea()).c_str(), getVisibilityRank()));
+ addDebugText(llformat("lod_radius %s dists %s", LLStringOps::getReadableNumber(lod_radius).c_str(),cam_dist_string.c_str()));
+ if (mPositionConstraintFixup.length() > 0.0f || mScaleConstraintFixup != 1.0f)
+ {
+ addDebugText(llformat("pos fix (%.1f %.1f %.1f) scale %f",
+ mPositionConstraintFixup[0],
+ mPositionConstraintFixup[1],
+ mPositionConstraintFixup[2],
+ mScaleConstraintFixup));
+ }
+
+#if 0
+ std::string region_name = "no region";
+ if (mRootVolp->getRegion())
+ {
+ region_name = mRootVolp->getRegion()->getName();
+ }
+ std::string skel_region_name = "skel no region";
+ if (getRegion())
+ {
+ skel_region_name = getRegion()->getName();
+ }
+ addDebugText(llformat("region %x %s skel %x %s",
+ mRootVolp->getRegion(), region_name.c_str(),
+ getRegion(), skel_region_name.c_str()));
+#endif
+
+ }
+ LLVOAvatar::updateDebugText();
+}
+
+void LLControlAvatar::getAnimatedVolumes(std::vector<LLVOVolume*>& volumes)
+{
+ if (!mRootVolp)
+ {
+ return;
+ }
+
+ volumes.push_back(mRootVolp);
+
+ LLViewerObject::const_child_list_t& child_list = mRootVolp->getChildren();
+ for (LLViewerObject::const_child_list_t::const_iterator iter = child_list.begin();
+ iter != child_list.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ LLVOVolume *child_volp = dynamic_cast<LLVOVolume*>(childp);
+ if (child_volp && child_volp->isAnimatedObject())
+ {
+ volumes.push_back(child_volp);
+ }
+ }
+}
+
+// This is called after an associated object receives an animation
+// message. Combine the signaled animations for all associated objects
+// and process any resulting state changes.
+void LLControlAvatar::updateAnimations()
+{
+ if (!mRootVolp)
+ {
+ LL_WARNS_ONCE("AnimatedObjectsNotify") << "No root vol" << LL_ENDL;
+ return;
+ }
+
+ std::vector<LLVOVolume*> volumes;
+ getAnimatedVolumes(volumes);
+
+ // Rebuild mSignaledAnimations from the associated volumes.
+ std::map<LLUUID, S32> anims;
+ for (std::vector<LLVOVolume*>::iterator vol_it = volumes.begin(); vol_it != volumes.end(); ++vol_it)
+ {
+ LLVOVolume *volp = *vol_it;
+ //LL_INFOS("AnimatedObjects") << "updating anim for vol " << volp->getID() << " root " << mRootVolp->getID() << LL_ENDL;
+ signaled_animation_map_t& signaled_animations = LLObjectSignaledAnimationMap::instance().getMap()[volp->getID()];
+ for (std::map<LLUUID,S32>::iterator anim_it = signaled_animations.begin();
+ anim_it != signaled_animations.end();
+ ++anim_it)
+ {
+ std::map<LLUUID,S32>::iterator found_anim_it = anims.find(anim_it->first);
+ if (found_anim_it != anims.end())
+ {
+ // Animation already present, use the larger sequence id
+ anims[anim_it->first] = llmax(found_anim_it->second, anim_it->second);
+ }
+ else
+ {
+ // Animation not already present, use this sequence id.
+ anims[anim_it->first] = anim_it->second;
+ }
+ LL_DEBUGS("AnimatedObjectsNotify") << "found anim for vol " << volp->getID() << " anim " << anim_it->first << " root " << mRootVolp->getID() << LL_ENDL;
+ }
+ }
+ if (!mPlaying)
+ {
+ mPlaying = true;
+ //if (!mRootVolp->isAnySelected())
+ {
+ updateVolumeGeom();
+ mRootVolp->recursiveMarkForUpdate();
+ }
+ }
+
+ mSignaledAnimations = anims;
+ processAnimationStateChanges();
+}
+
+// virtual
+LLViewerObject* LLControlAvatar::lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end,
+ S32 face,
+ bool pick_transparent,
+ bool pick_rigged,
+ bool pick_unselectable,
+ S32* face_hit,
+ LLVector4a* intersection,
+ LLVector2* tex_coord,
+ LLVector4a* normal,
+ LLVector4a* tangent)
+{
+ if (!mRootVolp)
+ {
+ return NULL;
+ }
+
+ LLViewerObject* hit = NULL;
+
+ if (lineSegmentBoundingBox(start, end))
+ {
+ LLVector4a local_end = end;
+ LLVector4a local_intersection;
+ if (mRootVolp->lineSegmentIntersect(start, local_end, face, pick_transparent, pick_rigged, pick_unselectable, face_hit, &local_intersection, tex_coord, normal, tangent))
+ {
+ local_end = local_intersection;
+ if (intersection)
+ {
+ *intersection = local_intersection;
+ }
+ hit = mRootVolp;
+ }
+ else
+ {
+ std::vector<LLVOVolume*> volumes;
+ getAnimatedVolumes(volumes);
+
+ for (std::vector<LLVOVolume*>::iterator vol_it = volumes.begin(); vol_it != volumes.end(); ++vol_it)
+ {
+ LLVOVolume *volp = *vol_it;
+ if (mRootVolp != volp && volp->lineSegmentIntersect(start, local_end, face, pick_transparent, pick_rigged, pick_unselectable, face_hit, &local_intersection, tex_coord, normal, tangent))
+ {
+ local_end = local_intersection;
+ if (intersection)
+ {
+ *intersection = local_intersection;
+ }
+ hit = volp;
+ break;
+ }
+ }
+ }
+ }
+
+ return hit;
+}
+
+// virtual
+std::string LLControlAvatar::getFullname() const
+{
+ if (mRootVolp)
+ {
+ return "AO_" + mRootVolp->getID().getString();
+ }
+ else
+ {
+ return "AO_no_root_vol";
+ }
+}
+
+// virtual
+bool LLControlAvatar::shouldRenderRigged() const
+{
+ const LLVOAvatar *attached_av = getAttachedAvatar();
+ if (attached_av)
+ {
+ return attached_av->shouldRenderRigged();
+ }
+ return true;
+}
+
+// virtual
+bool LLControlAvatar::isImpostor()
+{
+ // Attached animated objects should match state of their attached av.
+ LLVOAvatar *attached_av = getAttachedAvatar();
+ if (attached_av)
+ {
+ return attached_av->isImpostor();
+ }
+ return LLVOAvatar::isImpostor();
+}
+
+//static
+void LLControlAvatar::onRegionChanged()
+{
+ std::vector<LLCharacter*>::iterator it = LLCharacter::sInstances.begin();
+ for ( ; it != LLCharacter::sInstances.end(); ++it)
+ {
+ LLControlAvatar* cav = dynamic_cast<LLControlAvatar*>(*it);
+ if (!cav) continue;
+ cav->mRegionChanged = true;
+ }
+}