# Rotation relative to surface normal

no problem, could you tell me when you get home?

okay, now the farthest point i got to is this

basically i figured out how to rotate around that weird axis (but i don’t know where to rotate it to to make it finally relative! )

``````
local normalCFrame = CFrame.lookAt(hitPosition, hitPosition + normal)

local _rotation: CFrame = rotation
if relativeRotation then
local rotationRelativeToTarget: CFrame = CFrame.fromAxisAngle(normal, math.rad(abc)) * normalCFrame
_rotation = (rotationRelativeToTarget - rotationRelativeToTarget.Position)
end
``````
``````local rotation = CFrame.Angles(math.atan2(-normal.Y, normal.X), math.asin(normal.Z), 0)
local translation = CFrame.new(position) -- position is the part's position you want to make it relative to

part.CFrame = translation * rotation

``````

So I ain’t even that good at trigo but I think it should work.

Basically it’s 12:30am here so I can’t open my pc.

1 Like

It didn’t work
But I think i may have found something hold on

1 Like

Isn’t this basically what you asked for? This seems pretty much like what you see in the video. (excluding grid snapping)

I don’t think so because each time you want to place a part on the surface exactly aligned with the part you’re placing it on you have to get real finnicky and exactly get the rotation right

And I think that for my game that’s going to be a big no-no

I think this post has the info. But I think this post has some cases which is only applicable to character like RightVector of the HumanoidRootPart is always perpendicular to SurfaceNormal.

1 Like

Hey, this is almost working! Like i explained earlier, but now on two sides it doesn’t work because then the normal is parallel to the rightvector of the part. Have a look at the code. And the rotation was relative!

``````	if relativeRotation then
-- makes it so the RightVector and UpVector run parellel to the surface and LookVector = surface normal
--local EXTRASPIN = CFrame.fromEulerAnglesXYZ(math.pi/2, 0, 0)
--local EXTRASPIN = CFrame.fromEulerAnglesXYZ(math.pi/2, 0, 0)
--_rotation = --getRotationBetween(Vector3.new(0, 1, 0), normal	, Vector3.new(0,0, 1)) * EXTRASPIN
_rotation = CFrame.fromMatrix(
Vector3.new(), -- POSITION OF THE OBJECT
-normal:Cross(target.CFrame.RightVector), -- LOOKVECTOR
target.CFrame.RightVector, --RIGHTVECTOR
normal --UPVECTOR
)
end
``````
1 Like

So you mean this code almost works but for some sides it’s parallel right?

I think there’s a lot of methods you can do this, so I’m just trying to understand the specific scenario. The method I personally use for getting an object to be aligned upward given the vector normal is:

``````local result : RaycastResult
local normal : Vector3
local relative_vector = Vector3.yAxis -- This makes it so it's similar to a user's camera

local right_vector = normal:Cross(relative_vector)
local look_vector = right_vector:Cross(normal)

local object_size : Vector3

local cframe = CFrame.fromMatrix(
result.Position + (normal * object_size.Y * 0.5),
right_vector,
normal,
-look_vector
)
``````

This is what it looks like: https://gyazo.com/8408de856cb7f2e838ce7fcafa38eedc

2 Likes

What does local look_vector = right_vector:Cross(look_vector) mean? Where is the first look_vector coming from?

My bad, I meant to put normal there.

okay, so this is the problem i’m having.
when on a part with a rotation on one axis, all is well. But if on two or more axes rotated it doesn’t work anymore

i haven’t tested this yet for your code. hold on

I don’t think my code will help, this is more-so what I was trying to clarify when I was asking the question.

Plus I think the primary caveat to trying to do what you are trying is when you add a mesh or abnormal surface like a sphere (not cylinder) it’ll be very weird. My initial thought to completing this is to compare the orientation of the object which the raycast result returned and see the difference in the yAxis so then you can orientate the object by that amount; I haven’t done this myself yet so I don’t know how it’ll look.

That’s no problem. I’m going to have things in place for that. I don’t know how to do you suggestion

That’s alright, I haven’t done it before so I’m running through some tests before I let you know precisely how it could be done (not saying it will be pretty or good).

1 Like

Alright, so this is a bit wonky and it could definitely been done better but hopefully this satisfies what you’ll use it for.

totally not copy pasting some of code I sent here

``````local result : RaycastResult
local normal : Vector3
local relative_vector = Vector3.yAxis -- This makes it so it's similar to a user's camera

-- // As far as I know, this might not look pretty with mesh parts
-- // Also checking because I think result.Instance could be terrain
-- // I do not apologize for my use of ternaries
if (result.Instance and result.Instance:IsA("BasePart")) then
local result_cframe = result.Instance.CFrame
relative_vector = result_cframe.UpVector ~= result.Normal and result_cframe.UpVector or result_cframe.RightVector
end

local right_vector = normal:Cross(relative_vector)
local look_vector = right_vector:Cross(normal)

local object_size : Vector3

local cframe = CFrame.fromMatrix(
result.Position + (normal * object_size.Y * 0.5),
right_vector,
normal,
-look_vector
)
``````

This should be the result of that: https://gyazo.com/f11d43db1e5d5fae51d6f219e32291b4.mp4

2 Likes

Where does this variable come from

Oh my, when I wrote this I accidentally deleted the if statement and the local variable to turn it into a ternary; It’s `local result_cframe = result.Instance.CFrame`

Oh my god. You actually f#@^\$ng did it
Okay, it works, but could you please tell me what
result_cframe.UpVector ~= result.Normal and result_cframe.UpVector or result_cframe.RightVector
means? I have never seen a CFrame operated on by boolean operations. how does that work?