Object Position Offset based on Normal

I’m trying to make an object placement system that allows for objects to be placed on any surface (floor, wall, ceiling) This is done by finding the surface normal and adding an offset based on object size.

The problem is that I’m always adding the offset to the Y position.

The object’s placement is correct when it’s on the floor because its adding the Y size. If it were on the ceiling, it’d have to subtract the Y size. If it were on a wall it’d have to either add/subtract the X or Y size. checking for the normals (if normal == X then) seems like a lazy and long workaround, so I’d like to know what would be a more mathematical solution?


Here’s how I’m getting the position.

function Placement:CalculatePlace(position, target, normal)
	if position then
		local yOffset = (self.Object:GetModelSize() / 2).Y
		--                          yOffset is only added and only to position.Y
		local offsetPos = Vector3.new(position.X, position.Y + yOffset, position.Z)
		return CFrame.new(offsetPos, offsetPos + normal) * CFrame.Angles(math.rad(-90), 0, 0)
	end
end
2 Likes
local UP = Vector3.new(0,1,0)
function Placement:CalculatePlace(position, target, normal)
	if position then
            local theta = math.acos(UP:Dot(normal)) -- get the angle for rotation
            local axis = UP:Cross(normal).unit
            return CFrame.new(position + normal + self.Object:GetModelSize().Y / 2) * CFrame.fromAxisAngle(axis, theta) 
	end
end

Perhaps something like this would work. You could just get the angle needed to rotate, and the axis to rotate it around for the rotation. And use the normal plus and position and half the size of the object on the y for the position of it.

1 Like

You should use CFrame.LookVector

function Placement:CalculatePlace(position, target, normal)
	if position then
		local yOffset = (self.Object:GetModelSize() / 2).Y
		--                          yOffset is only added and only to position.Y
		local cfr = CFrame.new(position.X,position.Y,position.Z)
		local offsetPos = cfr + cfr.LookVector*yOffset
		return CFrame.new(offsetPos, offsetPos + normal) * CFrame.Angles(math.rad(-90), 0, 0)
	end
end

or CFrame.UpVector , CFrame.RightVector

1 Like

Thank you for the replies.

They didn’t seem to solve my problem. However, thanks for showing me methods to tackle the problem. I’ll look further into the CFrame API.

Also, this is a reference for what the code should do (this “solution” solves it)
but I don’t like it

		local yOffset = (self.Object:GetModelSize() / 2).Y
		local offsetPos
		
		if normal == Vector3.new(0, 1, 0) then
			offsetPos = Vector3.new(position.X, position.Y + yOffset, position.Z)
			
		elseif normal == Vector3.new(0, -1, 0) then
			offsetPos = Vector3.new(position.X, position.Y - yOffset, position.Z)
			
		elseif normal == Vector3.new(1, 0, 0) then
			offsetPos = Vector3.new(position.X + yOffset, position.Y, position.Z)
			
		elseif normal == Vector3.new(-1, 0, 0) then
			offsetPos = Vector3.new(position.X - yOffset, position.Y, position.Z)
			
		elseif normal == Vector3.new(0, 0, 1) then
			offsetPos = Vector3.new(position.X, position.Y, position.Z + yOffset)
			
		elseif normal == Vector3.new(0, 0, -1) then
			offsetPos = Vector3.new(position.X, position.Y, position.Z - yOffset)
		end
		
		return CFrame.new(offsetPos, offsetPos + normal) * CFrame.Angles(math.rad(-90), 0, 0)