Rotate an Object from Point A to B

Imagine you are turning a globe, but it’s a special globe that doesn’t have any rigid axis so it’s able to rotate on all 3 as if it were floating. When I highlight a specific point on a sphere (determined by a raytrace impact) I want to be able to drag that point to any other location on the sphere and have the “globe” spin to accommodate. Much like I was taking my finger in real life and moving a globe from one position to the other.

Here is what I have so far. I’m using the LookAtPosition which works fine from some angles, but if I try to grab a point on the side of the sphere it doesn’t drag up and down like it does from the front or back. I know why and it’s because the LookAtPosition doesn’t do anything with the Roll (X) axis…it always returns 0.

This is in VR so I can approach the sphere with a motion controller from any angle or direction but I want the functionality to be the same regardless of the initial point to be dragged. Think more in terms of a floating ball that can be spun front, left, right whatever…as opposed to a globe with a bar going down the center so it can only spin around the Yaw.

//The is the sphere to spin. It's made up of a bunch of separate smaller actors so this is how I have to get it.
 APlanet* ParentPlanet = Cast<APlanet>(SelectedTile->GetAttachParentActor());

//Get the new rotation
FRotator TickRotation = UKismetMathLibrary::FindLookAtRotation(ParentPlanet->GetActorLocation(), ControllerLocation);

//This happens on init only when the controller button is pressed to initiate the spin
if (PlanetRotationOffset == FRotator::ZeroRotator)
{
    PlanetRotationOffset = TickRotation; //Set the offset
    PlanetRotationAmount = ParentPlanet->GetActorRotation(); //Store the current world rotation
}

//Spin the planet
FRotator TargetRotation = TickRotation - PlanetRotationOffset;
ParentPlanet->SetActorRotation(UKismetMathLibrary::ComposeRotators(TargetRotation, PlanetRotationAmount));

Should I be using a rotation matrix instead? And if so, how?


RESOLVED

Based on the accepted answer I was able to get this working. Here is the code I used. A little context first though. I initiate the rotation on the sphere by clicking a button on a motion controller (could easily be a mouse or a different type of controller though). That initial press sets a variable that the button is active and also sets the GripInitV to a ZeroVector. That way that variable is fresh each time we click and hold the button. The rest is taken care of in tick right now but I plan to move that to a timer or something. Tick does a raytrace from the controller to the sphere I want to rotate and that is all that happens before this code runs.

//This is the actor I am rotating
APlanet* ParentPlanet = Cast<APlanet>(SelectedTile->GetAttachParentActor());

//Initialize vars
FVector ImpactV = HitData.ImpactPoint;
FVector PlanetCenterV = ParentPlanet->GetActorLocation();
if (RightGripStickyLocation == FVector::ZeroVector)
{
//This only gets set the first tick after the button is pressed
RightGripStickyLocation = ImpactV; //The start location
PlanetRotationAmount = ParentPlanet->GetActorRotation(); //The existing rotation
}

//Create the actual drag start and end vectors
FVector DragStart = RightGripStickyLocation - PlanetCenterV;
FVector DragEnd = ImpactV - PlanetCenterV;

//Build the rotation quaternion
FVector RotationV = FVector::CrossProduct(DragStart, DragEnd).UpVector;
FQuat RotationQuat = FQuat::FindBetweenVectors(DragStart, DragEnd);
float RotationAngle = FVector::DotProduct(DragStart, DragEnd) * DeltaTime;
RotationQuat.ToAxisAndAngle(RotationV, RotationAngle); //Set the axis and angle

//Rotate the sphere in world space
FTransform TargetTransform = ParentPlanet->GetTransform();
TargetTransform.SetRotation(RotationQuat);
TargetTransform.ConcatenateRotation(PlanetRotationAmount.Quaternion());
TargetTransform.NormalizeRotation();
ParentPlanet->SetActorTransform(TargetTransform);

you should use quanternions to avoid gimbal lock.

you will probably need to store the initial hit location and the current hit location on the sphere, then subtract those from the center of the sphere to get vectors pointing at the center. then use cross product on those vectors to find a perpendicular vector, and use that as XYZ of a quanternion, and W would be based on the dot product of those hit vectors multiplied by 180, causing a rotation around that axis we found from the cross product… or something like that. good luck.

Awesome…i’ve come across everything you’ve mentioned in my research but honestly i’m not sure how to put it all together (never used quaternions as i’m quite new to all this). Thanks for laying that out, it should help me get going in the right direction. If you’ve got a second and could slap just some basic c++ down to help me with a basic outline that would be most appreciated.

sorry, i don’t have a working example. i have never made anything like this before, and i’ve never used quanternions either. i was figuring it out and typing it out as i was also in the middle of talking to someone IRL about something completely unrelated, solving 2 complicated problems at once to challenge myself. so i have no idea how i came up with that, and ill be really surprised if it works. but it sounds legit, because cross product should give a perpendicular axis and i think quanternions rotate around a vector, using W as the angle, but im not sure, its all just flashbacks from random vector math books i have read. so good luck.

Oh ok. Understandable. I’ll work through it and see what happens. Thanks again.

This worked almost perfectly. Just a couple tweaks to get the initial rotation added to the new rotation so the sphere didn’t pop into view when I first selected it. I posted my working code in the original post.