12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984 |
- /**
- * @file llik.cpp
- * @brief Implementation of LLIK::Solver class and related helpers.
- *
- * $LicenseInfo:firstyear=2021&license=viewergpl$
- *
- * Copyright (c) 2021, 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 <algorithm>
- #include <sstream>
- #include "linden_common.h"
- #include "llik.h"
- #include "lldir.h"
- #include "lljoint.h"
- #include "llsdserialize.h"
- #include "hbxxh.h"
- static const std::string NULL_CONSTRAINT_NAME("NULL_CONSTRAINT");
- static const std::string SIMPLE_CONE_NAME("SIMPLE_CONE");
- static const std::string TWIST_LIMITED_CONE_NAME("TWIST_LIMITED_CONE");
- static const std::string ELBOW_NAME("ELBOW");
- static const std::string KNEE_NAME("KNEE");
- static const std::string ACUTE_ELLIPSOIDAL_NAME("ACUTE_ELLIPSOIDAL_CONE");
- static const std::string DOUBLE_LIMITED_HINGE_NAME("DOUBLE_LIMITED_HINGE");
- static const std::string UNKNOWN_CONSTRAINT_NAME("UNKNOWN_CONSTRAINT");
- namespace LLIK
- {
- // Utility function for truncating angle to range: [0, F_TWO_PI[
- static F32 remove_multiples_of_two_pi(F32 angle)
- {
- return angle - F_TWO_PI * (S32)(angle / F_TWO_PI);
- }
- // Utility function for clamping angle limits in range [-PI, PI]. Note:
- // arguments are passed by reference and modified as side-effect
- static void compute_angle_limits(F32& min_angle, F32& max_angle)
- {
- max_angle = remove_multiples_of_two_pi(max_angle);
- if (max_angle > F_PI)
- {
- max_angle -= F_TWO_PI;
- }
- min_angle = remove_multiples_of_two_pi(min_angle);
- if (min_angle > F_PI)
- {
- min_angle -= F_TWO_PI;
- }
- if (min_angle > max_angle)
- {
- F32 temp = min_angle;
- min_angle = max_angle;
- max_angle = temp;
- }
- }
- // Utility function for clamping angle between two limits. Consider angle
- // limits: min_angle and max_angle with axis out of the page. There exists an
- // "invalid bisector" angle which splits the invalid zone between the that
- // which is closest to mMinBend or mMaxBend.
- //
- // max_angle
- // `
- // `
- // `
- // (o)--------> 0
- // .-' `
- // .-' `
- // .-' `
- // invalid_bisector min_angle
- //
- static F32 compute_clamped_angle(F32 angle, F32 min_angle, F32 max_angle)
- {
- F32 invalid_bisector = max_angle +
- 0.5f * (F_TWO_PI - (max_angle - min_angle));
- if ((angle > max_angle && angle < invalid_bisector) ||
- angle < invalid_bisector - F_TWO_PI)
- {
- return max_angle;
- }
- return min_angle;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLIK::Constraint class
- ///////////////////////////////////////////////////////////////////////////////
- Constraint::Constraint(ConstraintType type, const LLSD& parameters)
- : mType(type)
- {
- mForward = LLVector3(parameters["forward_axis"]);
- mForward.normalize();
- }
- //virtual
- LLSD Constraint::asLLSD() const
- {
- LLSD data = LLSD::emptyMap();
- data["forward_axis"] = mForward.getValue();
- data["type"] = typeToName();
- return data;
- }
- //virtual
- U64 Constraint::getHash() const
- {
- return HBXXH64::digest((const void*)this, sizeof(Constraint));
- }
- bool Constraint::enforce(Joint& joint) const
- {
- const LLQuaternion& local_rot = joint.getLocalRot();
- LLQuaternion adjusted_loc_rot = computeAdjustedLocalRot(local_rot);
- if (!LLQuaternion::almost_equal(adjusted_loc_rot, local_rot))
- {
- joint.setLocalRot(adjusted_loc_rot);
- return true;
- }
- return false;
- }
- LLQuaternion Constraint::minimizeTwist(const LLQuaternion& j_loc_rot) const
- {
- // Default behavior of minimizeTwist() is to compute the shortest rotation
- // that produces the same swing.
- LLVector3 joint_forward = mForward * j_loc_rot;
- LLVector3 swing_axis = mForward % joint_forward;
- LLQuaternion new_local_rot = LLQuaternion::DEFAULT;
- constexpr F32 MIN_AXIS_LENGTH = 1.0e-5f;
- if (swing_axis.length() > MIN_AXIS_LENGTH)
- {
- F32 swing_angle = acosf(mForward * joint_forward);
- new_local_rot.setAngleAxis(swing_angle, swing_axis);
- }
- return new_local_rot;
- }
- const std::string& Constraint::typeToName() const
- {
- switch (mType)
- {
- case Constraint::NULL_CONSTRAINT:
- return NULL_CONSTRAINT_NAME;
- case Constraint::SIMPLE_CONE_CONSTRAINT:
- return SIMPLE_CONE_NAME;
- case Constraint::TWIST_LIMITED_CONE_CONSTRAINT:
- return TWIST_LIMITED_CONE_NAME;
- case Constraint::ELBOW_CONSTRAINT:
- return ELBOW_NAME;
- case Constraint::KNEE_CONSTRAINT:
- return KNEE_NAME;
-
- case Constraint::ACUTE_ELLIPSOIDAL_CONE_CONSTRAINT:
- return ACUTE_ELLIPSOIDAL_NAME;
-
- case Constraint::DOUBLE_LIMITED_HINGE_CONSTRAINT:
- return DOUBLE_LIMITED_HINGE_NAME;
- default:
- return UNKNOWN_CONSTRAINT_NAME;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLIK::SimpleCone class
- ///////////////////////////////////////////////////////////////////////////////
- SimpleCone::SimpleCone(const LLVector3& forward, F32 max_angle)
- {
- mType = SIMPLE_CONE_CONSTRAINT;
- mForward = forward;
- mForward.normalize();
- mMaxAngle = fabsf(max_angle);
- mCosConeAngle = cosf(mMaxAngle);
- mSinConeAngle = sinf(mMaxAngle);
- }
- SimpleCone::SimpleCone(const LLSD& parameters)
- : Constraint(Constraint::SIMPLE_CONE_CONSTRAINT, parameters)
- {
- mMaxAngle = fabsf(F32(parameters["max_angle"].asReal()) * DEG_TO_RAD);
- mCosConeAngle = cosf(mMaxAngle);
- mSinConeAngle = sinf(mMaxAngle);
- }
- //virtual
- LLSD SimpleCone::asLLSD() const
- {
- LLSD data = Constraint::asLLSD();
- data["max_angle"] = mMaxAngle * RAD_TO_DEG;
- return data;
- }
- //virtual
- U64 SimpleCone::getHash() const
- {
- return HBXXH64::digest((const void*)this, sizeof(SimpleCone));
- }
- LLQuaternion SimpleCone::computeAdjustedLocalRot(const LLQuaternion& j_loc_rot) const
- {
- LLVector3 joint_forward = mForward * j_loc_rot;
- F32 forward_component = joint_forward * mForward;
- if (forward_component < mCosConeAngle)
- {
- // The joint's version of mForward lies outside the cone, so we project
- // it onto the surface of the cone...
- // projection = (forward_part) + (orthogonal_part)
- LLVector3 perp = joint_forward - forward_component * mForward;
- perp.normalize();
- LLVector3 new_j_forw = mCosConeAngle * mForward + mSinConeAngle * perp;
- // ... then compute the adjusted rotation
- LLQuaternion adjustment;
- adjustment.shortestArc(joint_forward, new_j_forw);
- LLQuaternion adjusted_loc_rot = j_loc_rot * adjustment;
- adjusted_loc_rot.normalize();
- return adjusted_loc_rot;
- }
- return j_loc_rot;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLIK::TwistLimitedCone class
- ///////////////////////////////////////////////////////////////////////////////
- TwistLimitedCone::TwistLimitedCone(const LLVector3& forward,
- F32 cone_angle, F32 min_twist,
- F32 max_twist)
- : mConeAngle(cone_angle)
- {
- mType = TWIST_LIMITED_CONE_CONSTRAINT;
- mForward = forward;
- mForward.normalize();
- mCosConeAngle = cosf(cone_angle);
- mSinConeAngle = sinf(cone_angle);
- mMinTwist = min_twist;
- mMaxTwist = max_twist;
- compute_angle_limits(mMinTwist, mMaxTwist);
- }
- TwistLimitedCone::TwistLimitedCone(const LLSD& parameters)
- : Constraint(Constraint::TWIST_LIMITED_CONE_CONSTRAINT, parameters)
- {
- mConeAngle = parameters["cone_angle"].asReal() * DEG_TO_RAD;
- mCosConeAngle = cosf(mConeAngle);
- mSinConeAngle = sinf(mConeAngle);
- mMinTwist = parameters["min_twist"].asReal() * DEG_TO_RAD;
- mMaxTwist = parameters["max_twist"].asReal() * DEG_TO_RAD;
- compute_angle_limits(mMinTwist, mMaxTwist);
- }
- //virtual
- LLSD TwistLimitedCone::asLLSD() const
- {
- LLSD data = Constraint::asLLSD();
- data["cone_angle"] = mConeAngle * RAD_TO_DEG;
- data["min_twist"] = mMinTwist * RAD_TO_DEG;
- data["max_twist"] = mMaxTwist * RAD_TO_DEG;
- return data;
- }
- //virtual
- U64 TwistLimitedCone::getHash() const
- {
- return HBXXH64::digest((const void*)this, sizeof(TwistLimitedCone));
- }
- LLQuaternion TwistLimitedCone::computeAdjustedLocalRot(const LLQuaternion& j_loc_rot) const
- {
- LLVector3 joint_forward = mForward * j_loc_rot;
- LLQuaternion adjusted_loc_rot = j_loc_rot;
- F32 forward_component = joint_forward * mForward;
- if (forward_component < mCosConeAngle)
- {
- // The joint's version of mForward lies outside the cone, so we project
- // it onto the surface of the cone...
- // projection = forward_part + orthogonal_part
- LLVector3 perp = joint_forward - forward_component * mForward;
- perp.normalize();
- LLVector3 new_j_forw = mCosConeAngle * mForward + mSinConeAngle * perp;
- // ... then compute the adjusted rotation
- LLQuaternion adjustment;
- adjustment.shortestArc(joint_forward, new_j_forw);
- adjusted_loc_rot = j_loc_rot * adjustment;
- }
- // Rotate mForward by adjusted_loc_rot
- joint_forward = mForward * adjusted_loc_rot;
- forward_component = joint_forward * mForward;
- // Compute two axes perpendicular to joint_forward: perp_x and perp_y
- LLVector3 perp_x = mForward % joint_forward;
- F32 perp_length = perp_x.length();
- constexpr F32 MIN_PERP_LENGTH = 1.0e-3f;
- if (perp_length < MIN_PERP_LENGTH)
- {
- perp_x = LLVector3::y_axis % mForward;
- perp_length = perp_x.length();
- if (perp_length < MIN_PERP_LENGTH)
- {
- perp_x = mForward % LLVector3::x_axis;
- }
- }
- perp_x.normalize();
- LLVector3 perp_y = joint_forward % perp_x;
- // The components of joint_perp on each direction allow us to compute twist
- // angle
- LLVector3 joint_perp = perp_x * adjusted_loc_rot;
- F32 twist = atan2f(joint_perp * perp_y, joint_perp * perp_x);
- // Clamp twist within bounds
- if (twist > mMaxTwist || twist < mMinTwist)
- {
- twist = compute_clamped_angle(twist, mMinTwist, mMaxTwist);
- joint_perp -= (joint_perp * joint_forward) * joint_forward;
- LLVector3 new_joint_perp = cosf(twist) * perp_x + sinf(twist) * perp_y;
- LLQuaternion adjustment;
- adjustment.shortestArc(joint_perp, new_joint_perp);
- adjusted_loc_rot = adjusted_loc_rot * adjustment;
- }
- adjusted_loc_rot.normalize();
- return adjusted_loc_rot;
- }
- LLQuaternion TwistLimitedCone::minimizeTwist(const LLQuaternion& j_loc_rot) const
- {
- // Compute the swing and combine with default twist
- // which is the midpoint of the twist range.
- LLQuaternion mid_twist;
- mid_twist.setAngleAxis(0.5f * (mMinTwist + mMaxTwist), mForward);
- // j_loc_rot = mid_twist * swing
- LLQuaternion new_local_rot = mid_twist;
- LLVector3 joint_forward = mForward * j_loc_rot;
- LLVector3 swing_axis = mForward % joint_forward;
- constexpr F32 MIN_SWING_AXIS_LENGTH = 1.0e-3f;
- if (swing_axis.length() > MIN_SWING_AXIS_LENGTH)
- {
- LLQuaternion swing;
- F32 swing_angle = acosf(mForward * joint_forward);
- swing.setAngleAxis(swing_angle, swing_axis);
- new_local_rot = mid_twist * swing;
- }
- return new_local_rot;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLIK::ElbowConstraint class
- ///////////////////////////////////////////////////////////////////////////////
- ElbowConstraint::ElbowConstraint(const LLVector3& forward_axis,
- const LLVector3& pivot_axis,
- F32 min_bend, F32 max_bend,
- F32 min_twist, F32 max_twist)
- {
- mType = ELBOW_CONSTRAINT;
- mForward = forward_axis;
- mForward.normalize();
- mPivotAxis = mForward % (pivot_axis % mForward);
- mPivotAxis.normalize();
- mLeft = mPivotAxis % mForward;
- mMinBend = min_bend;
- mMaxBend = max_bend;
- compute_angle_limits(mMinBend, mMaxBend);
- mMinTwist = min_twist;
- mMaxTwist = max_twist;
- compute_angle_limits(mMinTwist, mMaxTwist);
- }
- ElbowConstraint::ElbowConstraint(const LLSD& parameters)
- : Constraint(Constraint::ELBOW_CONSTRAINT, parameters)
- {
- mPivotAxis = mForward % (LLVector3(parameters["pivot_axis"]) % mForward);
- mPivotAxis.normalize();
- mLeft = mPivotAxis % mForward;
- mMinBend = parameters["min_bend"].asReal() * DEG_TO_RAD;
- mMaxBend = parameters["max_bend"].asReal() * DEG_TO_RAD;
- compute_angle_limits(mMinBend, mMaxBend);
- mMinTwist = parameters["min_twist"].asReal() * DEG_TO_RAD;
- mMaxTwist = parameters["max_twist"].asReal() * DEG_TO_RAD;
- compute_angle_limits(mMinTwist, mMaxTwist);
- }
- //virtual
- LLSD ElbowConstraint::asLLSD() const
- {
- LLSD data = Constraint::asLLSD();
- data["pivot_axis"] = mPivotAxis.getValue();
- data["min_bend"] = mMinBend * RAD_TO_DEG;
- data["max_bend"] = mMaxBend * RAD_TO_DEG;
- data["min_twist"] = mMinTwist * RAD_TO_DEG;
- data["max_twist"] = mMaxTwist * RAD_TO_DEG;
- return data;
- }
- //virtual
- U64 ElbowConstraint::getHash() const
- {
- return HBXXH64::digest((const void*)this, sizeof(ElbowConstraint));
- }
- LLQuaternion ElbowConstraint::computeAdjustedLocalRot(const LLQuaternion& j_loc_rot) const
- {
- // Rotate mForward into joint-frame
- LLVector3 joint_forward = mForward * j_loc_rot;
- // Compute adjustment required to move joint_forward back into hinge plane
- LLVector3 proj_j_forw = joint_forward -
- (joint_forward * mPivotAxis) * mPivotAxis;
- LLQuaternion adjustment;
- adjustment.shortestArc(joint_forward, proj_j_forw);
- LLQuaternion adjusted_loc_rot = j_loc_rot * adjustment;
- // Measure twist
- LLVector3 twisted_pivot = mPivotAxis * adjusted_loc_rot;
- F32 cos_part = twisted_pivot * mPivotAxis;
- F32 sin_part = (mLeft * adjusted_loc_rot) * mPivotAxis;
- F32 twist = atan2f(sin_part, cos_part);
- LLVector3 new_j_forw = mForward * adjusted_loc_rot;
- if (twist < mMinTwist || twist > mMaxTwist)
- {
- // adjust twist
- twist = compute_clamped_angle(twist, mMinTwist, mMaxTwist);
- LLVector3 swung_left_axis = mPivotAxis % new_j_forw;
- LLVector3 new_twisted_pivot = cosf(twist) * mPivotAxis -
- sinf(twist) * swung_left_axis;
- adjustment.shortestArc(twisted_pivot, new_twisted_pivot);
- adjusted_loc_rot = adjusted_loc_rot * adjustment;
- new_j_forw = mForward * adjusted_loc_rot;
- }
- // Measure bend
- F32 bend = atan2f(new_j_forw * mLeft, new_j_forw * mForward);
- if (bend > mMaxBend || bend < mMinBend)
- {
- // Adjust bend
- bend = compute_clamped_angle(bend, mMinBend, mMaxBend);
- new_j_forw = cosf(bend) * mForward + sinf(bend) * mLeft;
- adjustment.shortestArc(joint_forward, new_j_forw);
- adjusted_loc_rot = adjusted_loc_rot * adjustment;
- }
- adjusted_loc_rot.normalize();
- return adjusted_loc_rot;
- }
- LLQuaternion ElbowConstraint::minimizeTwist(const LLQuaternion& j_loc_rot) const
- {
- // Assume all swing is really just bend about mPivotAxis and twist is
- // centered in the valid twist range. If bend_angle is outside the limits
- // then we check both +/- bend_angle and pick the one closest to the
- // allowed range. This comes down to a simple question: which is closer to
- // the midpoint of the bend range ?
- LLVector3 joint_forward = mForward * j_loc_rot;
- F32 fdot = joint_forward * mForward;
- LLVector3 perp_part = joint_forward - fdot * mForward;
- F32 bend_angle = atan2f(perp_part.length(), fdot);
- if (bend_angle < mMinBend || bend_angle > mMaxBend)
- {
- F32 alt_bend_angle = - bend_angle;
- F32 mid_bend = 0.5f * (mMinBend + mMaxBend);
- if (fabsf(alt_bend_angle - mid_bend) < fabsf(bend_angle - mid_bend))
- {
- bend_angle = alt_bend_angle;
- }
- }
- LLQuaternion bend;
- bend.setAngleAxis(bend_angle, mPivotAxis);
- LLQuaternion mid_twist;
- mid_twist.setAngleAxis(0.5f * (mMinTwist + mMaxTwist), mForward);
- return mid_twist * bend;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLIK::KneeConstraint class
- ///////////////////////////////////////////////////////////////////////////////
- KneeConstraint::KneeConstraint(const LLVector3& forward_axis,
- const LLVector3& pivot_axis,
- F32 min_bend, F32 max_bend)
- {
- mType = KNEE_CONSTRAINT;
- mForward = forward_axis;
- mForward.normalize();
- mPivotAxis = mForward % (pivot_axis % mForward);
- mPivotAxis.normalize();
- mLeft = mPivotAxis % mForward;
- mMinBend = min_bend;
- mMaxBend = max_bend;
- compute_angle_limits(mMinBend, mMaxBend);
- }
- KneeConstraint::KneeConstraint(const LLSD& parameters)
- : Constraint(Constraint::KNEE_CONSTRAINT, parameters)
- {
- mPivotAxis = mForward % (LLVector3(parameters["pivot_axis"]) % mForward);
- mPivotAxis.normalize();
- mLeft = mPivotAxis % mForward;
- mMinBend = parameters["min_bend"].asReal() * DEG_TO_RAD;
- mMaxBend = parameters["max_bend"].asReal() * DEG_TO_RAD;
- compute_angle_limits(mMinBend, mMaxBend);
- }
- //virtual
- LLSD KneeConstraint::asLLSD() const
- {
- LLSD data = Constraint::asLLSD();
- data["pivot_axis"] = mPivotAxis.getValue();
- data["min_bend"] = mMinBend * RAD_TO_DEG;
- data["max_bend"] = mMaxBend * RAD_TO_DEG;
- return data;
- }
- //virtual
- U64 KneeConstraint::getHash() const
- {
- return HBXXH64::digest((const void*)this, sizeof(KneeConstraint));
- }
- LLQuaternion KneeConstraint::computeAdjustedLocalRot(const LLQuaternion& j_loc_rot) const
- {
- // Rotate mPivotAxis into joint-frame
- LLVector3 joint_axis = mPivotAxis * j_loc_rot;
- LLQuaternion adjustment;
- adjustment.shortestArc(joint_axis, mPivotAxis);
- LLQuaternion adjusted_loc_rot = j_loc_rot * adjustment;
- // rotate mForward into joint-frame
- LLVector3 joint_forward = mForward * adjusted_loc_rot;
- LLVector3 new_j_forw = joint_forward;
- // compute angle between mForward and new_j_forw
- F32 bend = atan2f(new_j_forw * mLeft, new_j_forw * mForward);
- if (bend > mMaxBend || bend < mMinBend)
- {
- bend = compute_clamped_angle(bend, mMinBend, mMaxBend);
- new_j_forw = cosf(bend) * mForward + sinf(bend) * mLeft;
- adjustment.shortestArc(joint_forward, new_j_forw);
- adjusted_loc_rot = adjusted_loc_rot * adjustment;
- }
- adjusted_loc_rot.normalize();
- return adjusted_loc_rot;
- }
- LLQuaternion KneeConstraint::minimizeTwist(const LLQuaternion& j_loc_rot) const
- {
- // Assume all swing is really just bend about mPivotAxis.
- // If bend_angle is outside the limits then we check both +/- bend_angle and pick
- // the one closest to the allowed range. This comes down to a simple question:
- // which is closer to the midpoint of the bend range?
- LLVector3 joint_forward = mForward * j_loc_rot;
- F32 fdot = joint_forward * mForward;
- LLVector3 perp_part = joint_forward - fdot * mForward;
- F32 bend_angle = atan2f(perp_part.length(), fdot);
- if (bend_angle < mMinBend || bend_angle > mMaxBend)
- {
- F32 alt_bend_angle = - bend_angle;
- F32 mid_bend = 0.5f * (mMinBend + mMaxBend);
- if (fabsf(alt_bend_angle - mid_bend) < fabsf(bend_angle - mid_bend))
- {
- bend_angle = alt_bend_angle;
- }
- }
- LLQuaternion bend;
- bend.setAngleAxis(bend_angle, mPivotAxis);
- return bend;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // AcuteEllipsoidalCone class
- ///////////////////////////////////////////////////////////////////////////////
- AcuteEllipsoidalCone::AcuteEllipsoidalCone(const LLVector3& forward_axis,
- const LLVector3& up_axis,
- F32 forward, F32 up, F32 left,
- F32 down, F32 right)
- : mXForward(forward),
- mXUp(up),
- mXDown(down),
- mXLeft(left),
- mXRight(right)
- {
- mType = ACUTE_ELLIPSOIDAL_CONE_CONSTRAINT;
- mUp = up_axis;
- mUp.normalize();
- mForward = (mUp % forward_axis) % mUp;
- mForward.normalize();
- mLeft = mUp % mForward; // already normalized
- // Divide everything by 'foward' and take make sure they are positive.
- // This normalizes the forward component (adjacent side) of all the
- // triangles to have length=1.0, which is important for our trigonometry
- // math later.
- //
- // up left |
- // | / | /
- // |/ |/
- // @------------------+
- // 1.0 /|
- // |
- up = fabsf(up / forward);
- left = fabsf(left / forward);
- down = fabsf(down / forward);
- right = fabsf(right / forward);
- // These are the indices of the directions and quadrants.
- // With 'forward' pointing into the page.
- // up
- // |
- // 1 | 0
- // |
- // left ------(x)------ right
- // |
- // 2 | 3
- // |
- // down
- //
- // When projecting vectors onto the ellipsoidal surface we will always
- // scale the left-axis into the frame in which the ellipsoid is circular.
- // We cache the necessary scale coefficients now:
- //
- mQuadrantScales[0] = up / right;
- mQuadrantScales[1] = up / left;
- mQuadrantScales[2] = down / left;
- mQuadrantScales[3] = down / right;
- // When determining whether a direction is inside or outside the
- // ellipsoid we will need the cosine and cotangent of the cone angles in
- // the scaled frames. We cache them now:
- // cosine = adjacent / hypotenuse
- // cotangent = adjacent / opposite
- mQuadrantCosAngles[0] = 1.f / sqrtf(up * up + 1.f);
- mQuadrantCotAngles[0] = 1.f / up;
- mQuadrantCosAngles[1] = mQuadrantCosAngles[0];
- mQuadrantCotAngles[1] = mQuadrantCotAngles[0];
- mQuadrantCosAngles[2] = 1.f / sqrtf(down * down + 1.f);
- mQuadrantCotAngles[2] = 1.f / down;
- mQuadrantCosAngles[3] = mQuadrantCosAngles[2];
- mQuadrantCotAngles[3] = mQuadrantCotAngles[2];
- }
- AcuteEllipsoidalCone::AcuteEllipsoidalCone(const LLSD& parameters)
- : Constraint(Constraint::ACUTE_ELLIPSOIDAL_CONE_CONSTRAINT, parameters)
- {
- mUp = LLVector3(parameters["up_axis"]);
- mUp.normalize();
- mForward = (mUp % mForward) % mUp;
- mForward.normalize();
- mLeft = mUp % mForward; // already normalized
- mXForward = parameters["forward"].asReal();
- mXUp = parameters["up"].asReal();
- mXDown = parameters["down"].asReal();
- mXLeft = parameters["left"].asReal();
- mXRight = parameters["right"].asReal();
- F32 up = fabsf(mXUp / mXForward);
- F32 left = fabsf(mXLeft / mXForward);
- F32 down = fabsf(mXDown / mXForward);
- F32 right = fabsf(mXRight / mXForward);
- mQuadrantScales[0] = up / right;
- mQuadrantScales[1] = up / left;
- mQuadrantScales[2] = down / left;
- mQuadrantScales[3] = down / right;
- mQuadrantCosAngles[0] = 1.f / sqrtf(up * up + 1.f);
- mQuadrantCotAngles[0] = 1.f / up;
- mQuadrantCosAngles[1] = mQuadrantCosAngles[0];
- mQuadrantCotAngles[1] = mQuadrantCotAngles[0];
- mQuadrantCosAngles[2] = 1.f / sqrtf(down * down + 1.f);
- mQuadrantCotAngles[2] = 1.f / down;
- mQuadrantCosAngles[3] = mQuadrantCosAngles[2];
- mQuadrantCotAngles[3] = mQuadrantCotAngles[2];
- }
- //virtual
- LLSD AcuteEllipsoidalCone::asLLSD() const
- {
- LLSD data = Constraint::asLLSD();
- data["up_axis"] = mUp.getValue();
- data["forward"] = mXForward;
- data["down"] = mXDown;
- data["left"] = mXLeft;
- data["right"] = mXRight;
- return data;
- }
- //virtual
- U64 AcuteEllipsoidalCone::getHash() const
- {
- return HBXXH64::digest((const void*)this, sizeof(AcuteEllipsoidalCone));
- }
- LLQuaternion AcuteEllipsoidalCone::computeAdjustedLocalRot(const LLQuaternion& j_loc_rot) const
- {
- // Rotate mForward into joint-frame
- LLVector3 joint_forward = mForward * j_loc_rot;
- // joint_forward is normalized
- // Determine its quadrant
- F32 up_component = joint_forward * mUp;
- F32 left_component = joint_forward * mLeft;
- U32 q = 0;
- if (up_component < 0.f)
- {
- if (left_component < 0.f)
- {
- q = 2;
- }
- else
- {
- q = 3;
- }
- }
- else if (left_component < 0.f)
- {
- q = 1;
- }
- // Scale left axis to frame in which ellipse is a circle
- F32 scaled_left_comp = left_component * mQuadrantScales[q];
- // Reassemble in scaled frame
- F32 forward_component = joint_forward * mForward;
- LLVector3 new_j_forw = forward_component * mForward + up_component * mUp +
- scaled_left_comp * mLeft;
- // new_j_forw is not normalized
- // which means we must adjust its the forward_component when
- // checking for violation in scaled frame
- if (forward_component / new_j_forw.length() < mQuadrantCosAngles[q])
- {
- // joint violates constraint --> project onto cone
- //
- // violates projected
- // + +
- // . /|
- // . / |
- // . // |
- // .// |
- // @---+----
- // `
- // `
- //
- // Orthogonal components remain unchanged but we need to compute
- // a corrected forward_component (adjacent leg of the right triangle)
- // in the scaled frame. We can use the formula:
- // adjacent = opposite * cos(angle) / sin(angle)
- // adjacent = opposite * cot(angle)
- //
- F32 orthogonal_component = sqrtf(scaled_left_comp * scaled_left_comp +
- up_component * up_component);
- forward_component = orthogonal_component * mQuadrantCotAngles[q];
- // Re-assemble the projected direction in the non-scaled frame:
- new_j_forw = forward_component * mForward + up_component * mUp +
- left_component * mLeft;
- // new_j_forw is not normalized, but it doesn't matter
- // Compute adjusted_loc_rot
- LLQuaternion adjustment;
- adjustment.shortestArc(joint_forward, new_j_forw);
- LLQuaternion adjusted_loc_rot = j_loc_rot * adjustment;
- adjusted_loc_rot.normalize();
- return adjusted_loc_rot;
- }
- return j_loc_rot;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLIK::DoubleLimitedHinge class
- ///////////////////////////////////////////////////////////////////////////////
- DoubleLimitedHinge::DoubleLimitedHinge(const LLVector3& forward_axis,
- const LLVector3& up_axis,
- F32 min_yaw, F32 max_yaw,
- F32 min_pitch, F32 max_pitch)
- {
- mType = DOUBLE_LIMITED_HINGE_CONSTRAINT;
- mForward = forward_axis;
- mForward.normalize();
- mUp = mForward % (up_axis % mForward);
- mUp.normalize();
- mLeft = mUp % mForward;
- mMinYaw = min_yaw;
- mMaxYaw = max_yaw;
- compute_angle_limits(mMinYaw, mMaxYaw);
- // Keep pitch in range [-PI/2, PI/2]
- F32 HALF_PI = 0.5f * F_PI;
- mMinPitch = remove_multiples_of_two_pi(min_pitch);
- if (mMinPitch > HALF_PI)
- {
- mMinPitch = HALF_PI;
- }
- else if (mMinPitch < -HALF_PI)
- {
- mMinPitch = -HALF_PI;
- }
- mMaxPitch = remove_multiples_of_two_pi(max_pitch);
- if (mMaxPitch > HALF_PI)
- {
- mMaxPitch = HALF_PI;
- }
- else if (mMaxPitch < -HALF_PI)
- {
- mMaxPitch = -HALF_PI;
- }
- if (mMinPitch > mMaxPitch)
- {
- F32 temp = mMinPitch;
- mMinPitch = mMaxPitch;
- mMaxPitch = temp;
- }
- }
- DoubleLimitedHinge::DoubleLimitedHinge(const LLSD& parameters)
- : Constraint(Constraint::DOUBLE_LIMITED_HINGE_CONSTRAINT, parameters)
- {
- mUp = mForward % (LLVector3(parameters["up_axis"]) % mForward);
- mUp.normalize();
- mLeft = mUp % mForward;
- mMinYaw = parameters["min_yaw"].asReal() * DEG_TO_RAD;
- mMaxYaw = parameters["max_yaw"].asReal() * DEG_TO_RAD;
- compute_angle_limits(mMinYaw, mMaxYaw);
- // Keep pitch in range [-PI/2, PI/2]
- F32 HALF_PI = 0.5f * F_PI;
- F32 min_pitch = parameters["min_pitch"].asReal() * DEG_TO_RAD;
- mMinPitch = remove_multiples_of_two_pi(min_pitch);
- if (mMinPitch > HALF_PI)
- {
- mMinPitch = HALF_PI;
- }
- else if (mMinPitch < -HALF_PI)
- {
- mMinPitch = -HALF_PI;
- }
- F32 max_pitch = parameters["max_pitch"].asReal() * DEG_TO_RAD;
- mMaxPitch = remove_multiples_of_two_pi(max_pitch);
- if (mMaxPitch > HALF_PI)
- {
- mMaxPitch = HALF_PI;
- }
- else if (mMaxPitch < -HALF_PI)
- {
- mMaxPitch = -HALF_PI;
- }
- if (mMinPitch > mMaxPitch)
- {
- F32 temp = mMinPitch;
- mMinPitch = mMaxPitch;
- mMaxPitch = temp;
- }
- }
- //virtual
- LLSD DoubleLimitedHinge::asLLSD() const
- {
- LLSD data = Constraint::asLLSD();
- data["up_axis"] = mUp.getValue();
- data["min_yaw"] = mMinYaw * RAD_TO_DEG;
- data["max_yaw"] = mMaxYaw * RAD_TO_DEG;
- data["min_pitch"] = mMinPitch * RAD_TO_DEG;
- data["max_pitch"] = mMaxPitch * RAD_TO_DEG;
- return data;
- }
- //virtual
- U64 DoubleLimitedHinge::getHash() const
- {
- return HBXXH64::digest((const void*)this, sizeof(DoubleLimitedHinge));
- }
- LLQuaternion DoubleLimitedHinge::computeAdjustedLocalRot(const LLQuaternion& j_loc_rot) const
- {
- // Twist: eliminate twist by adjusting the rotated mLeft axis to remain in
- // horizontal plane
- LLVector3 joint_left = mLeft * j_loc_rot;
- LLQuaternion adjustment;
- adjustment.shortestArc(joint_left, joint_left - (joint_left * mUp) * mUp);
- LLQuaternion adjusted_loc_rot = j_loc_rot * adjustment;
- LLVector3 joint_forward = mForward * adjusted_loc_rot;
- // Yaw
- F32 up_component = joint_forward * mUp;
- LLVector3 horizontal_axis = joint_forward - up_component * mUp;
- F32 yaw = atan2f(horizontal_axis * mLeft, horizontal_axis * mForward);
- if (yaw > mMaxYaw || yaw < mMinYaw)
- {
- yaw = compute_clamped_angle(yaw, mMinYaw, mMaxYaw);
- horizontal_axis = cosf(yaw) * mForward + sinf(yaw) * mLeft;
- }
- else
- {
- horizontal_axis.normalize();
- }
- // Pitch. Note: the minus-sign in the "opposite" (sin) term here is because
- // our pitch-axis is mLeft and according to the right-hand-rule positive
- // pitch drops the forward axis down.
- F32 horiz_comp = sqrtf(llmax(1.f - up_component * up_component, 0.f));
- F32 pitch = atan2f(-up_component, horiz_comp);
- if (pitch > mMaxPitch || pitch < mMinPitch)
- {
- pitch = compute_clamped_angle(pitch, mMinPitch, mMaxPitch);
- up_component = -sinf(pitch);
- horiz_comp = sqrtf(llmax(1.f - up_component * up_component, 0.f));
- }
- LLVector3 new_j_forw = horiz_comp * horizontal_axis + up_component * mUp;
- new_j_forw.normalize();
- if (dist_vec(joint_forward, new_j_forw) > 1.0e-3f)
- {
- // Compute adjusted_loc_rot
- adjustment.shortestArc(joint_forward, new_j_forw);
- adjusted_loc_rot = adjusted_loc_rot * adjustment;
- }
- adjusted_loc_rot.normalize();
- return adjusted_loc_rot;
- }
- // Eliminates twist by adjusting the rotated mLeft axis to remain in horizontal
- // plane
- LLQuaternion DoubleLimitedHinge::minimizeTwist(const LLQuaternion& j_loc_rot) const
- {
- LLVector3 joint_left = mLeft * j_loc_rot;
- LLQuaternion adjustment;
- adjustment.shortestArc(joint_left, joint_left - (joint_left * mUp) * mUp);
- LLQuaternion adjusted_loc_rot = j_loc_rot * adjustment;
- adjusted_loc_rot.normalize();
- return adjusted_loc_rot;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLIK::Joint::Config sub-class
- ///////////////////////////////////////////////////////////////////////////////
- void Joint::Config::updateFrom(const Config& other_config)
- {
- if (mFlags == other_config.mFlags)
- {
- *this = other_config; // other_config updates everything
- }
- else // Find and apply all parameters in other_config
- {
- if (other_config.hasLocalPos())
- {
- setLocalPos(other_config.mLocalPos);
- }
- if (other_config.hasLocalRot())
- {
- setLocalRot(other_config.mLocalRot);
- }
- if (other_config.hasTargetPos())
- {
- setTargetPos(other_config.mTargetPos);
- }
- if (other_config.hasTargetRot())
- {
- setTargetRot(other_config.mTargetRot);
- }
- if (other_config.hasLocalScale())
- {
- setLocalScale(other_config.mLocalScale);
- }
- if (other_config.constraintIsDisabled())
- {
- disableConstraint();
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLIK::Joint class
- ///////////////////////////////////////////////////////////////////////////////
- Joint::Joint(LLJoint* info_ptr)
- : mInfoPtr(info_ptr),
- mID(info_ptr->getJointNum()),
- mConfig(NULL),
- mConfigFlags(0),
- mIkFlags(0)
- {
- resetFromInfo();
- }
- void Joint::resetFromInfo()
- {
- const LLVector3& scale = mInfoPtr->getScale();
- mLocalPos = mInfoPtr->getPosition().scaledVec(scale);
- mBone = mInfoPtr->getEnd().scaledVec(scale);
- mLocalPosLength = mLocalPos.length();
- // This is correct: we do NOT store info scale in mLocalScale which
- // represents Puppetry's tweak on top of whatever is set in the info.
- mLocalScale.set(1.f, 1.f, 1.f);
- }
- void Joint::addChild(const ptr_t& child)
- {
- if (child)
- {
- mChildren.push_back(child);
- }
- }
- void Joint::setTargetPos(const LLVector3& pos)
- {
- if (hasPosTarget())
- {
- // *HACK: cast mConfig to non-const pointer so we can modify it
- Config* config = const_cast<Config*>(mConfig);
- config->setTargetPos(pos);
- }
- }
- void Joint::setParent(const ptr_t& parent)
- {
- mParent = parent;
- if (!mParent)
- {
- // The root's local orientation is never updated by the IK algorithm.
- // Whatever orientation it has at the start of IK will be its final,
- // which is why we flag it as "locked". This also simplifies logic
- // elsewhere: in a few places we assume any non-locked Joint has a parent.
- mIkFlags = IK_FLAG_LOCAL_ROT_LOCKED;
- }
- reset();
- }
- void Joint::reset()
- {
- resetFromInfo();
- // Note: we do not bother to enforce localRotLocked() here because any call
- // to reset() is expected to be outside the Solver IK iterations.
- mLocalRot = LLQuaternion::DEFAULT;
- if (mParent)
- {
- mPos = mParent->mPos + mLocalPos * mParent->mRot;
- mRot = mParent->mRot;
- }
- else
- {
- mPos = mLocalPos;
- mRot = mLocalRot;
- }
- }
- void Joint::relaxRot(F32 blend_factor)
- {
- if (!localRotLocked())
- {
- mLocalRot = lerp(blend_factor, mLocalRot, LLQuaternion::DEFAULT);
- }
- if (mParent)
- {
- // We always re-compute world-frame transform because parent may have
- // relaxed.
- mRot = mLocalRot * mParent->mRot;
- mRot.normalize();
- mPos = mParent->mPos + mLocalPos * mParent->mRot;
- }
- else
- {
- mRot = mLocalRot;
- mPos = mLocalPos;
- }
- }
- void Joint::resetRecursively()
- {
- reset();
- for (auto& child : mChildren)
- {
- child->resetRecursively();
- }
- }
- void Joint::relaxRotationsRecursively(F32 blend_factor)
- {
- blend_factor = llclamp(blend_factor, 0.f, 1.f);
- relaxRot(blend_factor);
- for (auto& child : mChildren)
- {
- if (child->isActive())
- {
- child->relaxRotationsRecursively(blend_factor);
- }
- }
- }
- F32 Joint::recursiveComputeLongestChainLength(F32 length) const
- {
- length += mLocalPosLength;
- F32 longest_length = length;
- if (mChildren.empty())
- {
- longest_length += mBone.length();
- }
- else
- {
- for (const auto& child : mChildren)
- {
- F32 child_len = child->recursiveComputeLongestChainLength(length);
- if (child_len > longest_length)
- {
- longest_length = child_len;
- }
- }
- }
- return longest_length;
- }
- LLVector3 Joint::computeEndTargetPos() const
- {
- // Note: we expect this Joint has either: a target, or at least one
- // active child
- if (hasPosTarget())
- {
- return mConfig->getTargetPos();
- }
- LLVector3 target_pos;
- S32 num_active_children = 0;
- for (const auto& child : mChildren)
- {
- if (child->isActive())
- {
- target_pos += child->mPos;
- ++num_active_children;
- }
- }
- if (!num_active_children)
- {
- llwarns_sparse << "No active children !" << llendl;
- return target_pos;
- }
- return (1.f / F32(num_active_children)) * target_pos;
- }
- LLVector3 Joint::computeWorldTipOffset() const
- {
- LLVector3 offset = mPos;
- if (mParent)
- {
- offset -= mParent->mPos + mLocalPos * mParent->mRot;
- }
- return offset;
- }
- void Joint::updateEndInward()
- {
- // Note: during FABRIK we DO NOT enforce constraints.
- if (hasRotTarget())
- {
- mRot = mConfig->getTargetRot();
- if (hasPosTarget())
- {
- mPos = mConfig->getTargetPos() - mBone * mRot;
- }
- }
- else
- {
- std::vector<LLVector3> local_targets, world_targets;
- collectTargetPositions(local_targets, world_targets);
- size_t num_targets = local_targets.size();
- if (num_targets == 1)
- {
- // Special handling for the most common num_targets == 1 case
- // compute mPos
- LLVector3 bone_dir = world_targets[0] - mPos;
- bone_dir.normalize();
- mPos = world_targets[0] - (local_targets[0].length() * bone_dir);
- // Compute new mRot
- LLVector3 old_bone = local_targets[0] * mRot;
- LLQuaternion adjustment;
- adjustment.shortestArc(old_bone, bone_dir);
- mRot = mRot * adjustment;
- mRot.normalize();
- }
- else
- {
- LLVector3 new_pos;
- // Origin in quaternion space
- LLQuaternion avg_adjustment(0.f, 0.f, 0.f, 0.f);
- for (size_t i = 0; i < num_targets; ++i)
- {
- // mPos
- LLVector3 new_bone = world_targets[i] - mPos;
- new_bone.normalize();
- new_bone *= local_targets[i].length();
- new_pos += world_targets[i] - new_bone;
- // mRot
- LLVector3 old_bone = local_targets[i] * mRot;
- LLQuaternion adjustment;
- adjustment.shortestArc(old_bone, new_bone);
- if (adjustment.mQ[VW] < 0.f)
- {
- // Negate to keep all arithmetic on the same hypersphere
- avg_adjustment = avg_adjustment - adjustment;
- }
- else
- {
- avg_adjustment = avg_adjustment + adjustment;
- }
- }
- if (mParent && mParent->isActive())
- {
- // Compute mPos
- mPos = new_pos / (F32)(num_targets);
- }
- // Compute mRot
- avg_adjustment.normalize();
- mRot = mRot * avg_adjustment;
- mRot.normalize();
- }
- }
- // Note: mLocalRot will be updated later when we know mParent's location
- // Now that we know mRot --> update children mLocalRot
- for (auto& child : mChildren)
- {
- if (child->isActive())
- {
- child->updateLocalRot();
- }
- }
- }
- void Joint::updateEndOutward()
- {
- // Note: during FABRIK we DO NOT enforce constraints.
- // mParent is expected to be non-null.
- mPos = mParent->mPos + mLocalPos * mParent->mRot;
- // mRot
- if (localRotLocked())
- {
- mRot = mLocalRot * mParent->mRot;
- return;
- }
- if (hasRotTarget())
- {
- mRot = mConfig->getTargetRot();
- if (hasPosTarget())
- {
- mPos = mConfig->getTargetPos() - mBone * mRot;
- }
- }
- else
- {
- std::vector<LLVector3> local_targets, world_targets;
- collectTargetPositions(local_targets, world_targets);
- size_t num_targets = local_targets.size();
- if (num_targets == 1)
- {
- // Special handling for the most common num_targets == 1 case
- LLVector3 new_bone = world_targets[0] - mPos;
- LLVector3 old_bone = local_targets[0] * mRot;
- LLQuaternion adjustment;
- adjustment.shortestArc(old_bone, new_bone);
- mRot = mRot * adjustment;
- }
- else
- {
- // Origin in quaternion space
- LLQuaternion avg_adjustment(0.f, 0.f, 0.f, 0.f);
- LLQuaternion adjustment;
- for (size_t i = 0; i < num_targets; ++i)
- {
- LLVector3 new_bone = world_targets[i] - mPos;
- LLVector3 old_bone = local_targets[i] * mRot;
- adjustment.shortestArc(old_bone, new_bone);
- if (adjustment.mQ[VW] < 0.f)
- {
- // Negate to keep all Quaternion arithmetic on one
- // hypersphere
- avg_adjustment = avg_adjustment - adjustment;
- }
- else
- {
- avg_adjustment = avg_adjustment + adjustment;
- }
- }
- avg_adjustment.normalize();
- mRot = mRot * avg_adjustment;
- }
- mRot.normalize();
- }
- updateLocalRot();
- }
- // This Joint's child is specified in argument in case this Joint has multiple
- // children.
- void Joint::updateInward(const Joint::ptr_t& child)
- {
- // Note: during FABRIK we DO NOT enforce constraints. mParent is expected
- // to be non-null.
- // Compute mPos
- LLVector3 old_pos = mPos;
- LLVector3 bone_dir = child->mPos - old_pos;
- bone_dir.normalize();
- mPos = child->mPos - child->mLocalPosLength * bone_dir;
- // Compute mRot
- LLVector3 old_bone = child->mLocalPos * mRot;
- LLQuaternion adjustment;
- adjustment.shortestArc(old_bone, bone_dir);
- mRot = mRot * adjustment;
- mRot.normalize();
- // Compute child->mLocalRot
- child->updateLocalRot();
- // this->mLocalRot will be updated later
- }
- void Joint::updatePosAndRotFromParent()
- {
- if (mParent)
- {
- mPos = mParent->mPos + mLocalPos * mParent->mRot;
- mRot = mLocalRot * mParent->mRot;
- mRot.normalize();
- }
- }
- void Joint::updateOutward()
- {
- // Note: during FABRIK we DO NOT enforce constraints.
- // mParent is expected to be non-null.
- LLVector3 old_end_pos = mPos + mBone * mRot;
- // mPos
- mPos = mParent->mPos + mLocalPos * mParent->mRot;
- // mRot
- LLVector3 new_bone = old_end_pos - mPos;
- LLVector3 old_bone = mBone * mRot;
- LLQuaternion dQ;
- dQ.shortestArc(old_bone, new_bone);
- mRot = mRot * dQ;
- mRot.normalize();
- updateLocalRot();
- }
- void Joint::applyLocalRot()
- {
- if (!mParent)
- {
- return;
- }
- if (hasRotTarget())
- {
- // Apply backpressure by lerping toward new_rot
- LLQuaternion new_rot = mLocalRot * mParent->mRot;
- constexpr F32 WORLD_ROT_TARGET_BACKPRESSURE_COEF = 0.5f;
- mRot = lerp(WORLD_ROT_TARGET_BACKPRESSURE_COEF,
- mConfig->getTargetRot(), new_rot);
- // Recompute mLocalRot
- LLQuaternion inv_parent_rot = mParent->mRot;
- inv_parent_rot.transpose();
- mLocalRot = mRot * inv_parent_rot;
- mLocalRot.normalize();
- }
- else
- {
- mRot = mLocalRot * mParent->mRot;
- mRot.normalize();
- }
- }
- void Joint::updateLocalRot()
- {
- if (!localRotLocked())
- {
- // mPos and mRot are expected to be correct and mParent is expected to
- // be valid
- LLQuaternion inv_parent_rot = mParent->mRot;
- inv_parent_rot.transpose();
- mLocalRot = mRot * inv_parent_rot;
- mLocalRot.normalize();
- }
- }
- LLQuaternion Joint::computeParentRot() const
- {
- // Formula is:
- // mRot = mLocalRot * mParent->mRot
- // Solving for mParent->mRot gives:
- // mParent->mRot = mLocalRotInv * mRot
- LLQuaternion q = mLocalRot;
- q.transpose();
- q = q * mRot;
- q.normalize();
- return q;
- }
- void Joint::updateChildLocalRots() const
- {
- // Now that we know mRot we can update the childrens' mLocalRot
- for (const Joint::ptr_t& child : mChildren)
- {
- if (child->isActive())
- {
- child->updateLocalRot();
- }
- }
- }
- void Joint::lockLocalRot(const LLQuaternion& local_rot)
- {
- mLocalRot = local_rot;
- mIkFlags |= IK_FLAG_LOCAL_ROT_LOCKED;
- activate();
- if (!mParent)
- {
- mRot = local_rot;
- }
- }
- bool Joint::enforceConstraint()
- {
- if (localRotLocked())
- {
- // A fixed mLocalRot is effectively like a fixed Constraint so we
- // always return 'true' here: the Constraint is in effect and mRot may
- // have been optimistically modified but mLocalRot was not.
- return true;
- }
- if (mConstraint && !hasDisabledConstraint())
- {
- return mConstraint->enforce(*this);
- }
- return false;
- }
- void Joint::updateWorldTransformsRecursively()
- {
- updatePosAndRotFromParent();
- for (Joint::ptr_t& child : mChildren)
- {
- if (child->isActive())
- {
- child->updateWorldTransformsRecursively();
- }
- }
- }
- // Returns valid Joint::ptr_t to child iff only one child is active, else
- // returns null Joint::ptr_t
- Joint::ptr_t Joint::getSingleActiveChild()
- {
- Joint::ptr_t active_child;
- for (Joint::ptr_t& child : mChildren)
- {
- if (child->isActive())
- {
- if (active_child)
- {
- // Second child --> this Joint is not a "false" sub-base
- active_child.reset();
- break;
- }
- active_child = child;
- }
- }
- return active_child;
- }
- void Joint::setLocalRot(const LLQuaternion& new_local_rot)
- {
- if (!localRotLocked())
- {
- constexpr F32 BLEND_COEF = 0.25f;
- mLocalRot = lerp(BLEND_COEF, mLocalRot, new_local_rot);
- }
- }
- // Only call this if you know what you are doing; this should only be called
- // once before starting IK algorithm iterations.
- void Joint::setLocalScale(const LLVector3& scale)
- {
- // Compute final scale adustment to applly to mLocalPos and mBone
- constexpr F32 MIN_INVERTABLE_SCALE = 1.0e-15f;
- LLVector3 re_scale;
- for (U32 i = 0; i < 3; ++i)
- {
- // Verify mLocalScale component to avoid introducing NaN
- if (mLocalScale[i] > MIN_INVERTABLE_SCALE)
- {
- re_scale[i] = scale[i] / mLocalScale[i];
- }
- else
- {
- re_scale[i] = 0.f;
- }
- }
- // We remember the final scale adjustment for later...
- mLocalScale = scale;
- // ...and apply it immediately onto mLocalPos and mBone.
- mBone.scaleVec(re_scale);
- mLocalPos.scaleVec(re_scale);
- mLocalPosLength = mLocalPos.length();
- }
- LLVector3 Joint::getPreScaledLocalPos() const
- {
- LLVector3 pos = mLocalPos;
- // We inverse-scale mLocalPos because we already applied the info's scale
- // to mLocalPos so we could perform IK without constantly recomputing it,
- // and now we are being asked for mLocalPos in the info's pre-scaled frame.
- LLVector3 inv_scale = mInfoPtr->getScale();
- constexpr F32 MIN_INVERTABLE_SCALE = 1.0e-15f;
- for (U32 i = 0; i < 3; ++i)
- {
- // Verify mLocalScale component to avoid introducing NaN
- if (inv_scale[i] > MIN_INVERTABLE_SCALE)
- {
- inv_scale[i] = 1.f / inv_scale[i];
- }
- else
- {
- inv_scale[i] = 0.f;
- }
- }
- pos.scaleVec(inv_scale);
- return pos;
- }
- void Joint::adjustWorldRot(const LLQuaternion& adjustment)
- {
- mRot = mRot * adjustment;
- updateLocalRot();
- if (enforceConstraint())
- {
- applyLocalRot();
- }
- }
- void Joint::collectTargetPositions(std::vector<LLVector3>& local_targets,
- std::vector<LLVector3>& world_targets) const
- {
- // The "target positions" are points in the Joint local-frame which
- // correspond to points in other frames: either child positions or a target
- // end-effector. We need to know these positions in both local and world
- // frames.
- //
- // Note: it is expected this Joint has either: a target, or at least one
- // active child
- if (hasPosTarget())
- {
- local_targets.emplace_back(mBone);
- world_targets.emplace_back(mConfig->getTargetPos());
- }
- else
- {
- // *TODO: local_centroid and its length are invarient for the lifetime
- // of the Chains so we could pre-compute and cache them and simplify
- // the logic which consumes this info.
- for (const auto& child : mChildren)
- {
- if (child->isActive())
- {
- local_targets.emplace_back(child->mLocalPos);
- world_targets.emplace_back(child->mPos);
- }
- }
- }
- }
- void Joint::transformTargetsToParentLocal(std::vector<LLVector3>& local) const
- {
- if (mParent)
- {
- LLQuaternion world_to_parent = mParent->mRot;
- world_to_parent.transpose();
- for (auto& target : local)
- {
- LLVector3 world_target = (mPos + target * mRot) - mParent->mPos;
- target = world_target * world_to_parent;
- }
- }
- }
- bool Joint::swingTowardTargets(const std::vector<LLVector3>& local_targets,
- const std::vector<LLVector3>& world_targets)
- {
- if (localRotLocked())
- {
- // Nothing to do, but we assume targets are not yet aligned and return
- // 'true'
- return true;
- }
- constexpr F32 MIN_SWING_ANGLE = 0.001f * F_PI;
- bool something_changed = false;
- if (hasRotTarget())
- {
- mRot = mConfig->getTargetRot();
- something_changed = true;
- }
- else
- {
- size_t num_targets = local_targets.size();
- LLQuaternion adjustment;
- if (num_targets == 1)
- {
- LLVector3 old_bone = local_targets[0] * mRot;
- LLVector3 new_bone = world_targets[0] - mPos;
- adjustment.shortestArc(old_bone, new_bone);
- }
- else
- {
- adjustment.mQ[VW] = 0.f;
- for (size_t i = 0; i < num_targets; ++i)
- {
- LLVector3 old_bone = local_targets[i] * mRot;
- LLVector3 new_bone = world_targets[i] - mPos;
- LLQuaternion adj;
- adj.shortestArc(old_bone, new_bone);
- if (adj.mQ[VW] < 0.f)
- {
- // Negate to keep all arithmetic on the same hypersphere
- adjustment = adjustment - adj;
- }
- else
- {
- adjustment = adjustment + adj;
- }
- }
- adjustment.normalize();
- }
- if (!LLQuaternion::almost_equal(adjustment,
- LLQuaternion::DEFAULT, MIN_SWING_ANGLE))
- {
- // lerp the adjustment instead of using the full rotation: this
- // allows swing to distribute along the length of the chain.
- constexpr F32 SWING_FACTOR = 0.25f;
- adjustment = lerp(SWING_FACTOR, LLQuaternion::DEFAULT, adjustment);
- // compute mRot
- mRot = mRot * adjustment;
- mRot.normalize();
- something_changed = true;
- }
- }
- if (something_changed)
- {
- // Compute mLocalRot. Instead of calling updateLocalRot() which has
- // extra checks unnecessary in this context, we do the math explicitly.
- LLQuaternion inv_parent_rot = mParent->mRot;
- inv_parent_rot.transpose();
- mLocalRot = mRot * inv_parent_rot;
- mLocalRot.normalize();
- if (enforceConstraint())
- {
- applyLocalRot();
- #if LLIK_EXPERIMENTAL
- // EXPERIMENTAL: we hit the constraint during the swing; perhaps
- // some twist can get us closer
- twistTowardTargets(local_targets, world_targets);
- #endif
- }
- }
- return something_changed;
- }
- #if LLIK_EXPERIMENTAL
- void Joint::twistTowardTargets(const std::vector<LLVector3>& local_targets,
- const std::vector<LLVector3>& world_targets)
- {
- if (!mConstraint->allowsTwist())
- {
- return;
- }
- // Always twist about mConstraint->mForward axis
- LLVector3 axis = mConstraint->getForwardAxis() * mRot;
- LLQuaternion adjustment;
- size_t num_targets = local_targets.size();
- if (num_targets == 1)
- {
- // Transform to the world-frame with mPos as origin
- LLVector3 local_target = local_targets[0] * mRot;
- LLVector3 world_target = world_targets[0] - mPos;
- F32 target_length = local_target.length();
- constexpr F32 MIN_TARGET_LENGTH = 1.0e-2f;
- if (target_length < MIN_TARGET_LENGTH)
- {
- // Bone is too short
- return;
- }
- // Remove components parallel to axis
- local_target -= (local_target * axis) * axis;
- world_target -= (world_target * axis) * axis;
- if (local_target * world_target < 0.f)
- {
- // This discrepancy is better served with a swing
- return;
- }
- F32 radius = local_target.length();
- constexpr F32 MIN_RADIUS_FRACTION = 1.0e-2f;
- const F32 MIN_RADIUS = MIN_RADIUS_FRACTION * target_length;
- if (radius < MIN_RADIUS || world_target.length() < MIN_RADIUS)
- {
- // Twist movement too small to bother
- return;
- }
- // Compute the adjustment
- adjustment.shortestArc(local_target, world_target);
- }
- else
- {
- adjustment.mQ[VW] = 0.f;
- U32 num_adjustments = 0;
- for (size_t i = 0; i < local_targets.size(); ++i)
- {
- LLQuaternion adj;
- // Transform to the world-frame with mPos as origin
- LLVector3 local_target = local_targets[i] * mRot;
- LLVector3 world_target = world_targets[i] - mPos;
- F32 target_length = local_target.length();
- constexpr F32 MIN_TARGET_LENGTH = 1.0e-2f;
- if (target_length < MIN_TARGET_LENGTH)
- {
- // bone is too short
- adjustment = adjustment + adj;
- return;
- }
- // Remove components parallel to axis
- local_target -= (local_target * axis) * axis;
- world_target -= (world_target * axis) * axis;
- if (local_target * world_target < 0.f)
- {
- // This discrepancy is better served with a swing
- adjustment = adjustment + adj;
- return;
- }
- F32 radius = local_target.length();
- constexpr F32 MIN_RADIUS_FRACTION = 1.0e-2f;
- const F32 MIN_RADIUS = MIN_RADIUS_FRACTION * target_length;
- if (radius < MIN_RADIUS || world_target.length() < MIN_RADIUS)
- {
- // Twist movement will be too small
- adjustment = adjustment + adj;
- return;
- }
- // Compute the adjustment
- adj.shortestArc(local_target, world_target);
- adjustment = adjustment + adj;
- ++num_adjustments;
- }
- if (num_adjustments == 0)
- {
- return;
- }
- adjustment.normalize();
- }
- // lerp the adjustment instead of using the full rotation; this allows
- // twist to distribute along the length of the chain.
- constexpr F32 TWIST_BLEND = 0.4f;
- adjustment = lerp(TWIST_BLEND, LLQuaternion::DEFAULT, adjustment);
- // Compute mRot
- mRot = mRot * adjustment;
- mRot.normalize();
- // Compute mLocalRot. Instead of calling updateLocalRot() which has extra
- // checks unnecessary in this context, we do the math explicitly.
- LLQuaternion inv_parent_rot = mParent->mRot;
- inv_parent_rot.transpose();
- mLocalRot = mRot * inv_parent_rot;
- mLocalRot.normalize();
- if (enforceConstraint())
- {
- applyLocalRot();
- }
- }
- #endif // LLIK_EXPERIMENTAL
- void Joint::untwist()
- {
- if (hasRotTarget())
- {
- mRot = mConfig->getTargetRot();
- updateLocalRot();
- }
- else if (!localRotLocked())
- {
- // Compute new_local_rot
- LLQuaternion new_local_rot = LLQuaternion::DEFAULT;
- if (mConstraint && !hasDisabledConstraint())
- {
- new_local_rot = mConstraint->minimizeTwist(mLocalRot);
- }
- else
- {
- LLVector3 bone = mBone;
- bone.normalize();
- LLVector3 new_bone = bone * mLocalRot;
- LLVector3 swing_axis = bone % new_bone;
- constexpr F32 MIN_SWING_AXIS_LENGTH = 1.0e-3f;
- if (swing_axis.length() > MIN_SWING_AXIS_LENGTH)
- {
- F32 swing_angle = acosf(new_bone * bone);
- new_local_rot.setAngleAxis(swing_angle, swing_axis);
- }
- }
- // Blend toward new_local_rot
- constexpr F32 UNTWIST_BLEND = 0.25f;
- mLocalRot = lerp(UNTWIST_BLEND, mLocalRot, new_local_rot);
- // Note: if UNTWIST_BLEND is increased here the consequence will be
- // more noticeable occasional pops in some joints. It is an interaction
- // with transitions in/out of the
- // if (swing_axis.length() > MIN_SWING_AXIS_LENGTH)
- // condition above.
- // Apply new mLocalRot
- LLQuaternion new_rot = mLocalRot * mParent->mRot;
- if (!mParent->localRotLocked())
- {
- // Check to see if new mLocalRot would change world-frame bone
- // (which only happens for some Constraints)
- LLVector3 old_bone = mBone * mRot;
- LLVector3 new_bone = mBone * new_rot;
- constexpr F32 MIN_DELTA_COEF = 0.01f;
- if ((new_bone - old_bone).length() >
- MIN_DELTA_COEF * mBone.length())
- {
- // The new mLocalRot would change the world-frame bone
- // direction so we counter-rotate mParent to compensate.
- // Compute axis of correction
- LLVector3 axis = mParent->mBone * mParent->mRot;
- axis.normalize();
- // Project child bones to plane
- old_bone = old_bone - (old_bone * axis) * axis;
- new_bone = new_bone - (new_bone * axis) * axis;
- // Compute correction from new_bone back to old_bone
- LLQuaternion twist;
- twist.shortestArc(new_bone, old_bone);
- // Compute new parent rot
- LLQuaternion new_parent_rot = mParent->mRot * twist;
- new_parent_rot.normalize();
- mParent->setWorldRot(new_parent_rot);
- mParent->updateLocalRot();
- // Compute new rot
- new_rot = mLocalRot * mParent->mRot;
- }
- }
- mRot = new_rot;
- mRot.normalize();
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLIK::Solver class
- ///////////////////////////////////////////////////////////////////////////////
- void Solver::resetSkeleton()
- {
- mSkeleton.begin()->second->resetRecursively();
- }
- // Computes the offset from the "tip" of from_id to the "end" of to_id or the
- // negative when from_id > to_id
- LLVector3 Solver::computeReach(S16 to_id, S16 from_id) const
- {
- S16 ancestor = from_id;
- S16 descendent = to_id;
- bool swapped = false;
- if (ancestor > descendent)
- {
- ancestor = to_id;
- descendent = from_id;
- swapped = true;
- }
- LLVector3 reach;
- // Start at descendent and traverse up the limb until we find the ancestor
- joint_map_t::const_iterator itr = mSkeleton.find(descendent);
- if (itr != mSkeleton.end())
- {
- Joint::ptr_t joint = itr->second;
- LLVector3 chain_reach = joint->getBone();
- while (joint)
- {
- chain_reach += joint->getLocalPos();
- joint = joint->getParent();
- if (joint && joint->getID() == ancestor)
- {
- // Success !
- reach = chain_reach;
- break;
- }
- }
- }
- if (swapped)
- {
- reach = - reach;
- }
- return reach;
- }
- void Solver::addJoint(S16 joint_id, S16 parent_id, LLJoint* joint_info,
- const Constraint::ptr_t& constraint)
- {
- if (!joint_info)
- {
- llwarns_sparse << "Cannot add with NULL joint info." << llendl;
- return;
- }
- // Note: parent Joints must be added BEFORE their children.
- if (joint_id < 0)
- {
- llwarns << "Failed to add invalid joint_id=" << joint_id << llendl;
- return;
- }
- joint_map_t::iterator itr = mSkeleton.find(joint_id);
- if (itr != mSkeleton.end())
- {
- llwarns << "Failed to add joint_id=" << joint_id << ": already exists"
- << llendl;
- return;
- }
- Joint::ptr_t parent;
- itr = mSkeleton.find(parent_id);
- if (itr == mSkeleton.end())
- {
- if (parent_id >= mRootID)
- {
- llwarns << "failed to add joint_id=" << joint_id
- << ": could not find parent_id=" << parent_id << llendl;
- return;
- }
- }
- else
- {
- parent = itr->second;
- }
- Joint::ptr_t joint = std::make_shared<Joint>(joint_info);
- joint->setParent(parent);
- if (parent)
- {
- parent->addChild(joint);
- }
- mSkeleton.emplace(joint_id, joint);
- joint->setConstraint(constraint);
- }
- void Solver::addWristID(S16 wrist_id)
- {
- auto joint_itr = mSkeleton.find(wrist_id);
- if (joint_itr == mSkeleton.end())
- {
- llwarns << "Failed to find wrist_id=" << wrist_id << llendl;
- return;
- }
- mWristJoints.push_back(joint_itr->second);
- }
- #if LLIK_EXPERIMENTAL
- void Solver::adjustTargets(joint_config_map_t& targets)
- {
- // When an end-effector has both target_position and target_orientation
- // the IK problem can be reduced by giving the parent a target_position.
- // We scan targets for such conditions and when found: add/update the
- // parent's Target with target_position.
- for (auto& data_pair : targets)
- {
- Joint::Config& target = data_pair.second;
- U8 mask = target.getFlags();
- if (!target.hasWorldPos() || target.hasLocalRot() ||
- !target.hasWorldRot())
- {
- // Target does not match our needs
- continue;
- }
- S16 id = data_pair.first;
- auto joint_itr = mSkeleton.find(id);
- if (joint_itr == mSkeleton.end())
- {
- // Joint does not exist
- continue;
- }
- Joint::ptr_t& joint = joint_itr->second;
- const Joint::ptr_t& parent = joint->getParent();
- if (!parent)
- {
- // No parent
- continue;
- }
- // Compute parent's target pos. Note: we assume joint->mLocalPos ==
- // parent_joint->mBone (e.g. parent's end is same position as joint's
- // tip) which is not true in general, but is true for elbow->wrist.
- LLVector3 parent_target_pos = target.getPos() -
- joint->getBone() * target.getRot();
- auto parent_target_itr = targets.find(parent->getID());
- if (parent_target_itr != targets.end())
- {
- // parent already has a target --> modify it
- parent_target_itr->second.setPos(parent_target_pos);
- }
- else
- {
- // Parent does not have a target yet, so give it one.
- Joint::Config parent_target;
- parent_target.setPos(parent_target_pos);
- targets.insert({parent->getID(), parent_target});
- }
- // Delegate joint's target but set the joint active. The joint's world
- // transform will be updated during the IK iterations after all chains
- // have been processed.
- target.delegate();
- joint->activate();
- }
- }
- #endif // LLIK_EXPERIMENTAL
- // The Skeleton relaxes toward the T-pose and the IK solution will tend to put
- // the elbows higher than normal for a humanoid character. The dropElbow()
- // method tries to orient the elbows lower to achieve a more natural pose.
- void Solver::dropElbow(const Joint::ptr_t& wrist_joint)
- {
- const Joint::ptr_t& elbow_joint = wrist_joint->getParent();
- const Joint::ptr_t& shoulder_joint = elbow_joint->getParent();
- if (shoulder_joint->hasPosTarget())
- {
- // Remember: end-of-shoulder is tip-of-elbow. Assume whoever is setting
- // the shoulder's target position knows what they are doing.
- return;
- }
- // Compute some geometry
- LLVector3 shoulder_tip = shoulder_joint->getWorldTipPos();
- LLVector3 elbow_tip = elbow_joint->getWorldTipPos();
- LLVector3 elbow_end = elbow_joint->computeWorldEndPos();
- LLVector3 axis = elbow_end - shoulder_tip;
- axis.normalize();
- // Compute rotation of shoulder to bring upper-arm down
- LLVector3 down = (LLVector3::z_axis % axis) % axis;
- LLVector3 shoulder_bone = elbow_tip - shoulder_tip;
- LLVector3 projection = shoulder_bone - (shoulder_bone * axis) * axis;
- LLQuaternion adjustment;
- adjustment.shortestArc(projection, down);
- // Adjust shoulder to bring upper-arm down
- shoulder_joint->adjustWorldRot(adjustment);
- // elbow_joint's mLocalRot remains unchanged, but we need to update its
- // world-frame transforms
- elbow_joint->updatePosAndRotFromParent();
- if (wrist_joint->isActive())
- {
- // In theory: only wrist_joint's mLocalRot has changed, not its
- // world-frame transform.
- wrist_joint->updateLocalRot();
- // *TODO ? Enforce twist of wrist's Constraint and back-rotate the
- // elbow-drop to compensate
- }
- }
- bool Solver::updateJointConfigs(const joint_config_map_t& configs)
- {
- bool something_changed = configs.size() != mJointConfigs.size();
- // Check to see if configs changed since last iteration.
- if (!something_changed)
- {
- for (const auto& data_pair : mJointConfigs)
- {
- joint_config_map_t::const_iterator itr =
- configs.find(data_pair.first);
- if (itr == configs.end())
- {
- something_changed = true;
- break;
- }
- // Found old target in current configs.
- const Joint::Config& old_target = data_pair.second;
- const Joint::Config& new_target = itr->second;
- U8 mask = old_target.getFlags();
- if (mask != new_target.getFlags())
- {
- something_changed = true;
- break;
- }
- if ((mask & CONFIG_FLAG_TARGET_POS) &&
- dist_vec(old_target.getTargetPos(),
- new_target.getTargetPos()) > mAcceptableError)
- {
- something_changed = true;
- break;
- }
- if ((mask & CONFIG_FLAG_TARGET_ROT) &&
- !LLQuaternion::almost_equal(old_target.getTargetRot(),
- new_target.getTargetRot()))
- {
- something_changed = true;
- break;
- }
- if ((mask & CONFIG_FLAG_LOCAL_POS) &&
- dist_vec(old_target.getLocalPos(),
- new_target.getLocalPos()) > mAcceptableError)
- {
- something_changed = true;
- break;
- }
- if ((mask & CONFIG_FLAG_LOCAL_ROT) &&
- !LLQuaternion::almost_equal(old_target.getLocalRot(),
- new_target.getLocalRot()))
- {
- something_changed = true;
- break;
- }
- }
- }
- if (something_changed)
- {
- mJointConfigs = configs;
- }
- return something_changed;
- }
- void Solver::rebuildAllChains()
- {
- // Before recompute chains: clear active status on old chains
- for (const auto& data_pair : mChainMap)
- {
- const joint_list_t& chain = data_pair.second;
- for (const Joint::ptr_t& joint : chain)
- {
- joint->resetFlags();
- }
- }
- mChainMap.clear();
- mActiveRoots.clear();
- // makeChains
- //
- // Consider the following hypothetical skeleton, where each Joint tip
- // has a numerical ID and each end-effector tip is denoted with
- // bracketed [ID]:
- // 8 [11]
- // / /
- // 7---14--[15] 10
- // / /
- // 6---12---13 9
- // / /
- // 0----1---2----3----4---[5]--16---17--[18]
- // `
- // 19
- // `
- // [20]
- //
- // The target ID list is: [5,11,15,18,20].
- // IK would need to solve all joints except for [8,12,13].
- // In other words: all Joints are "active" except [8,12,13].
- //
- // We divide the Skeleton into "chain segments" that start at a targeted
- // Joint and continue up until: root (0), end-effector ([ID]), or
- // sub-base (Joint with multiple children).
- //
- // Inward passes operate on the Chains in order such that when it is time
- // to update a sub-base all of its active children will have already been
- // updated: it will be able to compute the centroid of its mWorldEndPos.
- //
- // Outward passes also only operate on the Chains. This simplifies
- // the logic because there will be no need to check for target or sub-base
- // until the end of a Chain is reached. Any Joint not on a Chain (e.g.
- // non-active) will keep its parent-relative rotation.
- //
- // The initial chain list would be:
- // { 5:[5,4,3,2]
- // 11:[11,10,9,5]
- // 15:[15,14,7]
- // 18:[18,17,16,5]
- // 20:[20,19,2] }
- // Where all chains include their end_point and also sub-base.
- // The remaining active non-targeted sub_base_map would be:
- // { 2:[2,1,0]
- // 7:[7,6]
- // 6:[6,2] }
- // In this scenario Joints (6) and (7) are "false" sub-bases: they
- // don't have targets and have multiple children but only one of them is "active".
- // We can condense the chains to be:
- // { 5:[5,4,3,2]
- // 11:[11,10,9,5]
- // 15:[15,14,7,6,2]
- // 18:[18,17,16,5]
- // 20:[20,19,2] }
- // and:
- // { 2:[2,1,0] }
- //
- std::set<S16> sub_bases;
- // mJointConfigs is sorted by joint_id low-to-high and we rely on this in
- // buildChain().
- for (auto& data_pair : mJointConfigs)
- {
- // Make sure joint_id is valid
- S16 joint_id = data_pair.first;
- joint_map_t::iterator itr = mSkeleton.find(joint_id);
- if (itr == mSkeleton.end())
- {
- continue;
- }
- Joint::ptr_t joint = itr->second;
- // Joint caches a pointer to the Target and the Joint::Config will
- // remain valid for the duration of the IK iterations.
- Joint::Config& config = data_pair.second;
- joint->setConfig(config);
- if (joint->getID() == mRootID)
- {
- // For root world-frame == local-frame
- U8 flags = joint->getConfigFlags();
- if (flags & MASK_ROT)
- {
- LLQuaternion q =
- (flags & CONFIG_FLAG_LOCAL_ROT) ? config.getLocalRot()
- : config.getTargetRot();
- joint->lockLocalRot(q);
- joint->activate();
- mActiveRoots.insert(joint);
- }
- if (flags & MASK_POS)
- {
- LLVector3 p =
- (flags & CONFIG_FLAG_LOCAL_POS) ? config.getLocalPos()
- : config.getTargetPos();
- joint->setLocalPos(p);
- joint->activate();
- }
- if (flags & CONFIG_FLAG_LOCAL_SCALE)
- {
- joint->setLocalScale(config.getLocalScale());
- }
- continue;
- }
- if (config.hasLocalRot())
- {
- joint->lockLocalRot(config.getLocalRot());
- }
- #if LLIK_EXPERIMENTAL
- if (config.hasDelegated())
- {
- // Do not build chain for delegated Target
- continue;
- }
- #endif
- if (config.hasTargetPos())
- {
- // Add and build chain
- mChainMap[joint_id] = joint_list_t();
- buildChain(joint, mChainMap[joint_id], sub_bases);
- // *HACK or FIX ? If we have sequential end effectors, we are not
- // guaranteed the expression module has sent us positions that can
- // be solved. We will instead assume that the child's position is
- // higher prioriy than the parent, get direction from child to
- // parent and move the parent's target to the exact bone length.
- // *TODO: will not work correctly for a parent with multiple direct
- // children with effector targets. Because we create the targets
- // form low to high we will know if the parent is an end-effector.
- Joint::ptr_t parent = joint->getParent();
- if (parent->hasPosTarget())
- {
- // Sequential targets detected
- LLVector3 child_target_pos = config.getTargetPos();
- LLVector3 parent_target_pos = parent->getTargetPos();
- LLVector3 direction = parent_target_pos - child_target_pos;
- direction.normalize();
- direction *= joint->getLocalPosLength();
- parent_target_pos = child_target_pos + direction;
- parent->setTargetPos(parent_target_pos);
- }
- }
- else if (config.hasLocalPos())
- {
- joint->setLocalPos(config.getLocalPos());
- joint->activate();
- }
- if (config.hasLocalScale())
- {
- joint->setLocalScale(config.getLocalScale());
- joint->activate();
- }
- }
- // Each sub_base gets its own Chain
- while (sub_bases.size() > 0)
- {
- std::set<S16> new_sub_bases;
- for (S16 joint_id : sub_bases)
- {
- // Add and build chain
- Joint::ptr_t joint = mSkeleton[joint_id];
- mChainMap[joint_id] = joint_list_t();
- buildChain(joint, mChainMap[joint_id], new_sub_bases);
- }
- sub_bases = std::move(new_sub_bases);
- }
- // Eliminate "false" sub-bases and condense the Chains; search for
- // Chain-joins.
- std::vector<U16> joins;
- for (const auto& data_pair : mChainMap)
- {
- const Joint::ptr_t& outer_end = data_pair.second[0];
- if (!outer_end->hasPosTarget() && !isSubBase(outer_end->getID()))
- {
- Joint::ptr_t active_child = outer_end->getSingleActiveChild();
- if (active_child)
- {
- // outer_end does not have a target, is not flagged as subbase,
- // and has only one active_child --> it is a "false" sub-base
- // and we will try to "join" this Chain to another.
- joins.push_back(outer_end->getID());
- }
- }
- }
- // Make the joins
- for (U16 id : joins)
- {
- // Hunt for recipient chain
- for (auto& data_pair : mChainMap)
- {
- auto& recipient = data_pair.second;
- const Joint::ptr_t& inner_end = recipient[recipient.size() - 1];
- if (inner_end->getID() == id)
- {
- // Copy donor to recipient
- const auto& donor = mChainMap[id];
- recipient.insert(recipient.end(), ++(donor.begin()),
- donor.end());
- // Erase donor
- mChainMap.erase(id);
- break;
- }
- }
- }
- // Cache the set of active branch roots
- for (auto& data_pair : mChainMap)
- {
- auto& chain = data_pair.second;
- size_t last_index = chain.size() - 1;
- Joint::ptr_t chain_base = chain[last_index];
- Joint::ptr_t parent = chain_base->getParent();
- if (!parent || !parent->isActive())
- {
- mActiveRoots.insert(chain_base);
- }
- }
- // Cache the list of all active joints
- mActiveJoints.clear();
- for (auto& data_pair : mSkeleton)
- {
- if (data_pair.second->isActive())
- {
- mActiveJoints.push_back(data_pair.second);
- data_pair.second->flagForHarvest();
- }
- }
- }
- ////////////////////////////////////// Solvers ////////////////////////////////
- F32 Solver::solve()
- {
- rebuildAllChains();
- // Before each solve: we relax a fraction toward the reset pose. This
- // provides return pressure that removes floating-point drift that would
- // otherwise wander around within the valid zones of the constraints.
- constexpr F32 INITIAL_RELAXATION_FACTOR = 0.25f;
- for (auto& root : mActiveRoots)
- {
- root->relaxRotationsRecursively(INITIAL_RELAXATION_FACTOR);
- }
- constexpr U32 MAX_FABRIK_ITERATIONS = 16;
- constexpr U32 MIN_FABRIK_ITERATIONS = 4;
- F32 max_error = F32_MAX;
- for (U32 loop = 0;
- loop < MIN_FABRIK_ITERATIONS ||
- (loop < MAX_FABRIK_ITERATIONS && max_error > mAcceptableError);
- ++loop)
- {
- max_error = solveOnce();
- }
- mLastError = max_error;
- return mLastError;
- }
- F32 Solver::solveOnce()
- {
- constexpr bool constrain = true;
- constexpr bool drop_elbow = true;
- constexpr bool untwist = true;
- #if LLIK_EXPERIMENTAL
- executeCcd(constrain, drop_elbow, untwist);
- #endif
- executeFabrik(constrain, drop_elbow, untwist);
- return measureMaxError();
- }
- void Solver::executeFabrik(bool constrain, bool drop_elbow, bool untwist)
- {
- executeFabrikPass();
- // Pull elbows downward toward a more natual pose
- for (const auto& wrist_joint : mWristJoints)
- {
- dropElbow(wrist_joint);
- }
- if (!constrain)
- {
- return;
- }
- // Since our FABRIK implementation does not enforce constraints during the
- // forward/backward passes, we do it here.
- enforceConstraintsOutward();
- if (!untwist)
- {
- return;
- }
- // It is often possible to remove excess twist between the Joints without
- // swinging their bones in the world-frame. We try this now to help reduce
- // the "spin drift" that can occur where Joint orientations pick up
- // systematic and floating-point errors and drift within the twist-limits
- // of their constraints.
- for (const auto& data_pair : mChainMap)
- {
- const joint_list_t& chain = data_pair.second;
- untwistChain(chain);
- }
- executeFabrikPass();
- // Note: we do not bother enforcing constraints after untwisting.
- }
- LLVector3 Solver::getJointLocalPos(S16 joint_id) const
- {
- LLVector3 pos;
- auto itr = mSkeleton.find(joint_id);
- if (itr != mSkeleton.end())
- {
- pos = itr->second->getLocalPos();
- }
- return pos;
- }
- bool Solver::getJointLocalTransform(S16 joint_id, LLVector3& pos,
- LLQuaternion& rot) const
- {
- auto itr = mSkeleton.find(joint_id);
- if (itr == mSkeleton.end())
- {
- return false;
- }
- pos = itr->second->getLocalPos();
- rot = itr->second->getLocalRot();
- return true;
- }
- LLVector3 Solver::getJointWorldEndPos(S16 joint_id) const
- {
- LLVector3 pos;
- joint_map_t::const_iterator itr = mSkeleton.find(joint_id);
- if (itr != mSkeleton.end())
- {
- pos = itr->second->computeWorldEndPos();
- }
- return pos;
- }
- LLQuaternion Solver::getJointWorldRot(S16 joint_id) const
- {
- LLQuaternion rot;
- joint_map_t::const_iterator itr = mSkeleton.find(joint_id);
- if (itr != mSkeleton.end())
- {
- rot = itr->second->getWorldRot();
- }
- return rot;
- }
- void Solver::resetJointGeometry(S16 joint_id,
- const Constraint::ptr_t& constraint)
- {
- joint_map_t::iterator itr = mSkeleton.find(joint_id);
- if (itr == mSkeleton.end())
- {
- llwarns << "Failed update unknown joint_id=" << joint_id << llendl;
- return;
- }
- const Joint::ptr_t& joint = itr->second;
- joint->resetFromInfo();
- joint->setConstraint(constraint);
- // Note: will need to call computeReach() after all Joints geometries are
- // reset.
- }
- void Solver::buildChain(Joint::ptr_t joint, joint_list_t& chain,
- std::set<S16>& sub_bases)
- {
- // Builds a Chain in descending order (inward) from end-effector or sub-
- // base. Stops at next end-effector (has target), sub-base (more than one
- // active child), or root. Side effect: sets each Joint on chain "active".
- chain.push_back(joint);
- joint->activate();
- // Walk up the chain of ancestors and add to chain but stop at: end-effector,
- // sub-base, or root. When a sub-base is encountered push its id onto
- // sub_bases.
- joint = joint->getParent();
- while (joint)
- {
- chain.push_back(joint);
- joint->activate();
- S16 joint_id = joint->getID();
- // Yes, add the joint to the chain before the break checks below
- // because we want to include the final joint (e.g. root, sub-base, or
- // previously targeted joint) at the end of the chain.
- if (isSubRoot(joint_id))
- {
- // AURA hack to deal with lack of constraints in spine
- break;
- }
- if (joint_id == mRootID)
- {
- break;
- }
- if (joint->hasPosTarget())
- {
- // Truncate this chain at targeted ancestor joint
- break;
- }
- if ((mSubBaseIds.empty() && joint->getNumChildren() > 1) ||
- isSubBase(joint_id))
- {
- sub_bases.insert(joint_id);
- break;
- }
- joint = joint->getParent();
- }
- }
- void Solver::executeFabrikInward(const joint_list_t& chain)
- {
- // Chain starts at end-effector or sub-base. Do not forget: chain is
- // organized in descending order: for inward pass we traverse the chain
- // forward.
- // Outer end of chain is special: it either has a target or is a sub-base
- // with active children
- chain[0]->updateEndInward();
- // Traverse Chain forward. Skip first Joint in chain (the "outer end"): we
- // just handled it. Also skip last Joint in chain (the "inner end"): it is
- // either the outer end of another chain (and will be updated then) or it
- // is one of the "active roots" and will be handled after all chains.
- S32 last_index = (S32)(chain.size()) - 1;
- for (S32 i = 1; i < last_index; ++i)
- {
- chain[i]->updateInward(chain[i - 1]);
- }
- }
- void Solver::executeFabrikOutward(const joint_list_t& chain)
- {
- // Chain starts at a end-effector or sub-base. Do not forget: chain is
- // organized in descending order: for outward pass we traverse the chain
- // in reverse.
- S32 last_index = (S32)(chain.size()) - 1;
- // Skip the Joint at last_index: chain's inner-end does not move at this
- // stage. Traverse the middle of chain in reverse
- for (S32 i = last_index - 1; i > 0; --i)
- {
- chain[i]->updateOutward();
- }
- // Outer end of chain is special: it either has a target or is a sub-base
- // with active children
- chain[0]->updateEndOutward();
- }
- void Solver::shiftChainToBase(const joint_list_t& chain)
- {
- size_t last_index = (S32)(chain.size()) - 1;
- const Joint::ptr_t& inner_end_child = chain[last_index - 1];
- LLVector3 offset = inner_end_child->computeWorldTipOffset();
- if (offset.lengthSquared() > mAcceptableError * mAcceptableError)
- {
- for (size_t i = 0; i < last_index; ++i)
- {
- chain[i]->shiftPos(-offset);
- }
- }
- }
- void Solver::executeFabrikPass()
- {
- // FABRIK = Forward And Backward Reching Inverse Kinematics
- // http://andreasaristidou.com/FABRIK.html
- // mChainMap is sorted by outer_end joint_id, low-to-high so for the inward
- // pass we traverse the chains in reverse order.
- for (chain_map_t::const_reverse_iterator itr = mChainMap.rbegin(),
- rend = mChainMap.rend();
- itr != rend; ++itr)
- {
- executeFabrikInward(itr->second);
- }
- // executeFabrikInward() does not update child mLocalRots for the inner_end
- // so we must do it manually for each active root
- for (auto& root : mActiveRoots)
- {
- root->updateChildLocalRots();
- }
- // The outward pass must solve the combined set of chains from-low-to-high
- // so we process them in forward order.
- for (const auto& data_pair : mChainMap)
- {
- const joint_list_t& chain = data_pair.second;
- executeFabrikOutward(chain);
- }
- }
- void Solver::enforceConstraintsOutward()
- {
- for (const auto& data_pair : mChainMap)
- {
- const joint_list_t& chain = data_pair.second;
- // Chain starts at a end-effector or sub-base. Do not forget: chain is
- // organized in descending order: for outward pass we traverse the
- // chain in reverse.
- S32 last_index = (S32)(chain.size()) - 1;
- // Skip the Joint at last_index: chain's inner-end does not move at
- // this stage. Traverse the middle of chain in reverse.
- for (S32 i = last_index - 1; i > -1; --i)
- {
- const Joint::ptr_t& joint = chain[i];
- joint->updatePosAndRotFromParent();
- if (joint->enforceConstraint())
- {
- joint->applyLocalRot();
- }
- }
- chain[0]->updateChildLocalRots();
- }
- }
- #if LLIK_EXPERIMENTAL
- void Solver::executeCcd(bool constrain, bool drop_elbow, bool untwist)
- {
- // *TODO:
- // - modify executeCcdPass() to handle enforce_constraints;
- // - handle drop_elbow before CCD pass;
- // - handle untwist ?
- executeCcdPass(constrain);
- }
- // Cyclic Coordinate Descend (CCD) is an alternative IK algorithm.
- // http://rodolphe-vaillant.fr/entry/114/cyclic-coordonate-descent-inverse-kynematic-ccd-ik
- //
- // It converges well however is more susceptible than FABRIK to solution
- // instability when Constraints are being enforced. We keep it around just in
- // case we want to try it, or for when we figure out how to enforce Constraints
- // without making CCD unstable.
- void Solver::executeCcdPass(bool constrain)
- {
- // mChainMap is sorted by outer_end joint_id, low-to-high and CCD is an
- // inward pass, so we traverse the map in reverse
- for (chain_map_t::const_reverse_iterator itr = mChainMap.rbegin(),
- rend = mChainMap.rend();
- itr != rend; ++itr)
- {
- executeCcdInward(itr->second, constrain);
- }
- // executeCcdInward(chain) recomputes world-frame transform of all Joints
- // in chain... except the child of the chain's inner_end. Now that all
- // chains are solved we shift each chain to connect with its sub-base.
- for (auto& data_pair: mChainMap)
- {
- shiftChainToBase(data_pair.second);
- }
- }
- void Solver::executeCcdInward(const joint_list_t& chain, bool constrain)
- {
- // 'chain' starts at a end-effector or sub-base. Do not forget: 'chain' is
- // organized in descending order: for inward pass we traverse the Chain
- // forward.
- const Joint::ptr_t& outer_end = chain[0];
- // outer_end has one or more targets known in both local and world frames.
- // For CCD we'll be swinging each joint of the Chain as we traverse inward
- // in attempts to get the local-frame targets to align with their world-
- //frame counterparts.
- std::vector<LLVector3> local_targets, world_targets;
- outer_end->collectTargetPositions(local_targets, world_targets);
- if (!outer_end->swingTowardTargets(local_targets, world_targets))
- {
- // Targets are close enough
- return;
- }
- // Traverse Chain forward and swing each part. Skip first Joint in 'chain'
- // (the "outer end"): we just handled it. Also skip last Joint in 'chain'
- // (the "inner end"): it is either the outer end of another Chain (and will
- // be updated as part of a subsequent Chain) or it is one of the "active
- // roots" and is not moved.
- S32 last_index = (S32)(chain.size()) - 1;
- S32 last_swung_index = 0;
- for (S32 i = 1; i < last_index; ++i)
- {
- chain[i - 1]->transformTargetsToParentLocal(local_targets);
- if (!chain[i]->swingTowardTargets(local_targets, world_targets))
- {
- break;
- }
- last_swung_index = i;
- }
- // Update the world-frame transforms of swung Joints
- for (S32 i = last_swung_index - 1; i > -1; --i)
- {
- chain[i]->updatePosAndRotFromParent();
- }
- // Finally: make sure to update outer_end's childrens' mLocalRots. Note: we
- // do not bother to enforce constraints in this step.
- outer_end->updateChildLocalRots();
- }
- #endif // LLIK_EXPERIMENTAL
- void Solver::untwistChain(const joint_list_t& chain)
- {
- S32 last_index = (S32)(chain.size()) - 1;
- // Note: we start at last_index-1 becuase Joint::untwist() will affect its
- // parent's twist and we don't want to mess with the inner_end of the chain
- // since it will be handled later in another chain.
- for (S32 i = last_index - 1; i > -1; --i)
- {
- chain[i]->untwist();
- }
- chain[0]->updateChildLocalRots();
- }
- F32 Solver::measureMaxError()
- {
- F32 max_error = 0.f;
- for (auto& data_pair : mJointConfigs)
- {
- S16 joint_id = data_pair.first;
- if (joint_id == mRootID)
- {
- // Skip error measure of root joint: should always be zero.
- continue;
- }
- Joint::Config& target = data_pair.second;
- #if LLIK_EXPERIMENTAL
- if (target.hasTargetPos() && !target.hasDelegated())
- #else
- if (target.hasTargetPos())
- #endif
- {
- LLVector3 end_pos = mSkeleton[joint_id]->computeWorldEndPos();
- F32 dist = dist_vec(end_pos, target.getTargetPos());
- if (dist > max_error)
- {
- max_error = dist;
- }
- }
- }
- return max_error;
- }
- } // namespace LLIK
- ///////////////////////////////////////////////////////////////////////////////
- // LLIKConstraintFactory class
- ///////////////////////////////////////////////////////////////////////////////
- void LLIKConstraintFactory::initSingleton()
- {
- std::string filename =
- gDirUtil.getFullPath(LL_PATH_CHARACTER, "avatar_constraint.llsd");
- llifstream file(filename.c_str());
- if (!file.is_open())
- {
- llwarns << "Error opening the IK constraints file: " << filename
- << llendl;
- return;
- }
- LLSD map;
- if (!LLSDSerialize::deserialize(map, file, LLSDSerialize::SIZE_UNLIMITED))
- {
- llwarns << "Unable to load and parse IK constraints from: " << filename
- << llendl;
- return;
- }
- for (LLSD::map_const_iterator it = map.beginMap(), end = map.endMap();
- it != end; ++it)
- {
- const std::string& joint_name = it->first;
- const LLSD& data = it->second;
- LLIK::Constraint::ptr_t ptr = getConstraint(data);
- if (ptr)
- {
- mJointMapping.emplace(joint_name, ptr);
- }
- }
- }
- LLIK::Constraint::ptr_t
- LLIKConstraintFactory::getConstrForJoint(const std::string& joint_name) const
- {
- auto it = mJointMapping.find(joint_name);
- return it != mJointMapping.end() ? it->second : LLIK::Constraint::ptr_t();
- }
- LLIK::Constraint::ptr_t LLIKConstraintFactory::getConstraint(const LLSD& data)
- {
- LLIK::Constraint::ptr_t ptr = create(data);
- if (ptr)
- {
- U64 hash = ptr->getHash();
- auto it = mConstraints.find(hash);
- if (it != mConstraints.end())
- {
- ptr = it->second;
- }
- else
- {
- mConstraints[hash] = ptr;
- }
- }
- return ptr;
- }
- //static
- LLIK::Constraint::ptr_t LLIKConstraintFactory::create(const LLSD& data)
- {
- std::string type = data["type"].asString();
- LLStringUtil::toUpper(type);
- LLIK::Constraint::ptr_t ptr;
- if (type == SIMPLE_CONE_NAME)
- {
- ptr = std::make_shared<LLIK::SimpleCone>(data);
- }
- else if (type == TWIST_LIMITED_CONE_NAME)
- {
- ptr = std::make_shared<LLIK::TwistLimitedCone>(data);
- }
- else if (type == ELBOW_NAME)
- {
- ptr = std::make_shared<LLIK::ElbowConstraint>(data);
- }
- else if (type == KNEE_NAME)
- {
- ptr = std::make_shared<LLIK::KneeConstraint>(data);
- }
- else if (type == ACUTE_ELLIPSOIDAL_NAME)
- {
- ptr = std::make_shared<LLIK::AcuteEllipsoidalCone>(data);
- }
- else if (type == DOUBLE_LIMITED_HINGE_NAME)
- {
- ptr = std::make_shared<LLIK::DoubleLimitedHinge>(data);
- }
- return ptr;
- }
|