Viewport Model Rotator

Viewport Model Rotator

Recently I have been wanting to allow the player to rotate models inside a viewport frame as I am making a crafting system for my game and I want the player to be able to preview the thing they want to craft.

Anyways I used some code that someone else had made and I made a module script out of it. I am making the module public as other users may want to allow players to rotate models inside viewport frames as well.

Setup

Get the module script here and add it to game.ReplicatedStorage. In a script you require the script:

local viewportModelRotator = require(game.ReplicatedStorage.Module) -- replace "Module" with the name of the Viewport Model Rotator script

API

-- requires the module (duh)
local viewportModelRotator = require(game.ReplicatedStorage.Module) -- replace "Module" with the name of the Viewport Model Rotator script

-- enables viewport model rotation on a viewport frame
local rotationViewport = viewportModelRotator:Enable(path.to.viewport.frame.goes.here)

-- disables viewport model rotation on a viewport frame
rotationViewport:Disable()

Miscellaneous Stuff

To rotate a model inside a viewport frame you hold left click and drag your mouse (assuming viewport model rotation is enabled on the viewport frame).

If you change the model inside the viewport frame then you have to readable the module on the viewport frame.

It currently only works with models - the model has to have PrimaryPart set otherwise it won’t work.

Features im planning on adding so you guys don’t waste your time suggesting it in the replies

  • mobile and xbox support
  • configuration options - speed of rotation, axis that the model can rotate, button that triggers the rotation (xbox, mobile and pc)
  • support for singular parts to be rotated
25 Likes

Two pieces of advice.

  1. You should never rotate the model itself in a ViewportFrame. See Notes / Performance on ViewportFrame GUI.

  2. SetPrimaryPartCFrame and GetPrimaryPartCFrame are being deprecated in favour of the new pivot APIs. PivotTo and GetPivot are the new functions to use for model space manipulation.

Hope they’ll be of use for future updates to your module.

5 Likes

I didn’t realise SetPrimaryPartCFrame and GetPrimaryPartCFrame are becoming deprecated.

2 Likes

how to use this? i dont know about script much

Hey! This module seems really awesome, but I’m having some issues with it:

  1. This is my viewport frame model before rotating it:

And this is the model as soon as I rotate it (the affected part is the PrimaryPart):

  1. It only can be rotated in the Y axis.

Thank you for reading!

Hello, great module but anyways, how to create this for vertical axis?

I have improved this script, now all parts move smoothly as they should. You can also move the model vertically, but it won’t work perfectly. I hope I was able to help someone.

My version module script

local UserInputService = game:GetService("UserInputService")
local ButtonsHeld = {} -- Tracks buttons being held. Used to know when dragging
local LastMousePos = nil  -- Used to calculate how far mouse has moved
local mouse = game.Players.LocalPlayer:GetMouse()

local viewportRotation = {}
viewportRotation.__index = viewportRotation

function viewportRotation:Enable(viewportFrame,sensitivity)
	
	local viewportModel = viewportFrame:FindFirstChildWhichIsA("Model")
	
	self.viewport = {
		inputChanged = nil;
		inputBegan = nil;
		inputEnded = nil;
		
		touchStarted = nil;
		touchEnded = nil;
	}
	setmetatable(self.viewport, viewportRotation)
	
	self.viewport.inputChanged = UserInputService.InputChanged:Connect(function(input, gameProcessedEvent)
		if input.UserInputType == Enum.UserInputType.MouseMovement then -- runs every time mouse is moved
			if ButtonsHeld["MouseButton1"] then -- makes sure player is holding down right click

				local CurrentMousePos = Vector2.new(mouse.X,mouse.Y)
				local change = (CurrentMousePos - LastMousePos)/sensitivity -- calculates distance mouse traveled (/5 to lower sensitivity)

				-- The angles part is weird here because of how the cube happens to be oriented. The angles may differ for other sections
				viewportModel:PivotTo(
					viewportModel:GetPivot() * CFrame.Angles(math.rad(change.Y*-1),math.rad(change.X),0)

				LastMousePos = CurrentMousePos
				-- This line makes everything possible. The PrimaryPart's orientation DOES NOT move the rest of the model with it. 
				viewportModel:GetPivot()
			end
		elseif input.UserInputType == Enum.UserInputType.Touch then
			if ButtonsHeld["Touch"] then
				local CurrentMousePos = Vector2.new(mouse.X,mouse.Y)
				local change = (CurrentMousePos - LastMousePos)/sensitivity -- calculates distance mouse traveled (/5 to lower sensitivity)

				-- The angles part is weird here because of how the cube happens to be oriented. The angles may differ for other sections
				viewportModel:PivotTo(
					viewportModel:GetPivot() * CFrame.Angles(math.rad(change.Y*-1),math.rad(change.X),0)
				)

				LastMousePos = CurrentMousePos
				-- This line makes everything possible. The PrimaryPart's orientation DOES NOT move the rest of the model with it. 
					viewportModel:GetPivot()
			end
		end
	end)
	
	self.viewport.inputBegan = UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then -- player starts dragging
			LastMousePos = Vector2.new(mouse.X, mouse.Y)
			ButtonsHeld["MouseButton1"] = true
		end
	end)
	
	self.viewport.inputEnded = UserInputService.InputEnded:Connect(function(input, gameProcessedEvent)
		if input.UserInputType == Enum.UserInputType.MouseButton1 then -- player stops dragging
			ButtonsHeld["MouseButton1"] = nil
			LastMousePos = nil
		end
	end)
	
	self.viewport.touchStarted = UserInputService.TouchStarted:Connect(function(input, gameProcessedEvent)
		if input.UserInputType == Enum.UserInputType.Touch then -- player starts dragging
			LastMousePos = Vector2.new(mouse.X, mouse.Y)
			ButtonsHeld["Touch"] = true
		end
	end)
	
	self.viewport.touchEnded = UserInputService.TouchEnded:Connect(function(input, gameProcessedEvent)
		if input.UserInputType == Enum.UserInputType.Touch then -- player starts dragging
			ButtonsHeld["Touch"] = nil
			LastMousePos = nil
		end
	end)
	
	return self.viewport
end

function viewportRotation:Disable(viewportFrame)
	if self.viewport then
		self.viewport.inputChanged:Disconnect()
		self.viewport.inputBegan:Disconnect()
		self.viewport.inputEnded:Disconnect()
	
		self.viewport.touchStarted:Disconnect()
		self.viewport.touchEnded:Disconnect()
	end
end

return viewportRotation

Local script

-- requires the module (duh)
local viewportModelRotator = require(game.ReplicatedStorage.ViewportRotation) -- replace "Module" with the name of the Viewport Model Rotator script

-- enables viewport model rotation on a viewport frame
viewportModelRotator:Enable(script.Parent.ViewportFrame,1.5) -- path VieportFrame, value to lower sensitivity

-- disables viewport model rotation on a viewport frame
viewportModelRotator:Disable(script.Parent.ViewportFrame,1.5) - -- path VieportFrame, value to lower sensitivity

Edited:
I also fixed the API, now the turning is quietly turned off as it should be.
Edited 2:
Also been added mobile support but I don’t think that it’s will work very well.
Edited 3:
I fixed error when input disable

6 Likes

Thanks! Really will help with a project im working on. Appreciate it!

Also if for some reason you didn’t want to move on the Y axis, simply make line 32 and 45

viewportModel:GetPivot() * CFrame.Angles(0,math.rad(change.X),0))
1 Like