Why does Normal rotate my models in weird ways?

I’m having a problem when the Normal returned causes an unwanted rotation on models. The reason I want to use the Normal vector is because I want the model to rotate along floor/walls/roof. So basically, the ‘Bottom’ surface of the model should ALWAYS be the side touching the surface (floor/walls/roof)

Worth noting, I actually did find a ‘fix’ for this, however, I then get other issues. The fix basically adds a CFrame.Angles to setting the CFrame, with -90 on the X and that causes the weird rotation to go away. But because the Y of the CFrame.Angles() is 0, it means the models rotation always gets reset back to 0.

Model:SetPrimaryPartCFrame(
		CFrame.new(NewPos, NewPos + NewRay.Normal) * -- Set position
		CFrame.Angles(-math.rad(90), 0, 0) * -- Fix the models rotation caused by NewRay.Normal
		CFrame.new(0, Model.PrimaryPart.Size.Y / 2, 0) -- Offset the model so its not halfway through the floor
	)
Mouse.Move:Connect(function()
	if not IsMoving then return end

	-- Check for seats
	for _, v in pairs(Model:GetDescendants()) do
		if v:IsA('Seat') then
			local Occupant = v.Occupant
			if Occupant then
				-- Make the player sitting down stand up
				Occupant.Sit = false
			end
		end
	end
	
	-- Do checks to make sure it's being placed within their base
	local FloorCheck = workspace:Raycast(Character.HumanoidRootPart.Position, Vector3.new(0, -50, 0))
	
	-- Not players floor/returned nil for some reason
	if not FloorCheck.Instance then return end
	
	-- Set models parts to not collideable
	for i, v in pairs(Model:GetDescendants()) do
		if v:IsA('BasePart') or v:IsA('UnionOperation') or v:IsA('MeshPart') then
			v.CanCollide = false
		end
	end
	
	local RaycastFilter = RaycastParams.new()
	RaycastFilter.FilterType = Enum.RaycastFilterType.Blacklist
	RaycastFilter.FilterDescendantsInstances = {Model, Player.Character, workspace.Plots.Interiors[Player.Name].Build.Door}
	
	local UnitRay = Camera:ScreenPointToRay(Mouse.X, Mouse.Y, 1)
	local NewRay = workspace:Raycast(UnitRay.Origin, UnitRay.Direction * 1000, RaycastFilter)
	
	if not NewRay.Instance then return end
	
	local PosX = math.floor((NewRay.Position.X / 0.5) + 0.5) * 0.5
	local PosY = math.floor((NewRay.Position.Y / 0.5) + 0.5) * 0.5
	local PosZ = math.floor((NewRay.Position.Z / 0.5) + 0.5) * 0.5
	
	local NewPos = Vector3.new(PosX, PosY, PosZ)

	Model:SetPrimaryPartCFrame(
		CFrame.new(NewPos, NewPos + NewRay.Normal) * -- Set position
		CFrame.new(0, Model.PrimaryPart.Size.Y / 2, 0) -- Offset the model so its not halfway through the floor
	)
end)

I believe you are making the CFrame look up away from the floor it’s being placed on, I suggest using CFrame.fromMatrix instead where you can manually set the UpVector and right vector of the model to what you need.

How would I incorporate that into my current code tho?

Instead of this which sets position and orientation such that the model CFrame is at NewPos and is looking at a point above NewPos (if normal is the floor with vector (0,1,0))

CFrame.new(NewPos, NewPos + NewRay.Normal) * -- Set position

Do this which sets the CFrame such that the new vector of the model has to be the floor Normal and the right vector is an arbitrary one that we obtain through the cross product. If you want to change it further you would need to find a way to add rotation or find a new right vector.

local arbritaryLookVector = Vector3.new(0, 0, -1)
-- Remember the right-hand rule
local rightVector = NewRay.Normal:Cross(arbritaryLookVector)

Model:SetPrimaryPartCFrame(
CFrame.fromMatrix(NewPos, rightVector,NewRay.Normal)
)

That creates all new problems :confused:


It doesn’t work along the right/left walls

Video is not working but it’s probably due to this:

This offset only moves the furniture “up” along the y axis, you would need to find a new way to offset a part relative to where it’s being placed wether on the floor or on the wall. Perhaps you will need to use:

Model.PrimaryPart.Size.X/2

in order to move it right from the way.

I suggest reading up on the CFrame articles and experimenting on your own in order to gain more tools to work with CFrames on.

Also, I advise using an external video hosting site like imgur to embed videos onto the devforumns and a screen recording software like ShareX(Windows) or Kap (Mac).

This is what’s happening, and the only piece of code is

Model:SetPrimaryPartCFrame(
		CFrame.fromMatrix(NewPos, rightVector,NewRay.Normal)
	)

Have you tried debugging the vectors that are being obtained in order to get the RightVector and the UpVector of the model?

I advise debugging your furniture system by creating a part that represents the UpVector, RightVector, and LookVector.

This is what it should look like although it’s quite odd that the blue vector is facing towards the part in the image just image it’s reversed and it represents the floor normal as the new relative Y Axis.

Once you do, you will be able to easily visualize what is wrong with your CFrame methods and answer most CFrame problems you come across:

  1. Would I need to rotate the furniture around the Y axis 90 degrees?

  2. Is the position of the CFrame incorrect? Do I need to add an offset relative to the floor normal?

  3. What exactly does this piece of code do to give me the RightVector? Is the right vector giving incorrect? Do I need to rotate it somehow to make it correct? Why did I choose (0, 0, -1) as vector.

local arbritaryLookVector = Vector3.new(0, 0, -1)
-- Remember the right-hand rule
local rightVector = NewRay.Normal:Cross(arbritaryLookVector)

Once you are able to visualize the CFrame you will be able to do some really cool stuff like this using new CFrame methods like CFrame.fromAxis()

Also, look at this tutorial furniture placement it’s where I got the image from and is basically what I’m trying to do with the example code I gave of finding a CFrame on the surface using the cross product properties.

That furniture placement system doesn’t function in the same way I want my furniture movement to work tho.