Rescale model and maintain all position and size aspects

I’m wanting to resize a model based on a scale and have all its objects maintain their correct positions. However, a part in the door isn’t being positioned correctly when resized.

local function ResizeModel(model, sizeOffset)
	local PrimaryPosition = model.PrimaryPart.Position
	
	for _, part in pairs(model:GetDescendants()) do
		if part:IsA("BasePart") then
			part.Position = PrimaryPosition:Lerp(part.Position, sizeOffset)
			part.Size *= sizeOffset
		end
	end
end

Resized model
image

Full size model
image

When you resize a BasePart instance its center point is changed and thus its position property is changed as well. In addition to this, collisions are accounted for and avoided by shifting the resized BasePart to prevent overlapping.

Record the part’s previous position, set its size then set its position (using the part’s previously stored position).

I recently had the same needs and after searching for a while I found this really cool module

By default, sayhisam1’s module multiplies the size of the model so working with large numbers is harder, I made my own version which has limited support for rigs but provides different scaling functions.

--[[

Resize models keeping their relative positions

--]]

type ArrayOfParts = {[number]: BasePart} -- Parts inside this array are excluded from size or position changes

local Resize = {}

function Resize.SetSize(Model: Model, TargetSize: Vector3, DontAffectSize: ArrayOfParts?, DontAffectPosition: ArrayOfParts?)
    local PrimaryPart: BasePart? = Model.PrimaryPart

    if PrimaryPart then
        local PrimaryPartCFrame: CFrame = PrimaryPart.CFrame

        local SizeMultiplier: number = TargetSize / PrimaryPart.Size

        for _, Part: BasePart in pairs(Model:GetDescendants()) do
            if Part:IsA("BasePart") then

                if DontAffectSize == nil or not table.find(DontAffectSize, Part) then
                    Part.Size *= SizeMultiplier
                end

                if Part ~= PrimaryPart and (DontAffectPosition == nil or not table.find(DontAffectPosition, Part)) then
                    local Distance: Vector3 = Part.Position - PrimaryPartCFrame.Position
                    local Rotation: CFrame = Part.CFrame - Part.Position

                    Part.CFrame = (CFrame.new(PrimaryPartCFrame.Position + Distance * SizeMultiplier) * Rotation)
                end
            end
        end
    else
        error("Model needs a primary part to be resized.")
    end
end

function Resize.IncrementSize(Model: Model, Size: Vector3, DontAffectSize: ArrayOfParts?, DontAffectPosition: ArrayOfParts?)
    local PrimaryPart: BasePart? = Model.PrimaryPart

    if PrimaryPart then
        Resize.SetSize(Model, PrimaryPart.Size + Size, DontAffectSize, DontAffectPosition)
    else
        error("Model needs a primary part to be resized.")
    end
end

function Resize.DecreaseSize(Model: Model, Size: Vector3, DontAffectSize: ArrayOfParts?, DontAffectPosition: ArrayOfParts?)
    local PrimaryPart: BasePart? = Model.PrimaryPart

    if PrimaryPart then
        Resize.SetSize(Model, PrimaryPart.Size - Size, DontAffectSize, DontAffectPosition)
    else
        error("Model needs a primary part to be resized.")
    end
end

function Resize.MultiplySize(Model: Model, Size: Vector3, DontAffectSize: ArrayOfParts?, DontAffectPosition: ArrayOfParts?)
    local PrimaryPart: BasePart? = Model.PrimaryPart

    if PrimaryPart then
        Resize.SetSize(Model, PrimaryPart.Size * Size, DontAffectSize, DontAffectPosition)
    else
        error("Model needs a primary part to be resized.")
    end
end

function Resize.DivideSize(Model: Model, Size: Vector3, DontAffectSize: ArrayOfParts?, DontAffectPosition: ArrayOfParts?)
    local PrimaryPart: BasePart? = Model.PrimaryPart

    if PrimaryPart then
        Resize.SetSize(Model, PrimaryPart.Size / Size, DontAffectSize, DontAffectPosition)
    else
        error("Model needs a primary part to be resized.")
    end
end

return Resize
2 Likes

This still has issues.

Works for full blocks
image

But slabs get completel squashed to paper thin
image

and the glass in the door is still positioned correctly
image

I used DecreaseSize and 2,2,2 for the vector3

Hmm, didn’t know about those issues. I’ll check the code and notice you when I find what’s causing trouble.

Well, I ran some tests and it works fine for me. I saw what you mean by slabs getting squashed and that happens because since you’re using a regular scale (2, 2, 2) to decrease the size of a half-sized part, the proportion messes up. To keep it in sync with the proportion you’ll have to decrease the size by (2, 1, 2) or some other scale that suits better for the slab.

The other thing that I saw is that my module uses the PrimaryPart’s size to determinate which will be the target size, which gives better results for large / irregular models but may not be that good for little or regular things like a slab or door. I modified the code so you can switch, with a boolean value, between using the PrimaryPart’s size to calculate the target size or using the Model’s ExtentsSize.

--[[

Resize models keeping their relative positions

--]]

type ArrayOfParts = {[number]: BasePart} -- Parts inside this array are excluded from size or position changes

local Resize = {}

function Resize.SetSize(Model: Model, TargetSize: Vector3, DontAffectSize: ArrayOfParts?, DontAffectPosition: ArrayOfParts?, UseExtentsSize: boolean?)
    local PrimaryPart: BasePart? = Model.PrimaryPart

    if PrimaryPart then
        local PrimaryPartCFrame: CFrame = PrimaryPart.CFrame

        local SizeMultiplier: number

        if UseExtentsSize then
            SizeMultiplier = TargetSize / Model:GetExtentsSize()
        else
            SizeMultiplier = TargetSize / PrimaryPart.Size
        end

        for _, Part: BasePart in pairs(Model:GetDescendants()) do
            if Part:IsA("BasePart") then

                if DontAffectSize == nil or not table.find(DontAffectSize, Part) then
                    Part.Size *= SizeMultiplier
                end

                if Part ~= PrimaryPart and (DontAffectPosition == nil or not table.find(DontAffectPosition, Part)) then
                    local Distance: Vector3 = Part.Position - PrimaryPartCFrame.Position
                    local Rotation: CFrame = Part.CFrame - Part.Position

                    Part.CFrame = (CFrame.new(PrimaryPartCFrame.Position + Distance * SizeMultiplier) * Rotation)
                end
            end
        end
    else
        error("Model needs a primary part to be resized.")
    end
end

local function ModifySize(Model: Model, Size: Vector3, Operation: string, UseExtentsSize: boolean?): Vector3
    local PrimaryPart: BasePart? = Model.PrimaryPart

    if PrimaryPart then
        local TargetSize: Vector3

        if Operation == "+" then
            TargetSize = (if UseExtentsSize then Model:GetExtentsSize() else PrimaryPart.Size) + Size
        elseif Operation == "-" then
            TargetSize = (if UseExtentsSize then Model:GetExtentsSize() else PrimaryPart.Size) - Size
        elseif Operation == "*" then
            TargetSize = (if UseExtentsSize then Model:GetExtentsSize() else PrimaryPart.Size) * Size
        elseif Operation == "/" then
            TargetSize = (if UseExtentsSize then Model:GetExtentsSize() else PrimaryPart.Size) / Size
        end

        return TargetSize
    else
        error("Model needs a primary part to be resized.")
    end
end

function Resize.IncreaseSize(Model: Model, Size: Vector3, DontAffectSize: ArrayOfParts?, DontAffectPosition: ArrayOfParts?, UseExtentsSize: boolean?)
    Resize.SetSize(Model, ModifySize(Model, Size, "+", UseExtentsSize), DontAffectSize, DontAffectPosition, UseExtentsSize)
end

function Resize.DecreaseSize(Model: Model, Size: Vector3, DontAffectSize: ArrayOfParts?, DontAffectPosition: ArrayOfParts?, UseExtentsSize: boolean?)
    Resize.SetSize(Model, ModifySize(Model, Size, "-", UseExtentsSize), DontAffectSize, DontAffectPosition, UseExtentsSize)
end

function Resize.MultiplySize(Model: Model, Size: Vector3, DontAffectSize: ArrayOfParts?, DontAffectPosition: ArrayOfParts?, UseExtentsSize: boolean?)
    Resize.SetSize(Model, ModifySize(Model, Size, "*", UseExtentsSize), DontAffectSize, DontAffectPosition, UseExtentsSize)
end

function Resize.DivideSize(Model: Model, Size: Vector3, DontAffectSize: ArrayOfParts?, DontAffectPosition: ArrayOfParts?, UseExtentsSize: boolean?)
    Resize.SetSize(Model, ModifySize(Model, Size, "/", UseExtentsSize), DontAffectSize, DontAffectPosition, UseExtentsSize)
end

return Resize

About the door, I’m not really sure of why that happens. It may be something related to cframes or a bad part shape, which would be strange considering that the module separates rotation from position. It could also be that the glass panel is the primary part, in which case its position is not updated while the door’s position is updated or vice versa.

Hmnm I still can’t get the door glass to work at all :confused:

The root part is a 3x3x3 invisible part, to make placement in the grid system simple. The door is 0.5x6x3. The transparent part is also welded to the door. Unsure if that can cause issues
image