I am trying to scale a mesh with bones.
If I use a scale of (.7,.7,.7) I would get bones scaled to (.7,.7,7) and that is correct
but if I wanted to do (.7,.5,.7) I get bones scaled to (.7,.7,.7) which is incorrect. It doesnt seem to respect all axis while scaling, and I don’t know enough about the code or how it works to determine how to get the desired results.

I appreciate any help, thanks.

function ScaleMesh(scale,root)
for _,part in pairs(root:GetDescendants()) do
if part:IsA("Bone") then
local bone = part
-- move bone local translation to part space, scale, then move back to bone parent space
local parentCFrame = bone.Parent.CFrame
local boneParentToPartCFrame = root.CFrame:Inverse() * parentCFrame
boneParentToPartCFrame = boneParentToPartCFrame - boneParentToPartCFrame.Position --rotation only
local pivotOffsetInPartSpace = (boneParentToPartCFrame * bone.Position) * scale
bone.Position = boneParentToPartCFrame:inverse() * pivotOffsetInPartSpace
elseif part:IsA("Attachment") then
part.Position = part.Position * scale
end
end
root.Size = root.Size * scale
end

Sorry to be a noob, but where would I apply them in the original code? I don’t quite understand the workings of the function, its not my original work.

I don’t really get why the function is getting the cframe to each bone’s parent. I would think it logical to just scale the mesh first, then get the bone’s crfame in relation to the mesh, then scale the bone, then put it back to its original scope. Maybe I just don’t quite understand how the cframes of bone hierarchies work. Since they are structured in a tree, does each bone cframe live in the scope of its parent bone?

Funny you ask that, I am trying that right now, typed up a recursive function, that goes through the children in order of their parenting and does just this, I will update if that works.

I have this in a server script, as a child of the model holding the meshpart

function ScaleMesh(mesh,scale)
local function DoScale(part,scale)
if part:IsA("MeshPart") then
part.Size = part.Size*scale
elseif part:IsA("Bone") then
part.Position = part.Position * scale
end
for _,i in pairs(part:GetChildren()) do
DoScale(i,scale)
end
end
DoScale(mesh,scale)
end
ScaleMesh(script.Parent,Vector3.new(1,.5,1))

function ScaleMesh(mesh,scale)
mesh.Size = mesh.Size * scale
for _,child in pairs(mesh:GetDescendants()) do
if child:IsA("Bone") then
child.Position = child.Position * scale
end
end
end

Even with the less messy code, it just doesn’t seem to scale correctly.
If I use the scale tool in studio, it scales the mesh and the bones perfectly.
How does the scale tool do this? Is there any code for it?

Here is an image of my results.
On the left is the original
Middle is what result I am getting
Right is scaling with the scale tool in studio, so its what it should look like

Just as I was about to fall asleep, I realized, when scaling the bones need to change ‘look direction’ based on the new dimensions of the mesh, when scaling in a non uniform way.

I will mess with some code today, unless someone else knows this already, but just for simplicity, I was thinking of this in bed in a 2d perspective.
If a mesh, is 1,2 and it contains a bone that is looking 1,1 if we ‘squash’ the mesh to be 1,1, we need to find the ‘ratio’? of the new mesh compared to the old one so … r=(1/1,1/2) then we multiply this by the bone’s look direction, so look = 1r.X, 1r.Y … which would be 1*(1/1), 1*(1/2) which gives us a bone lookvector of 1,.5, or 2,1. From what I can imagine in my head, this would be a bone who’s look vector is basically squished, appropriately with the mesh scale. If anyone wants to provide any math on this, or other input I would appreciate it.

So apparently for this to work, the bone’s cframe needs to be translated to the mesh’s space, so that scaling the cframe’s lookvector by x or y or z, will be in line with the mesh, and not whatever local rotation the bone is in.

So, unless I am mistaken, each bone’s position is in reference to its parent position, such as the first bone, bone1, is for example… 0,1,0 (relative to the mesh) then bone2 might be 0,.1,0 (relative to bone1) and the next 0,.1,0 (relative to bone2)

So to correctly put these in the mesh’s space and rotate them correctly, I am thinking each cframe needs to be translated to world coordinates, then translated to part (the mesh) coordinates
Then I need to create the new cframe based Cframe.LookAt, using the cframes position scaled, and lookdireciton multiplied by the ratio of the mesh’s scale. Then put back into world space, then back into the bone’s parent’s space?

I was wrong in thinking the cframe’s rotation has anything to do with scaling the bones. Apparently the cframe does not look towards the parent. And when scaling the cframe rotations do not ever change. So it’s mainly a matter of scaling position in relation to the mesh’s object space, and applying those positions in order.
However, I have to give God the credit on this one. I thought I had it all worked out in my head, but then when I went to type it up, I typed up the tree traversal backwards, which worked. So had God not had me type it totally backwards, I would still be trying other things.

Here is the code, I cheat a little with .WorldCFrame, and .WorldPosition, since bones are derived from Attachments.

function ScaleMesh(mesh,scale)
local nodeTree = {}
local function BuildTree(tree,mesh,inst,scale)
if inst:IsA("Bone") or inst:IsA("Attachment") then
local cf = mesh.CFrame:ToObjectSpace(inst.WorldCFrame)
cf = mesh.CFrame:ToWorldSpace((cf - cf.Position) + (cf.Position * scale))
table.insert(tree,{Pos = cf.Position,Inst = inst})
end
for _,i in pairs(inst:GetChildren()) do
BuildTree(tree,mesh,i,scale)
end
end
BuildTree(nodeTree,mesh,mesh,scale)
for n = 1,#nodeTree do
nodeTree[n].Inst.WorldPosition = nodeTree[n].Pos
end
mesh.Size = mesh.Size * scale
end
ScaleMesh(MyMesh,Vector3.new(1,.5,1))

If anyone knows of a cleaner or better way to do this, please let me know. But as for now, this works for what I need. Thank you to everyone who replied and gave input. I appreciate it.

@SelDraken Sorry there was a bug in the original script.
It seems you have a working script, but I don’t see the code for “BuildTree”

Here is the corrected version of the original script:

function ScaleMesh(scaleVector3, meshPart)
for _,descendant in pairs(meshPart:GetDescendants()) do
if descendant:IsA("Bone") then
local bone = descendant
-- move bone local translation to part space, scale xyz in part space, then move back to bone parent space
local parentCFrame
if (bone.Parent:IsA("Bone")) then -- parent can be either the MeshPart or another Bone
parentCFrame = bone.Parent.WorldCFrame
else
parentCFrame = bone.Parent.CFrame
end
local parentInPartCFrame = meshPart.CFrame:Inverse() * parentCFrame
local parentInPartRotationCFrame = parentInPartCFrame - parentInPartCFrame.Position --rotation only
local pivotOffsetInPartSpace = parentInPartRotationCFrame * bone.Position
local scaledPivotOffsetInPartSpace = pivotOffsetInPartSpace * scaleVector3
local partToParentRotationCFrame = parentInPartRotationCFrame:inverse()
bone.Position = partToParentRotationCFrame * scaledPivotOffsetInPartSpace
elseif descendant:IsA("Attachment") then
-- attachments are always directly parented to the MeshPart
local attachment = descendant
attachment.Position = attachment.Position * scaleVector3
end
end
meshPart.Size = meshPart.Size * scaleVector3
end
ScaleMesh(Vector3.new(2,1,2), workspace.S1.Lola_Geo)