Improve low angle interception when rotating

master
Beq 2025-06-01 00:42:29 +01:00
parent 966a9333c0
commit 30db8c80a7
2 changed files with 50 additions and 21 deletions

View File

@ -710,10 +710,7 @@ void FSManipRotateJoint::render()
}
// update visibility and rotation center.
if (!updateVisiblity())
{
return;
}
bool activeJointVisible = updateVisiblity();
// Setup GL state.
LLGLSUIDefault gls_ui;
gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep);
@ -742,6 +739,11 @@ void FSManipRotateJoint::render()
}
}
if (!activeJointVisible)
{
return;
}
// Update joint world matrices.
mJoint->updateWorldMatrixParent();
mJoint->updateWorldMatrix();
@ -753,30 +755,21 @@ void FSManipRotateJoint::render()
LLQuaternion currentLocalRot = mJoint->getRotation();
LLQuaternion rotatedNaturalAlignment = mNaturalAlignmentQuat * currentLocalRot;
rotatedNaturalAlignment.normalize();
// Compute the final world alignment:
LLQuaternion final_world_alignment = rotatedNaturalAlignment * parentWorldRot;
final_world_alignment.normalize();
const LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
LLCachedControl<bool> use_natural_direction(gSavedSettings, "FSManipRotateJointUseNaturalDirection", true);
LLQuaternion active_rotation = use_natural_direction? final_world_alignment : joint_world_rotation;
active_rotation.normalize();
// Render the manipulator rings in a separate function.
gGL.matrixMode(LLRender::MM_MODELVIEW);
renderAxes(agent_space_center, mRadiusMeters * 1.5f, active_rotation);
renderManipulatorRings(agent_space_center, active_rotation);
// Debug: render joint's Euler angles for diagnostic purposes.
LLVector3 euler_angles;
active_rotation.getEulerAngles(&euler_angles.mV[0],
&euler_angles.mV[1],
&euler_angles.mV[2]);
euler_angles *= RAD_TO_DEG;
euler_angles.mV[0] = ll_round(fmodf(euler_angles.mV[0] + 360.f, 360.f), 0.05f);
euler_angles.mV[1] = ll_round(fmodf(euler_angles.mV[1] + 360.f, 360.f), 0.05f);
euler_angles.mV[2] = ll_round(fmodf(euler_angles.mV[2] + 360.f, 360.f), 0.05f);
renderNameXYZ(euler_angles);
renderNameXYZ(active_rotation);
}
void FSManipRotateJoint::renderAxes(const LLVector3& agent_space_center, F32 size, const LLQuaternion& rotation)
@ -931,7 +924,7 @@ void FSManipRotateJoint::renderNameXYZ(const LLQuaternion& rot)
renderTextWithShadow(llformat("⟳: %.3f", mLastAngle * RAD_TO_DEG), window_center_x + 103.f, base_y, LLColor4(1.f, 0.65f, 0.f, 1.f));
base_y += 20.f;
renderTextWithShadow(llformat("Joint: %s", mJoint->getName().c_str()), window_center_x - 130.f, base_y, LLColor4(1.f, 0.1f, 1.f, 1.f));
renderTextWithShadow(llformat("Manip: %s%c", getManipPartString(mManipPart).c_str(), mUseEdgeMode?'*':' '), window_center_x + 30.f, base_y, LLColor4(1.f, 1.f, .1f, 1.f));
renderTextWithShadow(llformat("Manip: %s%c", getManipPartString(mManipPart).c_str(), mCamEdgeOn?'*':' '), window_center_x + 30.f, base_y, LLColor4(1.f, 1.f, .1f, 1.f));
if (mManipPart != LL_NO_PART)
{
LL_INFOS("FSManipRotateJoint") << "Joint: " << mJoint->getName()
@ -1054,9 +1047,11 @@ bool FSManipRotateJoint::handleMouseDownOnPart(S32 x, S32 y, MASK mask)
{
// Constrained rotation.
LLVector3 axis = setConstraintAxis(); // set the axis based on the manipulator part
mLastEuler = LLVector3::zero;
F32 axis_onto_cam = llabs(axis * mCenterToCamNorm);
const F32 AXIS_ONTO_CAM_TOL = cos(85.f * DEG_TO_RAD);
if (axis_onto_cam < AXIS_ONTO_CAM_TOL)
if (axis_onto_cam < AXIS_ONTO_CAM_TOLERANCE)
{
LLVector3 up_from_axis = mCenterToCamNorm % axis;
up_from_axis.normalize();
@ -1073,11 +1068,13 @@ bool FSManipRotateJoint::handleMouseDownOnPart(S32 x, S32 y, MASK mask)
}
LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, axis);
mMouseDown += mouse_depth * projected_center_to_cam;
mCamEdgeOn = true; // We are in edge mode, so we can use the mouse depth.
}
else
{
mMouseDown = findNearestPointOnRing(x, y, agent_space_center, axis) - agent_space_center;
mMouseDown.normalize();
mCamEdgeOn = false; // Not in edge mode, so we don't use the mouse depth.
}
mInitialIntersection = mMouseDown;
}
@ -1115,6 +1112,7 @@ bool FSManipRotateJoint::handleMouseUp(S32 x, S32 y, MASK mask)
setMouseCapture(false);
mManipPart = LL_NO_PART;
mLastAngle = 0.0f;
mCamEdgeOn = false;
return true;
}
else if(mHighlightedJoint)
@ -1374,13 +1372,43 @@ LLQuaternion FSManipRotateJoint::dragUnconstrained(S32 x, S32 y)
return sphere_rot * LLQuaternion(extra_angle, axis);
}
}
static LLQuaternion extractTwist(const LLQuaternion& rot, const LLVector3& axis)
{
// Copy and normalise the input (defensive)
LLQuaternion qnorm = rot;
qnorm.normalize();
// Extract vector part and scalar part
LLVector3 v(qnorm.mQ[VX], qnorm.mQ[VY], qnorm.mQ[VZ]);
F32 w = qnorm.mQ[VW];
// Project v onto the axis (removing any perpendicular component)
F32 dot = v * axis;
LLVector3 proj = axis * dot; // proj is now purely along 'axis'
// Build the “twist” quaternion from (proj, w), then renormalize
LLQuaternion twist(proj.mV[VX],
proj.mV[VY],
proj.mV[VZ],
w);
if (w < 0.f)
{
twist = -twist;
}
twist.normalize();
return twist;
}
LLQuaternion FSManipRotateJoint::dragConstrained(S32 x, S32 y)
{
// Get the constraint axis from our joint manipulator.
// (See the adjusted getConstraintAxis() below.)
LLVector3 constraint_axis = getConstraintAxis();
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
if (mCamEdgeOn)
{
LLQuaternion freeRot = dragUnconstrained(x, y);
return extractTwist(freeRot, constraint_axis);
}
// Project the current mouse position onto the plane defined by the constraint axis.
LLVector3 projected_mouse;
bool hit = getMousePointOnPlaneAgent(projected_mouse, x, y, agent_space_center, constraint_axis);

View File

@ -35,7 +35,7 @@ class LLJoint;
class LLVOAvatar; // or LLVOAvatarSelf, etc.
namespace {
const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 80.f * DEG_TO_RAD ); // cos() is not constexpr til c++26
const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 85.f * DEG_TO_RAD ); // cos() is not constexpr til c++26
constexpr F32 RADIUS_PIXELS = 100.f; // size in screen space
constexpr S32 CIRCLE_STEPS = 100;
constexpr F32 CIRCLE_STEP_SIZE = 2.0f * F_PI / CIRCLE_STEPS;
@ -108,6 +108,7 @@ protected:
LLQuaternion mSavedJointRot;
LLJoint * mHighlightedJoint = nullptr;
F32 mHighlightedPartDistance = 0.f;
LLVector3 mLastEuler = LLVector3::zero; // last euler angles in degrees
LLVector3 mInitialIntersection; // The initial point on the manipulators sphere (in agent space)
const std::vector<std::string_view> getSelectableJoints(){ return sSelectableJoints; };