I am trying to remake SDS’ trowel mechanic for my own game that allows to pre-visualise what your building would where your mouse aims.

(hope the video works)
The first footage is what SDS does, and the second is what I have done so far.
I have no idea however of what I should do next. Where do I start so the block can be properly oriented to be fully parallel to the part/wedge its touching?

You could try using CFrame.fromAxisAngle(). It gives a cframe that is rotated around a vector. In this situation, that vector should be perpendicular to both the default upvector and the surface normal. Cross product can be used for that. The amount of rotation (angle) should be the angle between the default upvector and the surface normal. Because they are both unit vectors, their dot product is the cosine of the angle between them.

local upVec = Vector3.new(0, 1, 0)
local function getCf(surfacePos, normal, partSize)
local axis = normal:Cross(upVec)
local angle = math.acos(normal:dot(upVec))
local pos = surfacePos + normal * (partSize.Y * .5)
return CFrame.fromAxisAngle(axis, angle) + pos
end

It’s a vector perpendicular to the surface. There’s a 90 degree angle between the surface and the vector. You can get the surface normal with a raycast.

ok so I did the thing but it only works on wedges (and it’s inverted but that’s probably because i screwed up somewhere) and when touching normal parallellepipedic parts the block gets sent to the stratosphere.
My code is as follows:

local detection=workspace:Raycast(script.Parent.TargetBlock.Position,script.Parent.TargetBlock.Position-mouse.Target.Position*-100, raycastParams)
print(detection)
if detection then
script.Parent:FindFirstChild("TargetBlock").CFrame=getCf(game:GetService("Players").LocalPlayer:GetMouse().Hit.p,detection.Normal,Vector3.new(4,4,4))
end

Also, after targeting a part, wedges don’t work anymore too, but that’s not surprising given that my ray is 100 studs long.

If the part rotates in the opposite direction than it should, then I had put the normal and upvector in incorrect order in the cross product, and they need to be swapped.

The way you get the direction and origin for the raycast also seems incorrect. Try using the function updatePartCf when you want to position the part (probably every frame).

local UserInputService = game:GetService("UserInputService")
local RAYCAST_DIST = 100
local camera = workspace.CurrentCamera
local part = script.Parent:FindFirstChild("TargetBlock")
local upVec = Vector3.new(0, 1, 0)
local function getMouseRayResult()
local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.BlackList
rayParams.FilterDescendantsInstances = {part}
local mousePos = UserInputService:GetMouseLocation()
local unitRay = camera:ViewportPointToRay(mousePos.X, mousePos.Z)
local rayResult = workspace:Raycast(unitRay.Origin, unitRay.Direction * RAYCAST_DIST, rayParams)
return rayResult
end
local function getCf(surfacePos, normal, partSize)
local pos = surfacePos + normal * (partSize.Y * .5)
local dot = upVec:Dot(normal)
if 1 - dot < 1e-5 then
return CFrame.new(pos)
elseif 1 + dot < 1e-5 then
return CFrame.fromMatrix(pos, Vector3.new(1, 0, 0), Vector3.new(0, -1, 0), Vector3.new(0, 0, -1))
end
local axis = upVec:Cross(normal)
local angle = math.acos(dot)
return CFrame.fromAxisAngle(axis, angle) + pos
end
local function updatePartCf()
local mouseRayResult = getMouseRayResult()
-- make sure that the ray actually hit something
if mouseRayResult then
part.CFrame = getCf(mouseRayResult.Position, mouseRayResult.Normal, part.Size)
end
end

Ok, it works all good now except for perfectly flat surfaces (roofs included) and the part gets teleported to coordinates 0, -a lot, 0 when that happens