123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- /**
- * @file lljointsolverrp3.cpp
- * @brief Implementation of LLJointSolverRP3 class.
- * Joint solver in Real Projective 3D space (RP3).
- * See: https://en.wikipedia.org/wiki/Real_projective_space
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "lljointsolverrp3.h"
- #include "llmath.h"
- #define F_EPSILON 0.00001f
- LLJointSolverRP3::LLJointSolverRP3()
- : mJointA(NULL),
- mJointB(NULL),
- mJointC(NULL),
- mJointGoal(NULL),
- mLengthAB(1.f),
- mLengthBC(1.f),
- mUseBAxis(false),
- mTwist(0.f)
- {
- mPoleVector.set(1.f, 0.f, 0.f);
- }
- void LLJointSolverRP3::setupJoints(LLJoint* joint_a, LLJoint* joint_b,
- LLJoint* joint_c, LLJoint* joint_goal)
- {
- mJointA = joint_a;
- mJointB = joint_b;
- mJointC = joint_c;
- mJointGoal = joint_goal;
- mLengthAB = mJointB->getPosition().length();
- mLengthBC = mJointC->getPosition().length();
- mJointABaseRotation = joint_a->getRotation();
- mJointBBaseRotation = joint_b->getRotation();
- }
- void LLJointSolverRP3::solve()
- {
- // Setup joints in their base rotations
- mJointA->setRotation(mJointABaseRotation);
- mJointB->setRotation(mJointBBaseRotation);
- // Get joint positions in world space
- LLVector3 a_pos = mJointA->getWorldPosition();
- LLVector3 b_pos = mJointB->getWorldPosition();
- LLVector3 c_pos = mJointC->getWorldPosition();
- LLVector3 g_pos = mJointGoal->getWorldPosition();
- // Get the pole_vector in world space
- LLMatrix4 world_joint_a_parent_mat;
- if (mJointA->getParent())
- {
- world_joint_a_parent_mat = mJointA->getParent()->getWorldMatrix();
- }
- LLVector3 pole_vec = rotate_vector(mPoleVector, world_joint_a_parent_mat);
- LLVector3 ab_vec = b_pos - a_pos; // vector from A to B
- LLVector3 bc_vec = c_pos - b_pos; // vector from B to C
- LLVector3 ac_vec = c_pos - a_pos; // vector from A to C
- LLVector3 ag_vec = g_pos - a_pos; // vector from A to G (goal)
- // Compute needed lengths of those vectors
- F32 ab_len = ab_vec.length();
- F32 bc_len = bc_vec.length();
- F32 ag_len = ag_vec.length();
- // Compute component vector of (A->B) orthogonal to (A->C)
- LLVector3 abac_comp_ortho_vec = ab_vec - ac_vec * ((ab_vec * ac_vec) /
- (ac_vec * ac_vec));
- // Compute the normal of the original ABC plane (and store for later)
- LLVector3 abc_norm;
- if (!mUseBAxis)
- {
- if (are_parallel(ab_vec, bc_vec, 0.001f))
- {
- // The current solution is maxed out, so we use the axis that is
- // orthogonal to both pole_vec and A->B
- if (are_parallel(pole_vec, ab_vec, 0.001f))
- {
- // ACK ! The problem is singular
- if (are_parallel(pole_vec, ag_vec, 0.001f))
- {
- // The solution is also singular
- return;
- }
- else
- {
- abc_norm = pole_vec % ag_vec;
- }
- }
- else
- {
- abc_norm = pole_vec % ab_vec;
- }
- }
- else
- {
- abc_norm = ab_vec % bc_vec;
- }
- }
- else
- {
- abc_norm = mBAxis * mJointB->getWorldRotation();
- }
- // Compute rotation of B.
- // Angle between A->B and B->C
- F32 abbc_ang = angle_between(ab_vec, bc_vec);
- // Vector orthogonal to A->B and B->C
- LLVector3 abbc_ortho_vec = ab_vec % bc_vec;
- if (abbc_ortho_vec.lengthSquared() < 0.001f)
- {
- abbc_ortho_vec = pole_vec % ab_vec;
- abac_comp_ortho_vec = pole_vec;
- }
- abbc_ortho_vec.normalize();
- F32 ag_len_qq = ag_len * ag_len;
- // Angle arm for extension
- F32 cos_theta = (ag_len_qq - ab_len * ab_len - bc_len * bc_len) /
- (2.f * ab_len * bc_len);
- if (cos_theta > 1.f)
- {
- cos_theta = 1.f;
- }
- else if (cos_theta < -1.f)
- {
- cos_theta = -1.f;
- }
- F32 theta = acosf(cos_theta);
- LLQuaternion b_rot(theta - abbc_ang, abbc_ortho_vec);
- // Compute rotation that rotates new A->C to A->G
- bc_vec = bc_vec * b_rot; // rotate B->C by b_rot
- // Update A->C
- ac_vec = ab_vec + bc_vec;
- LLQuaternion cg_rot;
- cg_rot.shortestArc(ac_vec, ag_vec);
- // Update A->B and B->C with rotation from C to G
- ab_vec = ab_vec * cg_rot;
- bc_vec = bc_vec * cg_rot;
- abc_norm = abc_norm * cg_rot;
- ac_vec = ab_vec + bc_vec;
- // Compute the normal of the APG plane
- if (are_parallel(ag_vec, pole_vec, 0.001f))
- {
- // The solution plane is undefined ==> we are done
- return;
- }
- LLVector3 apg_norm = pole_vec % ag_vec;
- apg_norm.normalize();
- // Compute the normal of the new ABC plane (only necessary if we are NOT
- // using mBAxis
- if (!mUseBAxis)
- {
- if (!are_parallel(ab_vec, bc_vec, 0.001f))
- {
- abc_norm = ab_vec % bc_vec;
- }
- #if 0
- else
- {
- // G is either too close or too far away we will use the old
- // ABCnormal
- }
- #endif
- abc_norm.normalize();
- }
- // Calcuate plane rotation
- LLQuaternion p_rot;
- if (are_parallel(abc_norm, apg_norm, 0.001f))
- {
- if (abc_norm * apg_norm < 0.f)
- {
- // We must be PI radians off ==> rotate by PI around ag_vec
- p_rot.setAngleAxis(F_PI, ag_vec);
- }
- #if 0
- else
- {
- // We are done
- }
- #endif
- }
- else
- {
- p_rot.shortestArc(abc_norm, apg_norm);
- }
- // Compute twist rotation
- LLQuaternion twist_rot(mTwist, ag_vec);
- // Compute rotation of A
- LLQuaternion a_rot = cg_rot * p_rot * twist_rot;
- // Apply the rotations
- mJointB->setWorldRotation(mJointB->getWorldRotation() * b_rot);
- mJointA->setWorldRotation(mJointA->getWorldRotation() * a_rot);
- }
|