How to make something like move tool and rotate tool, but without Handles, and instead with HandleAdornment? [HARD]

Hello guys! I’m making my plugin which should help people work with terrain faster.
But I stuck with some problems, one of them is Cone/CylinderHandleAdornments, which I want to use for dragger and rotate tools.
You will ask why I don’t want use built-in Handles and ArcHandles? I don’t want use them because I don’t love their style at all.
So I made my own “Handles” for rotation and move:
image
image
But I got one big problem when I tried to script their functionality. HandleAdornments have less. For example, they haven’t MouseDrag, which gives:
face: NormalId; distance: number
For Handles, and
axis: Axis; relativeAngle: number; deltaRadius: number
For ArcHandles

So, when I tried to make this function myself, I got stuck with another problem: IDK how to properly calculate everything:


First thing that IDK how to calculate distance for move tool. On FIRST picture everything seems very easy:

  1. Cast ray on that plane.
  2. Then got distance from center to that dot.
  3. Tada you got result.

I did that. And found problem: What if camera don’t see this plane? For example, if camera looks up. I tried: and got inversion. So if mouse looking right, script means that I’m moving left. (like roblox does xd). But this way CANNOT be implemented for Y-axis

Another way I can say is try to find intersection with mouse ray and (for this example) X-axis.

While this idea I liked more, I got another problem: What if this 2 vectors just won’t cross at all?


(yellow dot is cursor (IK that it’s bad)

I believe that this way is better than previous, but I need a bit of help with it: how I can adjust calculations to hit X-axis?

This’s part of code for detecting intersection with plane:

local MouseDirection = Mouse.UnitRay.Direction

local PlaneCFrame = CFrame.new() --Center of plane should be here.
local PlaneDirection = (PlaneCFrame * CFrame.Angles(math.pi / 2, 0, 0)).LookVector --Top direction of plane.
local PlaneCameraRelative = PlaneCFrame:Inverse() * workspace.CurrentCamera.CFrame 
local PlaneCameraLenght = -PlaneCameraRelative.Position.Y --How high camera is from plane.
local MouseOnPlaneDirection = CFrame.fromOrientation(PlaneCFrame:ToOrientation()):Inverse() * CFrame.new(MouseDirection.X, MouseDirection.Y, MouseDirection.Z)

local MouseHypothenuseLenght = PlaneCameraLenght / MouseOnPlaneDirection.Position.Y
--MouseHypothenuseLenght is How much we need multiply MouseDirection to hit plane

Can someone here help me make this code work for only 1 axis, but not plane?


(Here you can see white “Planet” - center of plane, and white grid with blue part - where mouse hits plane.

I believe the Transform plugin is the closest mover you can look into. I don’t know how it’s made but can’t you take a look in the plugin’s code?

Sorry, but can you give me link to that plugin? IDK about which one you talking about.

The Transform plugin is found in the Model tab, next to Home (in Studio)

Looks like studio just won’t do anything if camera don’t see handles.

So I think studio have things set like this:
image
And if mouse don’t hit any of this planes (They are infinite I think but for this example they have borders) Studio won’t do anything.
So to move things by axis X, it should hit yellow or pink plane, for Z - cyan or pink, and for Y - yellow or cyan. I’ll try make smth like this.

I tried implement this, and got 50% of good results:

local Axis = string.sub(Object.Name, 1, 1)
local success = false
local Hypothenuse = 0
local MouseDirection = Mouse.UnitRay.Direction --Направление гипотенузы треугольника, надо найти длину
local ManualCFrame = CFrame.new(PlaneManualParams[1]) * CFrame.Angles(math.rad(PlaneManualParams[2].X), math.rad(PlaneManualParams[2].Y), math.rad(PlaneManualParams[2].Z))

if Axis == "X" or Axis == "Z" then
	--trying to hit one out of 2 planes
	--plane XZ
	local ManualCFrame = CFrame.new(PlaneManualParams[1]) * CFrame.Angles(math.rad(PlaneManualParams[2].X), math.rad(PlaneManualParams[2].Y), math.rad(PlaneManualParams[2].Z))
	local XZPlaneDirection = (ManualCFrame * CFrame.Angles(math.pi / 2, 0, 0)).LookVector --Смотрит ВВЕРХ
	local XZPlaneCameraRelative = ManualCFrame:Inverse() * workspace.CurrentCamera.CFrame
	local XZPlaneCameraLenght = -XZPlaneCameraRelative.Position.Y
	local XZMouseOnPlaneDirection = CFrame.fromOrientation(ManualCFrame:ToOrientation()):Inverse() * CFrame.new(MouseDirection.X, MouseDirection.Y, MouseDirection.Z)

	local XZMouseHypothenuseLenght = XZPlaneCameraLenght / XZMouseOnPlaneDirection.Position.Y
	if XZMouseHypothenuseLenght >= 0 then
		success = true
	end
	Hypothenuse = XZMouseHypothenuseLenght
end
if Axis == "Z" or Axis == "Y" and success == false then
	--trying to hit one out of 2 planes
	--plane ZY
	local ManualCFrame = CFrame.new(PlaneManualParams[1]) * CFrame.Angles(math.rad(PlaneManualParams[2].X), math.rad(PlaneManualParams[2].Y), math.rad(PlaneManualParams[2].Z))
	local ZYPlaneDirection = (ManualCFrame * CFrame.Angles(0, 0, math.pi / 2)).LookVector --Смотрит ВВЕРХ
	local ZYPlaneCameraRelative = ManualCFrame:Inverse() * workspace.CurrentCamera.CFrame
	local ZYPlaneCameraLenght = -ZYPlaneCameraRelative.Position.Y
	local ZYMouseOnPlaneDirection = CFrame.fromOrientation(ManualCFrame:ToOrientation()):Inverse() * CFrame.new(MouseDirection.X, MouseDirection.Y, MouseDirection.Z)

	local ZYMouseHypothenuseLenght = ZYPlaneCameraLenght / ZYMouseOnPlaneDirection.Position.Y
	if ZYMouseHypothenuseLenght >= 0 then
		success = true
	end
	Hypothenuse = ZYMouseHypothenuseLenght
end
if Axis == "Y" or Axis == "X" and success == false then
	--trying to hit one out of 2 planes
	--plane YX
	local YXPlaneDirection = (ManualCFrame * CFrame.Angles(math.pi / 2, 0, 0)).LookVector --Смотрит ВВЕРХ
	local YXPlaneCameraRelative = ManualCFrame:Inverse() * workspace.CurrentCamera.CFrame
	local YXPlaneCameraLenght = -YXPlaneCameraRelative.Position.Y
	local YXMouseOnPlaneDirection = CFrame.fromOrientation(ManualCFrame:ToOrientation()):Inverse() * CFrame.new(MouseDirection.X, MouseDirection.Y, MouseDirection.Z)

	local YXMouseHypothenuseLenght = YXPlaneCameraLenght / YXMouseOnPlaneDirection.Position.Y
	if YXMouseHypothenuseLenght >= 0 then
		success = true
	end
	Hypothenuse = YXMouseHypothenuseLenght
end

--BrushPart.CFrame = CFrame.new((workspace.CurrentCamera.CFrame.Position + (MouseDirection * Hypothenuse)).X, (workspace.CurrentCamera.CFrame.Position + (MouseDirection * Hypothenuse)).Y, (workspace.CurrentCamera.CFrame.Position + (MouseDirection * Hypothenuse)).Z) * CFrame.fromOrientation(math.rad(DefaultBrushRotation.X), math.rad(DefaultBrushRotation.Y), math.rad(DefaultBrushRotation.Z)) * CFrame.new(0, ToolSettings[Tool]["Brush"]["Size"][2].Y * (ToolSettings[Tool]["Brush"]["Pivot"][2] - 50) / 100, 0)
if success == true then
	local MouseHit = MouseDirection * Hypothenuse
	local MouseAlongAxis = MouseHit[Axis]
	local CameraAlongAxis = workspace.CurrentCamera.CFrame.Position[Axis]
	--local DistanceAlongAxis = CameraAlongAxis + MouseAlongAxis
	--local DistanceAlongAxis = -PlaneManualParams[1][Axis] + MouseAlongAxis
	MoveHandlesDrag(Axis, MouseAlongAxis)
else
	warn("Not success")
end

X and Z axis work almsot perfect, but only on XZ plane, and axis Y just don’t get affected at all.


Y-distance.