Is this the best way to scale a model?

By rounding my scale to the nearest 0.1 and caching the results, it has improved performance by +30%. Higher memory usage of course, but nothing crazy.

Server is generating about 13,000 items in 1.4 seconds consistently.

7 Likes

What’s your code so far? Are you CFraming before or after parenting?

2 Likes

Before. Each foliage prefab has a different ‘transformation’ function that applies the object to a given position (i.e. rocks and trees need to be placed/rotated differently). All transformations are done before parenting. Final parenting of the whole folder is last, and thus allows server to replicate all items to the client however it sees best.

3 Likes

Are you using http://wiki.roblox.com/index.php?title=API:Class/Model/SetPrimaryPartCFrame to move models around?

1 Like

Yes

1 Like

CFrame operations can be batched, which appears to speed things up around 2x for me. I’ve found that grouping operations into batches of ~1000 offers the greatest speedup on my machine. To multiply a tuple of Vector3s by another CFrame, just do cf:PointToWorldSpace(…).

Also, you can store the scale and the object space transformation as a single CFrame, since CFrames by themselves aren’t required to be orthogonal:

local mPts,mSizes
local cf = primaryCf:inverse()
local ScaleMatrix = CFrame.new(0,0,0,scale,0,0,0,scale,0,0,0,scale)
cf=cf*ScaleMatrix
mPts = {cf:PointToWorldSpace(unpack(mPts))}
mSizes = {ScaleMatrix:VectorToWorldSpace(unpack(mSizes))}

Where mPts and mSizes are defined appropriately.

Edit: On second thought, this might not meet your needs if you’re only transforming 2-3 parts per model. It’s worth a try, though.

14 Likes

I feel like GetChildren would be faster than GetDescendants, although if it is, probably not enough to impact performance.

Maybe only scale half of the models? If variation is what you want, do you really need to scale every single one of them?

Also, you could maybe make a variable for primaryCf:inverse() as well. Means it would be called/calculated 2 to 3 times less often. Optionally use pointToObjectSpace like suremark suggested (not sure which would be faster).

And finally, do you really need to return the model? I mean, if you’re passing it in the function, I imagine you wouldn’t need another reference to it.

1 Like

Interesting. This isn’t documented anywhere, but it works

1 Like

Mind blown. Thanks for teaching me a new cframe method :smile:

1 Like

It was on the old wiki :smiley: I discovered it last week and have been trying to think of some useful applications for it that aren’t already handled by the ROBLOX engine, but you’d be the first to give me such an idea.

By the way, it works for all of the ToWorldSpace and ToObjectSpace methods, i.e. those plus the ones for points and vectors.

3 Likes

How do you handle welds in your models? Don’t all the welds break when rescaling the parts?

1 Like

Yeah they would probably break, or otherwise just put things in the wrong position. You would have to scale the C0/C1 properties of the welds.

If you’re auto-welding, I would recommend scaling first before welding

3 Likes

All the parts from my model have scaled, but their positions appear to remain the same. As a result, there are gaps between all my parts (I am scaling down). I do have welds connecting some parts together since it is a moving object. Solutions would be appreciated.

1 Like

I had the same issue, but I figured it out.

local function scaleModel(model, scale)
	local PrimaryPart = model.PrimaryPart
	local PrimaryPartCFrame = model:GetPrimaryPartCFrame()
	
	--Destroy welds
	for _,object in pairs(model:GetDescendants()) do
		if object:IsA('BasePart') then
			for _,object in pairs(object:GetDescendants()) do
				if object:IsA('Weld') or object:IsA('ManualWeld') or object:IsA('WeldConstraint') then
					pcall(function()
						object.Part0.Anchored = true
						object.Part1.Anchored = true
					end)
					object:Destroy()
				end
			end
		end
	end
	
	--Scale BaseParts
	for _,object in pairs(model:GetDescendants()) do
		if object:IsA('BasePart') then	
			object.Size = object.Size*scale
			
			local distance = (object.Position - PrimaryPartCFrame.p)
			local rotation = (object.CFrame - object.Position)
			object.CFrame = (CFrame.new(PrimaryPartCFrame.p + distance*scale) * rotation)
		end
	end
end

https://imgur.com/a/8rf0gKW

26 Likes

Hello guys,

I asked @KoxuVBC in a private message for a solution where the parts of the model are not anchored. He then gave me this advise:

This is my implementation, which works. I want to share it with you. Maybe even someone has a suggestion for improvement.

local function scaleModel(model, scale)
local PrimaryPart = model.PrimaryPart
local PrimaryPartCFrame = model:GetPrimaryPartCFrame()

--Destroy welds
for _,object in pairs(model:GetDescendants()) do
	if object:IsA('Weld') or object:IsA('ManualWeld') or object:IsA('WeldConstraint') then
		object:Destroy()			
	end		
end
for _,object in pairs(model:GetDescendants()) do
	if object:IsA('BasePart') then
		for _,object2 in pairs(model:GetDescendants()) do
			if object2:IsA('BasePart') then
				if object ~= object2 then
					local constraint = Instance.new("WeldConstraint")
					constraint.Part0 = object
					constraint.Part1 = object2
					constraint.Parent = object
				end
			end		
		end			
	end		
end

--Scale BaseParts
for _,object in pairs(model:GetDescendants()) do
	if object:IsA('BasePart') then	
		object.Size = object.Size*scale
		
		local distance = (object.Position - PrimaryPartCFrame.p)
		local rotation = (object.CFrame - object.Position)
		object.CFrame = (CFrame.new(PrimaryPartCFrame.p + distance*scale) * rotation)
	end
end

end

Greetings!

8 Likes

I don’t think that works well with R16: image

However it works great with R6:

1 Like

For some reason, OP’s code doesn’t work for a model I was trying to scale. If you get weird scaling behavior in the future when trying to use the code, this code works for me:

function scaleModel(model, scale)
	local origin = model.PrimaryPart.Position
	
	for _, part in ipairs(model:GetDescendants()) do
		if part:IsA("BasePart") then
			local pos = part.Position
			local rotCf = part.CFrame - pos
			local fromOriginDir = pos - origin
			part.Size *= Vector3.new(scale, scale, scale)
			part.CFrame = rotCf + origin + fromOriginDir*scale
		end
	end
end
25 Likes

I made @rbxts package for scale-model because this could be a little tricky. It needs to scale bunch of different kinds of sub-objects. For example, my package can scale Fire, Attachments, and even a NumberSequence inside of a ParticleEmitter.

You can install it to roblox-ts solution with npm i @rbxts/scale-model
Sourcecode in Typescript could be found on my Github

I made a roblox-ts Playground which will show the sourcecode compiled to Lua, however it’s not very human-friendly.

3 Likes

There is a scale model plugin that I use. Model Resize Plugin 2.1 [ DRAG TO RESIZE! ] - Roblox

Cool, yep, I knew about that, but I need to resize Models at runtime with script.

3 Likes