123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600 |
- /**
- * @file lldriverparam.cpp
- * @brief A visual parameter that drives (controls) other visual parameters.
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "lldriverparam.h"
- #include "llavatarappearance.h"
- #include "llwearable.h"
- #include "llwearabledata.h"
- //-----------------------------------------------------------------------------
- // LLDriverParamInfo
- //-----------------------------------------------------------------------------
- LLDriverParamInfo::LLDriverParamInfo()
- : mDriverParam(NULL)
- {
- }
- bool LLDriverParamInfo::parseXml(LLXmlTreeNode* node)
- {
- llassert(node->hasName("param") && node->getChildByName("param_driver"));
- if (!LLViewerVisualParamInfo::parseXml(node))
- {
- return false;
- }
- LLXmlTreeNode* param_driver_node = node->getChildByName("param_driver");
- if (!param_driver_node)
- {
- return false;
- }
- for (LLXmlTreeNode* child = param_driver_node->getChildByName("driven");
- child; child = param_driver_node->getNextNamedChild())
- {
- S32 driven_id;
- static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
- if (child->getFastAttributeS32(id_string, driven_id))
- {
- F32 min1 = mMinWeight;
- F32 max1 = mMaxWeight;
- F32 max2 = max1;
- F32 min2 = max1;
- // driven ________ //
- // ^ /| |\ //
- // | / | | \ //
- // | / | | \ //
- // | / | | \ //
- // | / | | \ //
- //-------|----|-------|----|-------> driver //
- // | min1 max1 max2 min2
- static LLStdStringHandle min1_string = LLXmlTree::addAttributeString("min1");
- child->getFastAttributeF32(min1_string, min1); // optional
- static LLStdStringHandle max1_string = LLXmlTree::addAttributeString("max1");
- child->getFastAttributeF32(max1_string, max1); // optional
- static LLStdStringHandle max2_string = LLXmlTree::addAttributeString("max2");
- child->getFastAttributeF32(max2_string, max2); // optional
- static LLStdStringHandle min2_string = LLXmlTree::addAttributeString("min2");
- child->getFastAttributeF32(min2_string, min2); // optional
- // Push these on the front of the deque, so that we can construct
- // them in order later (faster)
- mDrivenInfoList.emplace_front(driven_id, min1, max1, max2, min2);
- }
- else
- {
- llerrs << "<driven> Unable to resolve driven parameter: "
- << driven_id << llendl;
- return false;
- }
- }
- return true;
- }
- //-----------------------------------------------------------------------------
- // LLDriverParam
- //-----------------------------------------------------------------------------
- LLDriverParam::LLDriverParam(LLAvatarAppearance* appearance,
- LLWearable* wearable)
- : LLViewerVisualParam(),
- mDefaultVec(),
- mDriven(),
- mCurrentDistortionParam(NULL),
- mAvatarAppearance(appearance),
- mWearablep(wearable)
- {
- llassert(mAvatarAppearance);
- llassert(mWearablep == NULL || mAvatarAppearance->isSelf());
- mDefaultVec.clear();
- }
- LLDriverParam::LLDriverParam(const LLDriverParam& other)
- : LLViewerVisualParam(other),
- mDefaultVec(other.mDefaultVec),
- mDriven(other.mDriven),
- mCurrentDistortionParam(other.mCurrentDistortionParam),
- mAvatarAppearance(other.mAvatarAppearance),
- mWearablep(other.mWearablep)
- {
- llassert(mAvatarAppearance);
- llassert(mWearablep == NULL || mAvatarAppearance->isSelf());
- }
- bool LLDriverParam::setInfo(LLDriverParamInfo* info)
- {
- llassert(mInfo == NULL);
- if (info->mID < 0)
- {
- return false;
- }
- mInfo = info;
- mID = info->mID;
- info->mDriverParam = this;
- setWeight(getDefaultWeight(), false);
- return true;
- }
- //virtual
- LLViewerVisualParam* LLDriverParam::cloneParam(LLWearable* wearable) const
- {
- llassert(wearable);
- return new LLDriverParam(*this);
- }
- void LLDriverParam::setWeight(F32 weight, bool upload_bake)
- {
- F32 min_weight = getMinWeight();
- F32 max_weight = getMaxWeight();
- if (mIsAnimating)
- {
- // allow overshoot when animating
- mCurWeight = weight;
- }
- else
- {
- mCurWeight = llclamp(weight, min_weight, max_weight);
- }
- // driven ________
- // ^ /| |\ ^
- // | / | | \ |
- // | / | | \ |
- // | / | | \ |
- // | / | | \ |
- //-------|----|-------|----|-------> driver
- // | min1 max1 max2 min2
- for (entry_list_t::iterator iter = mDriven.begin(), end = mDriven.end();
- iter != end; ++iter)
- {
- LLDrivenEntry* driven = &(*iter);
- LLDrivenEntryInfo* info = driven->mInfo;
- LLViewerVisualParam* param = driven->mParam;
- F32 driven_weight = 0.f;
- F32 driven_min = param->getMinWeight();
- F32 driven_max = param->getMaxWeight();
- if (mIsAnimating)
- {
- // driven param doesn't interpolate (textures, for example)
- if (!param->getAnimating())
- {
- continue;
- }
- if (mCurWeight < info->mMin1)
- {
- if (info->mMin1 == min_weight)
- {
- if (info->mMin1 == info->mMax1)
- {
- driven_weight = driven_max;
- }
- else
- {
- // Up-slope extrapolation
- F32 t = (mCurWeight - info->mMin1) /
- (info->mMax1 - info->mMin1);
- driven_weight = driven_min +
- t * (driven_max - driven_min);
- }
- }
- else
- {
- driven_weight = driven_min;
- }
- setDrivenWeight(driven, driven_weight, upload_bake);
- continue;
- }
- else if (mCurWeight > info->mMin2)
- {
- if (info->mMin2 == max_weight)
- {
- if (info->mMin2 == info->mMax2)
- {
- driven_weight = driven_max;
- }
- else
- {
- // Down-slope extrapolation
- F32 t = (mCurWeight - info->mMax2) /
- (info->mMin2 - info->mMax2);
- driven_weight = driven_max +
- t * (driven_min - driven_max);
- }
- }
- else
- {
- driven_weight = driven_min;
- }
- setDrivenWeight(driven, driven_weight, upload_bake);
- continue;
- }
- }
- driven_weight = getDrivenWeight(driven, mCurWeight);
- setDrivenWeight(driven,driven_weight,upload_bake);
- }
- }
- #if 0 // Unused methods
- F32 LLDriverParam::getTotalDistortion()
- {
- F32 sum = 0.f;
- for (entry_list_t::iterator iter = mDriven.begin(), end = mDriven.end();
- iter != end; ++iter)
- {
- LLDrivenEntry* driven = &(*iter);
- sum += driven->mParam->getTotalDistortion();
- }
- return sum;
- }
- const LLVector4a& LLDriverParam::getAvgDistortion()
- {
- // It is actually incorrect to take the average of averages, but it is good
- // enough here.
- LLVector4a sum;
- sum.clear();
- S32 count = 0;
- for (entry_list_t::iterator iter = mDriven.begin(), end = mDriven.end();
- iter != end; ++iter)
- {
- LLDrivenEntry* driven = &(*iter);
- sum.add(driven->mParam->getAvgDistortion());
- ++count;
- }
- sum.mul(1.f / (F32)count);
- mDefaultVec = sum;
- return mDefaultVec;
- }
- F32 LLDriverParam::getMaxDistortion()
- {
- F32 max = 0.f;
- for (entry_list_t::iterator iter = mDriven.begin(), end = mDriven.end();
- iter != end; ++iter)
- {
- LLDrivenEntry* driven = &(*iter);
- F32 param_max = driven->mParam->getMaxDistortion();
- if (param_max > max)
- {
- max = param_max;
- }
- }
- return max;
- }
- LLVector4a LLDriverParam::getVertexDistortion(S32 index, LLPolyMesh* poly_mesh)
- {
- LLVector4a sum;
- sum.clear();
- for (entry_list_t::iterator iter = mDriven.begin(), end = mDriven.end();
- iter != end; ++iter)
- {
- LLDrivenEntry* driven = &(*iter);
- sum.add(driven->mParam->getVertexDistortion(index, poly_mesh));
- }
- return sum;
- }
- const LLVector4a* LLDriverParam::getFirstDistortion(U32* index,
- LLPolyMesh** poly_mesh)
- {
- mCurrentDistortionParam = NULL;
- const LLVector4a* v = NULL;
- for (entry_list_t::iterator iter = mDriven.begin(), end = mDriven.end();
- iter != end; ++iter)
- {
- LLDrivenEntry* driven = &(*iter);
- v = driven->mParam->getFirstDistortion(index, poly_mesh);
- if (v)
- {
- mCurrentDistortionParam = driven->mParam;
- break;
- }
- }
- return v;
- }
- const LLVector4a* LLDriverParam::getNextDistortion(U32* index,
- LLPolyMesh** poly_mesh)
- {
- llassert(mCurrentDistortionParam);
- if (!mCurrentDistortionParam)
- {
- return NULL;
- }
- LLDrivenEntry* driven = NULL;
- entry_list_t::iterator iter;
- entry_list_t::iterator end = mDriven.end();
- // Set mDriven iteration to the right point
- for (iter = mDriven.begin(); iter != end; ++iter)
- {
- driven = &(*iter);
- if (driven->mParam == mCurrentDistortionParam)
- {
- break;
- }
- }
- llassert(driven);
- if (!driven)
- {
- return NULL; // shouldn't happen, but...
- }
- // We are already in the middle of a param's distortions, so get the next
- // one.
- const LLVector4a* v = driven->mParam->getNextDistortion(index, poly_mesh);
- if (!v && iter != end)
- {
- // This param is finished, so start the next param. It might not have
- // any distortions, though, so we have to loop to find the next param
- // that does.
- for (++iter; iter != end; ++iter)
- {
- driven = &(*iter);
- v = driven->mParam->getFirstDistortion(index, poly_mesh);
- if (v)
- {
- mCurrentDistortionParam = driven->mParam;
- break;
- }
- }
- }
- return v;
- };
- #endif
- S32 LLDriverParam::getDrivenParamsCount() const
- {
- return mDriven.size();
- }
- const LLViewerVisualParam* LLDriverParam::getDrivenParam(S32 index) const
- {
- if (0 > index || index >= (S32)mDriven.size())
- {
- return NULL;
- }
- return mDriven[index].mParam;
- }
- void LLDriverParam::setAnimationTarget(F32 target_value, bool upload_bake)
- {
- LLVisualParam::setAnimationTarget(target_value, upload_bake);
- for (entry_list_t::iterator iter = mDriven.begin(), end = mDriven.end();
- iter != end; ++iter)
- {
- LLDrivenEntry* driven = &(*iter);
- F32 driven_weight = getDrivenWeight(driven, mTargetWeight);
- // this isn't normally necessary, as driver params handle interpolation
- // of their driven params but texture params need to know to assume
- // their final value at beginning of interpolation
- driven->mParam->setAnimationTarget(driven_weight, upload_bake);
- }
- }
- void LLDriverParam::stopAnimating(bool upload_bake)
- {
- LLVisualParam::stopAnimating(upload_bake);
- for (entry_list_t::iterator iter = mDriven.begin(), end = mDriven.end();
- iter != end; ++iter)
- {
- LLDrivenEntry* driven = &(*iter);
- driven->mParam->setAnimating(false);
- }
- }
- //virtual
- bool LLDriverParam::linkDrivenParams(visual_param_mapper mapper,
- bool only_cross_params)
- {
- bool success = true;
- for (LLDriverParamInfo::entry_info_list_t::iterator
- iter = getInfo()->mDrivenInfoList.begin(),
- end = getInfo()->mDrivenInfoList.end();
- iter != end; ++iter)
- {
- LLDrivenEntryInfo* driven_info = &(*iter);
- S32 driven_id = driven_info->mDrivenID;
- // Check for already existing links. Do not overwrite.
- bool found = false;
- for (entry_list_t::iterator driven_iter = mDriven.begin(),
- driven_end = mDriven.end();
- driven_iter != driven_end; ++driven_iter)
- {
- if (driven_iter->mInfo->mDrivenID == driven_id)
- {
- found = true;
- break;
- }
- }
- if (!found)
- {
- LLViewerVisualParam* param =
- (LLViewerVisualParam*)mapper(driven_id);
- if (param)
- {
- param->setParamLocation(this->getParamLocation());
- }
- if (param &&
- (!only_cross_params || param->getCrossWearable()))
- {
- mDriven.emplace_back(param, driven_info);
- }
- else
- {
- success = false;
- }
- }
- }
- return success;
- }
- void LLDriverParam::resetDrivenParams()
- {
- mDriven.clear();
- mDriven.reserve(getInfo()->mDrivenInfoList.size());
- }
- void LLDriverParam::updateCrossDrivenParams(LLWearableType::EType driven_type)
- {
- bool needs_update = getWearableType() == driven_type;
- // if the driver has a driven entry for the passed-in wearable type, we
- // need to refresh the value
- for (entry_list_t::iterator iter = mDriven.begin(), end = mDriven.end();
- iter != end; ++iter)
- {
- LLDrivenEntry* driven = &(*iter);
- LLViewerVisualParam* param = driven ? driven->mParam : NULL;
- if (param && param->getCrossWearable() &&
- param->getWearableType() == driven_type)
- {
- needs_update = true;
- }
- }
- if (needs_update)
- {
- LLWearableType::EType driver_type = (LLWearableType::EType)getWearableType();
- // If we've gotten here, we've added a new wearable of type "type"
- // Thus this wearable needs to get updates from the driver wearable.
- // The call to setVisualParamWeight seems redundant, but is necessary
- // as the number of driven wearables has changed since the last update.
- LLWearable* wearable;
- wearable = mAvatarAppearance->getWearableData()->getTopWearable(driver_type);
- if (wearable)
- {
- wearable->setVisualParamWeight(mID,
- wearable->getVisualParamWeight(mID),
- false);
- }
- }
- }
- F32 LLDriverParam::getDrivenWeight(const LLDrivenEntry* driven,
- F32 input_weight)
- {
- F32 min_weight = getMinWeight();
- F32 max_weight = getMaxWeight();
- const LLDrivenEntryInfo* info = driven->mInfo;
- F32 driven_weight = 0.f;
- F32 driven_min = driven->mParam->getMinWeight();
- F32 driven_max = driven->mParam->getMaxWeight();
- if (input_weight <= info->mMin1)
- {
- if (info->mMin1 == info->mMax1 && info->mMin1 <= min_weight)
- {
- driven_weight = driven_max;
- }
- else
- {
- driven_weight = driven_min;
- }
- }
- else if (input_weight <= info->mMax1)
- {
- F32 t = (input_weight - info->mMin1) / (info->mMax1 - info->mMin1);
- driven_weight = driven_min + t * (driven_max - driven_min);
- }
- else if (input_weight <= info->mMax2)
- {
- driven_weight = driven_max;
- }
- else if (input_weight <= info->mMin2)
- {
- F32 t = (input_weight - info->mMax2) / (info->mMin2 - info->mMax2);
- driven_weight = driven_max + t * (driven_min - driven_max);
- }
- else
- {
- if (info->mMax2 >= max_weight)
- {
- driven_weight = driven_max;
- }
- else
- {
- driven_weight = driven_min;
- }
- }
- return driven_weight;
- }
- void LLDriverParam::setDrivenWeight(LLDrivenEntry* driven, F32 driven_weight,
- bool upload_bake)
- {
- if (mWearablep && mAvatarAppearance->isValid() &&
- driven->mParam->getCrossWearable() &&
- mAvatarAppearance->getWearableData()->isOnTop(mWearablep))
- {
- // Call setWeight through LLVOAvatarSelf so other wearables can be
- // updated with the correct values
- mAvatarAppearance->setVisualParamWeight((LLVisualParam*)driven->mParam,
- driven_weight, upload_bake);
- }
- else
- {
- driven->mParam->setWeight(driven_weight, upload_bake);
- }
- }
|