How to stop model in viewportframe from being oriented incorrectly?

**What do you want to achieve?

I want my vehicle models in my viewportframes to be rotated and oriented correctly in a consistent position. I have a dedicated viewporthandler modulescript to create and cache viewports for the vehicle models.

2. What is the issue? Include screenshots / videos if possible!
image
One of my viewports for one of my vehicles is rotated incorrectly and facing the wrong way.

The issue is the body mesh of the transport jeep model. Without the mesh, its positioned normally, but with it, it’s messed up like in the image

3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I tried to update the script to rotate the model using the vehicle’s chassis, I tried turning the viewport camera (though I’m not sure if I did it right), I tried setting attributes on problematic vehicles that needs the model’s CFrame to be forcibly rotated. Nothing works and I dont know how to fix this.

Please help, thank you!

local VehicleViewportModule = {}

local RunService = game:GetService("RunService")

local CONFIG = {
    DEFAULT_ROTATION = 0,
    
    ZOOM_FACTORS = {
        DEFAULT = 0.9,
        CIVILIAN = 0.9,   
        ADVANCED = 0.85,  
        GAMEPASS = 0.95   
    }
}

local modelCache = {}
local rotationConnections = {}

-- List of core parts that shouldn't affect orientation
local CORE_PARTS = {
    "Chassis",
    "DriverSeat",
    "SteeringRack",
    "AntiRollBar",
    "DirectionPart"
}

local function cleanModel(model)
    -- Remove scripts
    for _, child in ipairs(model:GetDescendants()) do
        if child:IsA("Script") or child:IsA("LocalScript") or child:IsA("ModuleScript") then
            child:Destroy()
        end
    end
    
    -- Remove joints
    for _, child in ipairs(model:GetDescendants()) do
        if child:IsA("JointInstance") or child:IsA("Motor") or child:IsA("Motor6D") or 
           child:IsA("Weld") or child:IsA("WeldConstraint") or child:IsA("ManualWeld") then
            child:Destroy()
        end
    end
    
    -- Clean up non-core parts for special direction models
    if model:GetAttribute("SpecialDirection") then
        for _, child in ipairs(model:GetChildren()) do
            if child:IsA("BasePart") then
                local isCorePart = false
                for _, corePart in ipairs(CORE_PARTS) do
                    if child.Name == corePart then
                        isCorePart = true
                        break
                    end
                end
                if not isCorePart then
                    child:Destroy()
                end
            end
        end
    end
end

local function setModelDirection(model, originalModel)
    if originalModel:GetAttribute("SpecialDirection") then
        local directionPart = model:FindFirstChild("DirectionPart")
        if directionPart then
            model:PivotTo(CFrame.new() * directionPart.CFrame.Rotation)
            directionPart:Destroy()
        end
    else
        model:PivotTo(CFrame.new())
    end
end

function VehicleViewportModule.createPreview(viewportFrame, vehicleModel)
    local existingConnection = rotationConnections[viewportFrame]
    if existingConnection then
        existingConnection:Disconnect()
        rotationConnections[viewportFrame] = nil
    end

    local cacheKey = vehicleModel:GetFullName()
    
    if not modelCache[cacheKey] then
        local displayModel = Instance.new("Model")
        
        for _, child in pairs(vehicleModel:GetChildren()) do
            if not (child:IsA("Script") or child:IsA("LocalScript") or child:IsA("ModuleScript")) then
                local clone = child:Clone()
                clone.Parent = displayModel
            end
        end
        
        cleanModel(displayModel)
        setModelDirection(displayModel, vehicleModel)
        
        local vehicleType = vehicleModel:GetAttribute("VehicleType") or "DEFAULT"
        local zoomFactor = CONFIG.ZOOM_FACTORS[vehicleType] or CONFIG.ZOOM_FACTORS.DEFAULT
        
        local modelSize = displayModel:GetExtentsSize()
        local cameraOffset = modelSize.Magnitude * zoomFactor
        
        local cameraSettings = {
            CFrame = CFrame.new(
                Vector3.new(cameraOffset, cameraOffset / 2, cameraOffset),
                Vector3.new(0, 0, 0)
            )
        }
        
        modelCache[cacheKey] = {
            model = displayModel,
            cameraSettings = cameraSettings
        }
    end
    
    for _, child in pairs(viewportFrame:GetChildren()) do
        child:Destroy()
    end
    
    local cachedData = modelCache[cacheKey]
    local modelClone = cachedData.model:Clone()
    modelClone.Parent = viewportFrame
    
    local camera = Instance.new("Camera")
    camera.CFrame = cachedData.cameraSettings.CFrame
    camera.Parent = viewportFrame
    viewportFrame.CurrentCamera = camera
    
    local rotationAngle = 0
    
    rotationConnections[viewportFrame] = RunService.RenderStepped:Connect(function(deltaTime)
        if not modelClone or not modelClone.Parent then
            local connection = rotationConnections[viewportFrame]
            if connection then
                connection:Disconnect()
                rotationConnections[viewportFrame] = nil
            end
            return
        end
        
        rotationAngle = rotationAngle + deltaTime * 0.5
        pcall(function()
            modelClone:PivotTo(CFrame.new() * CFrame.Angles(0, rotationAngle, 0))
        end)
    end)
end

function VehicleViewportModule.preloadVehicles(vehiclesList, vehiclesFolder)
    for _, vehicleName in pairs(vehiclesList) do
        local vehicle = vehiclesFolder:FindFirstChild(vehicleName)
        if vehicle then
            local dummyViewport = Instance.new("ViewportFrame")
            VehicleViewportModule.createPreview(dummyViewport, vehicle)
            dummyViewport:Destroy()
        end
    end
end

function VehicleViewportModule.clearCache()
    for viewportFrame, connection in pairs(rotationConnections) do
        if connection then
            connection:Disconnect()
        end
    end
    
    table.clear(rotationConnections)
    table.clear(modelCache)
end

return VehicleViewportModule

Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.

1 Like

You might want to try to reset the pivot for the model.

image

1 Like

Good idea, I tried that and unfortunately nothing happened I think

1 Like

I check your script and I see you got a function setModelDirection()

I think the issue is that for the Humvee the orientation part is placed correctly, but the green jeep when placing the orientation part you have rotated it. Try copying the part from the Humvee and placing it carefully inside the transport jeep how it is in the Humvee.

I hope you understand.
As orientation part I meant the directionPart

1 Like