Can anyone tell me how to change the UpVector of a CFrame without changing the relative Y axis rotation?
Can you elaborate on your question? It’s not making sense right now.
I’m not really sure how to ask it any differently.
Consider a block that spins like a top left/right. The upvector points to the sky. Now change the upvector to point toward world forward. But not change the degree it was already rotated relative.
Hard to articulate this.
You have a start and target upvector:
local start = part.CFrame.UpVector
local target = Vector3.new(1, 1, 1).Unit
You can do this in a single rotation about an axis. The axis is perpendicular to both:
local axis = start:Cross(target)
And figure out the angle between the two
local angle = math.acos(start:Dot(target))
Then just rotate the CFrame by that amount
part.CFrame = CFrame.fromAxisAngle(axis, angle) * part.CFrame.Rotation + part.CFrame.Position
I’ll do some testing to check, but I’m pretty sure that would work. Ignoring cases like full 180 degree rotations or 0 degree rotations, for which you’ll have to write special cases.
Edit: left out one part, gimme a sec Fixed. Final function:
local function AlignUpVector(cframe: CFrame, up: Vector3)
local start = cframe.UpVector
local angle = math.acos(start:Dot(up))
local axis = nil
if angle < 0.00001 or angle > math.pi - 0.00001 then
axis = Vector3.xAxis
else
axis = start:Cross(up)
end
return CFrame.fromAxisAngle(axis, angle) * cframe.Rotation + cframe.Position
end
Example use:
local target = Vector3.new(1, 1, 1).Unit
part.CFrame = AlignUpVector(part.CFrame, target)
Edit: Modified function to take into account special cases
It might help too if I explain what I’m trying to accomplish.
Basically, I’m trying to detect how much the block spins on the relative Yaxis. I can do this accurately while the upvector is world up. However the upvector is constantly changing. So I was thinking I could translate to the world up vector then grab the current relative Yaxis orientation to see what the change in orientation was.
Ty I think this may work. I’m going to do some testing and verify I can accomplish my goal with it. Looks promising.
Yeah, I think you’d be able to align the previous CFrame’s upvector with the current CFrame’s upVector and then compare the angle between their right vectors?
local function GetSpin(prev: CFrame, current: CFrame)
local aligned = AlignUpVector(prev, current.UpVector)
return math.acos(aligned.RightVector:Dot(current.RightVector))
end
Tyvm and kudos your trig game is def better than mine.
I should be able to accomplish the end goal with this.
Fixing it:
local function AlignUpVector(cframe: CFrame, up: Vector3)
local start = cframe.UpVector
local angle = math.acos(start:Dot(up))
local axis = nil
if angle < 0.00001 or angle > math.pi - 0.00001 or angle ~= angle then
axis = Vector3.xAxis
else
axis = start:Cross(up)
end
return CFrame.fromAxisAngle(axis, angle) * cframe.Rotation + cframe.Position
end
added the angle~= angle cause it sometimes returns nan which glitches it.
Angle should never be NaN
You may be giving the function a non-Unit vector, which can cause NaN
You probably want to call .Unit on your Vector before passing it to this function
Otherwise the angle will be wrong or NaN