How do I rotate a primary part's CFrame

Could anyone tell me what’s happening here?

So basically, I’m trying to make a system where I can rotate a “preview” of a part

Now, I want there to be support for rotating models but issue is, I can’t seem to find a way to rotate the primary part

If I just rotate it using orientation, for some reason every part EXCEPT for the primary part rotates?

I figured it was because the weld was breaking, so I tried rotating with CFrame, but all the posts I found gave extremely weird glitches were some but not all of the parts were rotating and of course, the primary part still didn’t budge.
I tried using a for loop in order to rotate every part individually but that didn’t work the way I wanted it to, and of course the primarypart still isn’t rotating.

function rotateT(obj)
	if obj then
		if obj:IsA("Model") then
			if not obj.PrimaryPart then
				return
			end
			obj.PrimaryPart.CFrame = obj.PrimaryPart.CFrame * CFrame.Angles(math.rad(0),math.rad(0),math.rad(90))
		elseif obj:IsA("BasePart") then
			obj.Orientation += Vector3.new(0,0,90)
		end
	end
end

This is just a small section of the code, so if you want the rest of it ask me.

Also, it uses RunService’s BindToRenderStep() so maybe there’s something to do with that?

Video about issue with rotating the primary part using CFrame or Vector3

If you are trying to rotate the whole model I think you’ll want to use PivotTo

 obj:PivotTo(obj.PrimaryPart.CFrame * CFrame.Angles(math.rad(0),math.rad(0),math.rad(90)))
1 Like

For some reason, it doesn’t move at all, is there something wrong with the primary part?
I had loads of errors about the primary part not existing which is why I added the if statement to check if it was there.

Oh, sorry didn’t see you were still referencing primaryPart there to.
Just use the Model’s pivot as the base to add the rotation onto as well.

obj:PivotTo(obj:GetPivot() * CFrame.Angles(math.rad(0),math.rad(0),math.rad(90))

I have no clue what’s going on because that’s not working either? There’s something seriously wrong with my primary part or something

Have you tried adding some “print” statements through your code to check how far it’s getting.
And for Models it really shouldn’t matter if it has a PrimaryPart or not. Since you should be using :PivotTo on the Model itself.

function rotateT(obj)
	if obj then
		if obj:IsA("Model") then
            print("is a model")
			obj:PivotTo(obj:GetPivot() * CFrame.Angles(math.rad(0),math.rad(0),math.rad(90)))
		elseif obj:IsA("BasePart") then
			obj.Orientation += Vector3.new(0,0,90)
		end
	end
end

As a quick test I threw this together and the part is rotating.

local tm = workspace.TestModel
while true do
	task.wait(1)
	print(tm:GetPivot())
	tm:PivotTo(tm:GetPivot() *  CFrame.Angles(math.rad(0),math.rad(0),math.rad(10)))
end

Yes, I removed them to make it more readable for this post.

The reason why I’m so concerned about the primary part is because it’s the most important part for the model, what I mean is that it’s a “hitbox” which is very important for the model to do what its supposed to do once its placed.

Also, I need to choose a part to run these lines of codes on clone:PivotTo(CFrame.new(result.Position + result.Normal * clone.PrimaryPart.Size.Y/2))
Choosing it randomly wouldn’t be the greatest.
I tried without the primary part and even without it, the function failed.
What the hell is going on with my code

Are you able to post the model file of this object?

Yeah, it’s normal and probably good for your models to still have a primaryPart defined. The main thing is you should be PivotTo to make sure the whole model, not just the primary part gets moved.
And as for your other snippet, depending on what your model looks like it could also be independent of the primaryPart by going off of the model’s size instead of the primaryPart’s size

clone:PivotTo(CFrame.new(result.Position + result.Normal * clone:GetExtentsSize()/2))
1 Like

model.rbxm (4.4 KB)

1 Like

Yeah I think changing all references of obj.PrimaryPart.CFrame to either obj:GetPivot() or obj:SetPivot() as @CookieAroundTheBend suggested should do the trick. Since you are working with these methods you won’t need to worry about the madness of welding, so you can just delete those and set all of your parts to be anchored. That should save you a headache as well. :slight_smile:

I removed primary parts, welds and replaced all references with what you told me to do and I’m still stumped.
Here’s my whole script I don’t know what’s going on

Script
-- services
local runservice = game:GetService("RunService")
local repstore = game:GetService("ReplicatedStorage")
local players = game:GetService("Players")
local uis = game:GetService("UserInputService")
local tweenservice = game:GetService("TweenService")
local cas = game:GetService("ContextActionService")

-- stuff to do with the player
local char = script.Parent
local plr = players:GetPlayerFromCharacter(char)
local mouse = plr:GetMouse()
local camera = workspace.CurrentCamera
local gui = plr.PlayerGui.Cancel

local returned = false

-- stuff in replicatedstorage
local parts = repstore.Previews
local remoteevent = repstore.Place
local fail = repstore.Failed

local mobile = plr.PlayerGui.Bool

-- deb
local deb = false

local deb2 = false

function rotateR(obj)
	if obj then
		if obj:IsA("Model") then
			obj:PivotTo(obj:GetPivot() * CFrame.Angles(math.rad(0),math.rad(15),math.rad(0)))
		elseif obj:IsA("BasePart") then
			obj.Orientation += Vector3.new(0,15,0)
		end
	end
end

function rotateT(obj)
	if obj then
		if obj:IsA("Model") then
			print("aed")
			obj:PivotTo(obj:GetPivot() * CFrame.Angles(math.rad(0),math.rad(0),math.rad(90)))
		elseif obj:IsA("BasePart") then
			obj.Orientation += Vector3.new(0,0,90)
		end
	end
end


repstore.Other.OnClientEvent:Connect(function(part)
	if plr.Character.Stacked.Value > 1 then
		return
	end
	local part =  parts:FindFirstChild(part)
	if not part then
		warn("Client returned nil")
		return
	end

	gui.Enabled = true
	
	local function preview()
		local clone = part:Clone()
		clone.Parent = workspace.Previews
		return clone
	end

	local clone = preview()

	local param = RaycastParams.new()
	param:AddToFilter(script.Parent)

	local function render()
		
		local location = uis:GetMouseLocation()
		local direction = camera:ViewportPointToRay(location.X, location.Y)
		local result = workspace:Raycast(direction.Origin, direction.Direction * 1000, param)
		if plr.Items:FindFirstChild(clone.Name) then
			if plr.Items:FindFirstChild(clone.Name).Value <=0 then
				clone:Destroy()
			end
		end
		if result and clone then
			if clone:IsA("Model") then
				clone:PivotTo(CFrame.new(result.Position + result.Normal * clone:GetExtentsSize()/2))
			elseif clone:IsA("BasePart") then
				clone.Position = result.Position + result.Normal * clone.Size.Y/2
			end
		end
	end
	
	cas:BindActionAtPriority("RotateSideways", function(actionName, inputState, inputObject)
		if inputState == Enum.UserInputState.Begin then
			rotateR(clone) end end, true, 2)
	cas:BindActionAtPriority("RotateFowards", function(actionName, inputState, inputObject)
		 if inputState == Enum.UserInputState.Begin then
				rotateT(clone) end end, true, 2)
	cas:SetTitle("RotateSideways", "Rotate")
	cas:SetDescription("RotateSideways", "Af")
	cas:SetTitle("RotateFowards", "Rotate Up")
	cas:SetPosition("RotateSideways", UDim2.new(0.3,0,0.56,0))
	cas:SetPosition("RotateFowards", UDim2.new(0.05,0,0.56,0))
	runservice:BindToRenderStep("Preview", Enum.RenderPriority.Camera.Value, render)
	uis.InputBegan:Connect(function(input, grp)
		if grp then return end
		if  input.UserInputType == Enum.UserInputType.MouseButton1 then
			if clone and not deb then
				gui.Enabled = false
				if clone:IsA("BasePart") then
					remoteevent:FireServer(clone.CFrame, clone.Name)
				elseif clone:IsA("Model") then
					remoteevent:FireServer(clone:GetPivot(), clone.Name)
				end
			end
		elseif input.UserInputType == Enum.UserInputType.Touch then

			mobile.Value = true
		elseif input.KeyCode == Enum.KeyCode.R then
			rotateR(clone)
			print("errr")
		elseif input.KeyCode == Enum.KeyCode.T then
			rotateT(clone)
		end
	end)
	
	mobile.Changed:Connect(function()
		if mobile.Value == false then
			cas:UnbindAction("RotateSideways")
			cas:UnbindAction("RotateFowards")
		end
	end)
	
	uis.InputEnded:Connect(function(input, grp)
		if not grp and input.UserInputType == Enum.UserInputType.Touch then
			if mobile.Value == true then
				return
			end
			mobile.Value = false
			gui.Enabled = false
			print("err")
			cas:UnbindAction("RotateSideways")
			cas:UnbindAction("RotateFowards")
			remoteevent:FireServer(clone:GetPivot(), clone.Name)
			print(clone.CFrame)
		end
	end)

end)

I’m close to just using a union instead of a model this is so painful to work with

Ah okay so the problem here is that while you are updating the rotation, it’s only there for a frame until it is undone by the call to PivotTo in the next frame:

clone:PivotTo(
     CFrame.new(result.Position + result.Normal * clone:GetExtentsSize()/2)
)

This constructor contains no information on orientation, meaning the model will revert to its “0, 0, 0” orientation. Maybe you could create a variable to keep track of the rotation that the user has applied? Then, instead of making calls to rotateR and rotateT, you would update this rotation variable. To increment the rotation, you could do something like:

rotation *= CFrame.fromEulerAnglesXYZ(0, math.rad(15), 0)

Then your render function would incorporate this orientation into the CFrame calculation:

local offset = CFrame.new(result.Position + result.Normal * clone:GetExtentsSize()/2)

clone:PivotTo(offset * rotation)

Does that make sense?

2 Likes

This weird glitch keeps happening

Can you post your new code? I can’t replicate this

-- services
local runservice = game:GetService("RunService")
local repstore = game:GetService("ReplicatedStorage")
local players = game:GetService("Players")
local uis = game:GetService("UserInputService")
local tweenservice = game:GetService("TweenService")
local cas = game:GetService("ContextActionService")

-- stuff to do with the player
local char = script.Parent
local plr = players:GetPlayerFromCharacter(char)
local mouse = plr:GetMouse()
local camera = workspace.CurrentCamera
local gui = plr.PlayerGui.Cancel

local returned = false

local rotation = CFrame.new()

-- stuff in replicatedstorage
local parts = repstore.Previews
local remoteevent = repstore.Place
local fail = repstore.Failed

local mobile = plr.PlayerGui.Bool

-- deb
local deb = false

local deb2 = false

function rotateR(obj)
	if obj then
			rotation *= CFrame.fromEulerAnglesXYZ(0, math.rad(15), 0)
	end
end

function rotateT(obj)
	if obj then
			print("aed")
		rotation *= CFrame.fromEulerAnglesXYZ(0, 0, math.random(90))
	end
end


repstore.Other.OnClientEvent:Connect(function(part)
	if plr.Character.Stacked.Value > 1 then
		return
	end
	local part =  parts:FindFirstChild(part)
	if not part then
		warn("Client returned nil")
		return
	end
	gui.Enabled = true
	
	local function preview()
		local clone = part:Clone()
		clone.Parent = workspace.Previews
		return clone
	end

	local clone = preview()

	local param = RaycastParams.new()
	param:AddToFilter(script.Parent)
			
	local function render()
		
		local location = uis:GetMouseLocation()
		local direction = camera:ViewportPointToRay(location.X, location.Y)
		local result = workspace:Raycast(direction.Origin, direction.Direction * 1000, param)
		if plr.Items:FindFirstChild(clone.Name) then
			if plr.Items:FindFirstChild(clone.Name).Value <=0 then
				clone:Destroy()
			end
		end
		if result and clone then
			if clone:IsA("Model") then		
				local offset = CFrame.new(result.Position + result.Normal * clone:GetExtentsSize()/2)
				clone:PivotTo(offset * rotation)
			elseif clone:IsA("BasePart") then
				local offset = CFrame.new(result.Position + result.Normal * clone.Size.Y/2)
				clone:PivotTo(offset * rotation)
			end
		end
	end
	
	cas:BindActionAtPriority("RotateSideways", function(actionName, inputState, inputObject)
		if inputState == Enum.UserInputState.Begin then
			rotateR(clone) end end, true, 2)
	cas:BindActionAtPriority("RotateFowards", function(actionName, inputState, inputObject)
		 if inputState == Enum.UserInputState.Begin then
				rotateT(clone) end end, true, 2)
	cas:SetTitle("RotateSideways", "Rotate")
	cas:SetDescription("RotateSideways", "Af")
	cas:SetTitle("RotateFowards", "Rotate Up")
	cas:SetPosition("RotateSideways", UDim2.new(0.3,0,0.56,0))
	cas:SetPosition("RotateFowards", UDim2.new(0.05,0,0.56,0))
	runservice:BindToRenderStep("Preview", Enum.RenderPriority.Camera.Value, render)
	uis.InputBegan:Connect(function(input, grp)
		if grp then return end
		if  input.UserInputType == Enum.UserInputType.MouseButton1 then
			if clone and not deb then
				gui.Enabled = false
				if clone:IsA("BasePart") then
					remoteevent:FireServer(clone.CFrame, clone.Name)
				elseif clone:IsA("Model") then
					remoteevent:FireServer(clone:GetPivot(), clone.Name)
				end
			end
		elseif input.UserInputType == Enum.UserInputType.Touch then

			mobile.Value = true
		elseif input.KeyCode == Enum.KeyCode.R then
			rotateR(clone)
			print("errr")
		elseif input.KeyCode == Enum.KeyCode.T then
			rotateT(clone)
		end
	end)
	
	mobile.Changed:Connect(function()
		if mobile.Value == false then
			cas:UnbindAction("RotateSideways")
			cas:UnbindAction("RotateFowards")
		end
	end)
	
	uis.InputEnded:Connect(function(input, grp)
		if not grp and input.UserInputType == Enum.UserInputType.Touch then
			if mobile.Value == true then
				return
			end
			mobile.Value = false
			gui.Enabled = false
			print("err")
			cas:UnbindAction("RotateSideways")
			cas:UnbindAction("RotateFowards")
			remoteevent:FireServer(clone:GetPivot(), clone.Name)
			print(clone.CFrame)
		end
	end)

end)
````Preformatted text`

Maybe you could change the PrimaryPart to nil, rotate the part, then change it back.

It’s already been changed to nil

Try CFrame.fromAxisAngle to apply pure rotation and not positioning. Here:

CFrame | Documentation - Roblox Creator Hub