[Closed] Create a chain of bones from a spline
Something I put together to works some bugs out of a bigger project I’m working on.
Any feedback is, of course, appreciated!
fn getAxis p1 p2 p3 = -- Get an axis using three points
(
x1 = normalize (p2 - p1)
x2 = normalize (p3 - p2)
normalize (cross x1 x2)
)
fn getKnotPoints obj s:1 k: = -- Get the positions of an array of knots along a spline
(
case (classOf k) of
(
array: try (for i in k collect (getKnotPoint obj s i)) catch()
integer: #(getKnotPoint obj s k)
unsuppliedClass: for i = 1 to (numKnots obj s) collect (getKnotPoint obj s i)
)
) -- end getKnotPoints
fn x90_Up o =
(
in coordsys local o.rotation.x_rotation += 90
for c in o.children do in coordsys o rotate c (eulerAngles -90 0 0)
)
fn x90_Down o =
(
in coordsys local o.rotation.x_rotation -= 90
for c in o.children do in coordsys o rotate c (eulerAngles 90 0 0)
)
fn makeTipBone b =
(
local tip = boneSys.createBone b.transform.translation (b.transform.translation - 6) b.dir
tip.name = b.name + "_Tip"
tip.parent = b
tip.transform = b.transform
tip.backFin = tip.frontFin = tip.sideFins = false -- switch off all fins
tip.height = tip.length = tip.width = aMin #(b.width, b.height) -- make tip equilateral
in coordsys local move tip [b.length, 0, 0] -- position tip
tip
)
fn boneChainFromLine lin names base: fWid: fWP:true side: type: wid: WP:true =
(
format "type: %
" type
local segCount = (numKnots lin 1) - 1
local LP = getKnotPoints lin
local boneChain = #()
segLengths = getSegLengths lin 1
segLengths = for s = ((segLengths.count / 2) + 1) to (segLengths.count - 1) collect segLengths[s]
segS = aMin segLengths
segL = aMax segLengths
case of
(
(type == #head or type == #spine):
(
for i = 1 to segCount do
(
local p1 = LP[i], p2 = LP[i + 1]
local a = cross [0, 1, 0] (p2 - p1)
local b = boneSys.createBone p1 p2 a
if names[i] != undefined then
b.name = names[i]
else
b.name = "Bone_" + lin.name + "_" + (i as string)
append boneChain b
if i > 1 do b.parent = boneChain[i - 1]
)
)
(type == #leg):
(
for i = 1 to 5 do -- create thigh and calf
(
local p1 = LP[i], p2 = LP[i + 1]
local a = -- bone alignment
(
case i of
(
1: getAxis LP[3] LP[2] LP[1]
2: getAxis LP[1] LP[2] LP[3]
default: cross [0,0,1] (p2 - p1)
)
) -- end a
b = boneSys.createBone p1 p2 a
append boneChain b
if names[i] != undefined then
b.name = names[i]
else
b.name = "Bone_" + lin.name + "_" + (i as string)
if i == 1 then
(
if base != unsupplied do
b.parent = base
)
else
(
b.parent = boneChain[i - 1]
while (in coordsys parent b.rotation.x_Rotation > 45) do
x90_Down b
while (in coordsys parent b.rotation.x_Rotation < -45) do
x90_Up b
)
) -- end i loop
) -- end leg
(type == #arm):
(
for i = 1 to 3 do
(
local p1 = LP[i], p2 = LP[i + 1]
local a = -- bone alignment
(
case i of
(
1:
(
if base != undefined then
cross (p2 - p1) base.transform[3]
else
cross [0, 1, 0] (p2 - p1)
)
2: getAxis LP[4] LP[3] LP[2]
3: getAxis LP[2] LP[3] LP[4]
) -- end case (i)
) -- end a
local b = boneSys.createBone p1 p2 a
append boneChain b
if names[i] != undefined then
b.name = names[i]
else
b.name = "Bone_" + lin.name + "_" + (i as string)
if i == 1 then
(
if base != undefined do
b.parent = base
)
else
(
b.parent = boneChain[i - 1]
while (in coordsys parent b.rotation.x_Rotation > 45) do
x90_Down b
while (in coordsys parent b.rotation.x_Rotation < -45) do
x90_Up b
)
) -- end i loop
boneChain[boneChain.count].boneScaleType = #none
s = for f = 1 to (numSplines lin) collect (getKnotPoints lin s:f) -- knuckles
m = s[ceil (s.count as float / 2) + 1][1] -- middle finger knuckle point
w = ((LP[4] * 3) + m) / 4 -- middle finger metacarpal start point
n = "Bone_" + (filterString lin.name "_")[1] + "_Finger" + side + "_"
for f = 2 to (numSplines lin) do
(
local fingerBoneChain = #()
local FP = getKnotPoints lin s:f
if f > 2 do -- create metacarpals
(
local p1 = ((s[f][1] - m) / 2) + w, p2 = s[f][1]
local a = getAxis FP[2] FP[3] FP[1]
local b = boneSys.createBone p1 p2 a
append fingerBoneChain b
b.parent = boneChain[boneChain.count] -- parent metacarpals to forearm
)
for i = 2 to (FP.count) do -- create phalanges
(
local p1 = FP[i - 1], p2 = FP[i]
local a = getAxis FP[2] FP[3] FP[1]
local b = boneSys.createBone p1 p2 a
-- Set parent bones
if fingerBoneChain.count > 0 then
b.parent = fingerBoneChain[fingerBoneChain.count]
else
b.parent = boneChain[boneChain.count]
append fingerBoneChain b
)
fingerSegS = aMin (for i in fingerBoneChain collect i.length)
fingerSegL = aMax (for i in fingerBoneChain collect i.length)
for i = 1 to fingerBoneChain.count do
(
b = fingerBoneChain[i]
b.name =
(
if f > 2 then
n + (f - 1) as string + "_" + (i - 1) as string
else
"Bone_" + (filterString lin.name "_")[1] + "_Thumb" + side + "_" + i as string
)
b.height = b.width = -- set height and width of bone
(
case fWP of
(
0: fWid
1: b.length * fWid / 100
2: fingerSegS * fWid / 100
3: fingerSegL * fWid / 100
)
)
b.frontFin = b.sideFins = false
b.backFin = true
b.backFinSize = b.width / 2
)
) -- end f loop
) -- end arm
default:
(
for i = 1 to segCount do
(
local p1 = LP[i], p2 = LP[i + 1] -- start and end points
local a = -- bone alignment
(
if LP.count > 2 then
(
if i == 1 then
getAxis LP[3] LP[2] LP[1]
else
getAxis LP[i - 1] LP[i] LP[i + 1]
)
else
normalize (p1 - p2)
)
local b = boneSys.createBone p1 p2 a
append boneChain b
if names[i] != undefined then
b.name = names[i]
else
b.name = "Bone_" + lin.name + "_" + (i as string)
if i == 1 then
(
if base != undefined do
b.parent = base
)
else
(
b.parent = boneChain[i - 1]
while (in coordsys parent b.rotation.x_Rotation > 45) do
x90_Down b
while (in coordsys parent b.rotation.x_Rotation < -45) do
x90_Up b
)
) -- end i loop
) -- end default
) -- end case
for i = 1 to boneChain.count do
(
b = boneChain[i]
case of
(
(type == #head and i == boneChain.count): b.width = 0.6 * (b.height = b.length)
(type == #leg and i > 2): b.height = 0.25 * (b.width = 0.4 * boneChain[boneChain.count].length)
default:
(
b.height = b.width = -- set height and width of bone
(
case WP of
(
0: wid
1: b.length * wid / 100
2: segS * wid / 100
3: segL * wid / 100
) -- end case (WP)
)
)
) -- end case
b.frontFin = b.sideFins = false
b.backFin = true
b.backFinSize = b.height / 2
) -- end i loop
if not (type == #head or type == #spine) do makeTipBone boneChain[boneChain.count]
boneChain
) -- end boneChainFromLine
try (destroyDialog RO_BoneChainFromLine) catch()
rollout RO_BoneChainFromLine "Bone Chain From Line"
(
local multiSplines = false
fn filterLine obj =
(
(classOf obj == Line) and
(
case RO_BoneChainFromLine.rad_Type.state of
(
1: numSplines obj == 1 and numKnots obj 1 > 2 -- head and neck
2: numSplines obj == 1 and numKnots obj 1 > 1 -- spine
3: numKnots obj 1 == 6 -- leg
4: numSplines obj > 1 and numKnots obj 1 == 4 -- arm
default: numSplines obj == 1
)
)
) -- end filterLine
pickButton pck_Line "Select Line" filter:filterLine autoDisplay:true width:270
label lbl_Type "Chain type:" pos:[15,35]
radioButtons rad_Type labels:#("head and neck", "spine", "leg", "arm", "default") columns:1 default:5 pos:[15,55]
radioButtons rad_Side labels:#("left", "right") enabled: false pos:[15,140]
label lbl_Width "Bone Width:" pos:[164,35]
editText edt_Width width:36 text:"10" pos:[224,35]
checkButton chk_Percent "%" width:20 height:18 checked:true pos:[265,35]
radioButtons rad_Width labels:#("per segment", "of shortest segment", "of longest segment") columns:1 default:3 pos:[164,55]
label lbl_FingerWidth "Finger Width:" pos:[164,110] enabled:false
editText edt_FingerWidth width:36 text:"20" pos:[230,110] enabled:false
checkButton chk_FingerPercent "%" width:20 height:18 checked:true pos:[271,110] enabled:false
radioButtons rad_FingerWidth labels:#("per segment", "of shortest segment", "of longest segment") columns:1 default:3 pos:[164,130] enabled:false
label lbl_CharacterName "Character Name:" pos:[15,165]
editText edt_CharacterName width:278 pos:[10,180]
label lbl_BoneNames "Bone Names:" pos:[15,205]
editText edt_BoneNames width:278 pos:[10,220]
label lbl_Info "Please select a line"
button btn_CreateChain "Create Chain" enabled:false
-----------------
local side
fn setNames =
(
edt_BoneNames.text = ""
prefix = "Bone_" + edt_CharacterName.text
segCount = (numKnots pck_Line.object 1) - 1
case rad_Type.state of
(
1:
(
for i = 1 to (segCount - 1) do
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Neck" + (i as string) + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Head"
)
2:
(
for i = 1 to segCount do
(
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Spine" + (i as string)
if i < segCount do edt_BoneNames.text = edt_BoneNames.text + ", "
)
)
3:
(
side = if rad_Side.state == 1 then "L" else "R"
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Thigh" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Calf" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Instep" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Toe" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Heel" + side
)
4:
(
side = if rad_Side.state == 1 then "L" else "R"
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Clavicle" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_UpperArm" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_ForeArm" + side
)
default:
(
for i = 1 to segCount do
(
edt_BoneNames.text = edt_BoneNames.text + prefix + (i as string)
if i < segCount do edt_BoneNames.text = edt_BoneNames.text + ", "
)
)
) -- end case (rad_Type.state)
) -- end setNames
-----------------
on pck_Line picked obj do
(
if edt_CharacterName.text == "" do edt_CharacterName.text = (filterString obj.name "_")[1]
setNames()
lbl_Info.text = "Ready"
btn_CreateChain.enabled = true
) -- end handler (pck_Line)
on rad_Type changed state do
(
format "changed state
"
rad_Side.enabled = if (state == 3) or (state == 4) then true else false
-- Enable/disable finger related controls
for c in #(lbl_FingerWidth, edt_FingerWidth, chk_FingerPercent, rad_FingerWidth) do
c.enabled = if (state == 4) then true else false
if pck_Line.object != undefined do
(
--local valid = true
obj = pck_Line.object
lbl_Info.text = "Ready"
case state of
(
3: if numKnots obj != 6 do lbl_Info.text = "First spline in shape must have 6 vertices"
4:
(
if numKnots obj 1 != 4 do
lbl_Info.text = "First spline in shape needs 4 vertices"
if numSplines obj < 3 do
lbl_Info.text = "Shape needs 3 or more splines"
)
default: if numSplines obj != 1 do lbl_Info.text = "Shape must have 1 spline"
) -- end case (state)
if lbl_Info.text == "Ready" then
(
setNames()
btn_CreateChain.enabled = true
)
else
(
btn_CreateChain.enabled = false
edt_BoneNames.text = ""
)
)
) -- end handler (rad_Type)
on rad_Side changed state do
(
if pck_Line.object != undefined and lbl_Info.text == "Ready" then
setNames()
else
edt_BoneNames.text = ""
)
on chk_Percent changed state do
rad_Width.enabled = state
on btn_CreateChain pressed do with undo on
(
names = filterString edt_BoneNames.text ", "
chainType =
(
case rad_Type.state of
(
1: #head
2: #spine
3: #leg
4: #arm
5: #default
)
)
wp = if (not chk_Percent.state) then 0 else rad_Width.state -- percent based width type
fwp = if (not chk_FingerPercent.state) then 0 else rad_FingerWidth.state
boneChainFromLine pck_Line.object names base:pck_Line.object.parent fWid:(edt_FingerWidth.text as float) fwp:fWP side:side type:chainType wid:(edt_Width.text as float) wp:wp
) -- end handler (btn_CreateChain)
)
createDialog RO_BoneChainFromLine width:300
could you give a pipeline example where your method might be useful?
btw. i’m very experienced rigger. could it be possible that i’ve missed anything?
Well, it’s in a pretty basic state as seen here. I created this primarily to quickly generate rigs to test out some of my other scripts with. It’s just something to automate the process a bit. Take a character model, throw in a few splines with vertices at the joints, and you can generate a complete skeleton of max bones for the character in a matter of seconds.
More importantly, by specifying the particular types of chains you want to create, this script can be used a base to attach any custom code that you want to each of the limbs. IK chains, control objects, rotation limits, custom attributes, etc. can all be dropped into the various definitions, and will then be automatically incorporated when creating a chain of that type.
good. but spline doesn’t have a normal. it has tangent. yes. but it’s not enough to define bones orientation. is just set of points (as joints) not an easier way to create a bone chain?
In the case of the spline and neck, it IS just using the spline as a set of points. The script is assuming that most people are modeling their characters in a default position of facing forward, and any crookedness in the spine is just that – a crooked spine – rather than an indication of any twisting taking place.
If you /do/ want to try and use the positioning of the points to determine orientation, you can always use the “default” setting instead.
EDIT: okay wow, I feel silly. Somehow I not only read spline as spine but I actually typed OUT “spline” when I meant “spine”! I probably should have taken that nap I was thinking about, haha.
But anyway, the normals are determined by a combination of points along the spline and the type of chain that is specified by the user. For example, knees and elbows move on a single axis, so the normals of the upper and lower arms and leg bones are determined according to that axis. The fingers operate on a similar principle, and the bones of the feet use the ground plane to determine their starting normals.
The spine and head, as I stated before, assume a state of facing forward as their starting position, and the “default” spline uses the starting point of each bone as well as the points directly before and after that point to determine the normal.
Added some code for building a basic autoclav arm.
fn getAxis p1 p2 p3 = -- Get an axis using three points
(
x1 = normalize (p2 - p1)
x2 = normalize (p3 - p2)
normalize (cross x1 x2)
)
fn getKnotPoints obj s:1 k: = -- Get the positions of an array of knots along a spline
(
case (classOf k) of
(
array: try (for i in k collect (getKnotPoint obj s i)) catch()
integer: #(getKnotPoint obj s k)
unsuppliedClass: for i = 1 to (numKnots obj s) collect (getKnotPoint obj s i)
)
) -- end getKnotPoints
fn x90_Up o =
(
in coordsys local o.rotation.x_rotation += 90
for c in o.children do in coordsys o rotate c (eulerAngles -90 0 0)
)
fn x90_Down o =
(
in coordsys local o.rotation.x_rotation -= 90
for c in o.children do in coordsys o rotate c (eulerAngles 90 0 0)
)
fn makeTipBone b =
(
local tip = boneSys.createBone b.transform.translation (b.transform.translation - 6) b.dir
tip.wireColor = b.wireColor
tip.name = b.name + "_Tip"
tip.parent = b
tip.transform = b.transform
tip.backFin = tip.frontFin = tip.sideFins = false -- switch off all fins
tip.height = tip.length = tip.width = aMin #(b.width, b.height) -- make tip equilateral
in coordsys local move tip [b.length, 0, 0] -- position tip
tip
)
fn boneChainFromLine lin names base: fWid: fWP:true IKChain: side: type: wid: WP:true =
(
local segCount = (numKnots lin 1) - 1
local LP = getKnotPoints lin
local boneChain = #()
local segLengths = getSegLengths lin 1
segLengths = for s = ((segLengths.count / 2) + 1) to (segLengths.count - 1) collect segLengths[s]
local chainLength = 0
for s in segLengths do chainLength += s
local segS = aMin segLengths
local segL = aMax segLengths
local hw = -- height and width
(
case WP of
(
0: fWid
1: b.length * fWid / 100
2: segS * fWid / 100
3: segL * fWid / 100
4: chainLength * fWid / 100
)
) -- end hw
case of
(
(type == #head or type == #spine):
(
for i = 1 to segCount do
(
local p1 = LP[i], p2 = LP[i + 1] -- start and end points
local a = cross [0, 1, 0] (p2 - p1)
local b = boneSys.createBone p1 p2 a
if names[i] != undefined then
b.name = names[i]
else
b.name = "Bone_" + lin.name + "_" + (i as string)
append boneChain b
if i == 1 then
if base != unsupplied do b.parent = base
else
b.parent = boneChain[i - 1]
) -- end i loop
)
(type == #leg):
(
for i = 1 to 5 do -- create thigh and calf
(
local p1 = LP[i], p2 = LP[i + 1] -- start and end points
local a = -- bone alignment
(
case i of
(
1: getAxis LP[3] LP[2] LP[1]
2: getAxis LP[1] LP[2] LP[3]
default: cross [0,0,1] (p2 - p1)
)
) -- end a
b = boneSys.createBone p1 p2 a
append boneChain b
if names[i] != undefined then
b.name = names[i]
else
b.name = "Bone_" + lin.name + "_" + (i as string)
if i == 1 then
if base != unsupplied do b.parent = base
else
b.parent = boneChain[i - 1]
) -- end i loop
) -- end leg
(type == #arm):
(
for i = 1 to 3 do
(
local p1 = LP[i], p2 = LP[i + 1] -- start and end points
local a = -- bone alignment
(
case i of
(
1:
(
if base != undefined then
cross (p2 - p1) base.transform[3]
else
cross [0, 1, 0] (p2 - p1)
)
2: getAxis LP[4] LP[3] LP[2]
3: getAxis LP[2] LP[3] LP[4]
) -- end case (i)
) -- end a
local b = boneSys.createBone p1 p2 a
append boneChain b
if names[i] != undefined then
b.name = names[i]
else
b.name = "Bone_" + lin.name + "_" + (i as string)
if i == 1 then
(
if base != undefined do
b.parent = base
)
else
(
b.parent = boneChain[i - 1]
while (in coordsys parent b.rotation.x_Rotation > 45) do
x90_Down b
while (in coordsys parent b.rotation.x_Rotation < -45) do
x90_Up b
)
) -- end i loop
boneChain[boneChain.count].boneScaleType = #none
s = for f = 1 to (numSplines lin) collect (getKnotPoints lin s:f) -- knuckles
m = s[ceil (s.count as float / 2) + 1][1] -- middle finger knuckle point
w = ((LP[4] * 3) + m) / 4 -- middle finger metacarpal start point
n = "Bone_" + (filterString lin.name "_")[1] + "_Finger" + side + "_"
-- Create finger bones
----------------------
for f = 2 to (numSplines lin) do
(
local fingerBoneChain = #()
local FP = getKnotPoints lin s:f
if f > 2 do -- create metacarpals
(
local p1 = ((s[f][1] - m) / 2) + w, p2 = s[f][1]
local a = getAxis FP[2] FP[3] FP[1]
local b = boneSys.createBone p1 p2 a
append fingerBoneChain b
b.parent = boneChain[boneChain.count] -- parent metacarpals to forearm
) -- end if (f > 2)
for i = 2 to (FP.count) do -- create phalanges
(
local p1 = FP[i - 1], p2 = FP[i]
local a = getAxis FP[2] FP[3] FP[1]
local b = boneSys.createBone p1 p2 a
-- Set parent bones
if fingerBoneChain.count > 0 then
b.parent = fingerBoneChain[fingerBoneChain.count]
else
b.parent = boneChain[boneChain.count]
append fingerBoneChain b
) -- end i loop
local fingerChainLength = 0
for i in fingerBoneChain do fingerChainLength += i.length
local fingerSegS = aMin (for i in fingerBoneChain collect i.length)
local fingerSegL = aMax (for i in fingerBoneChain collect i.length)
for i = 1 to fingerBoneChain.count do
(
b = fingerBoneChain[i]
b.name =
(
if f > 2 then
n + (f - 1) as string + "_" + (i - 1) as string
else
"Bone_" + (filterString lin.name "_")[1] + "_Thumb" + side + "_" + i as string
) -- end b.name
b.height = b.width = -- set height and width of bone
(
case fWP of
(
0: fWid
1: b.length * fWid / 100
2: fingerSegS * fWid / 100
3: fingerSegL * fWid / 100
4: fingerChainLength * fWid / 100
)
)
b.frontFin = b.sideFins = false
b.backFin = true
b.backFinSize = b.width / 2
)
) -- end f loop
----------------------
-- AutoClav arm setup
---------------------
if IKChain do
(
autoClavChain = #()
for i = 1 to 3 do
(
b = boneChain[i]
a = copy b
a.boneEnable = true
a.boneScaleType = #none
a.name = b.name + "_A"
a.taper = 90
a.wireColor = (color 255 0 0)
append autoClavChain a
-- Bone and fin size
a.width = a.height = (hw / 2)
a.frontFin = a.sideFins = false
a.backFin = true
a.backFinSize = a.height / 8
a.parent =
(
if i == 1 then
boneChain[1].parent
else
autoClavChain[i - 1]
) -- end a.parent
) -- end i loop
makeTipBone autoClavChain[autoClavChain.count]
aim = point name:(boneChain[1].name + "_Aim") transform:boneChain[1].transform
tgt = point name:(boneChain[1].name + "_Target") transform:boneChain[2].transform parent:autoClavChain[2]
aim.rotation.controller = lookAt_Constraint lookAt_Vector_Length:0 pickUpNode:autoClavChain[1] relative:true upNode_World:false
aim.rotation.controller.appendTarget tgt 100
aim.parent = base
for p in #(aim, tgt) do
(
p.size = hw
p.wireColor = color 14 255 2
p[4][2].value = false
p[4][4].value = false
p[4][5].value = true
)
in coordSys local move tgt [boneChain[2].length / 2, 0, 0]
boneChain[1].parent = aim
) -- end if (IKChain)
---------------------
) -- end arm
default:
(
for i = 1 to segCount do
(
local p1 = LP[i], p2 = LP[i + 1] -- start and end points
local a = -- bone alignment
(
if LP.count > 2 then
(
if i == 1 then
getAxis LP[3] LP[2] LP[1]
else
getAxis LP[i - 1] LP[i] LP[i + 1]
)
else
normalize (p1 - p2)
) -- end a
local b = boneSys.createBone p1 p2 a
append boneChain b
if names[i] != undefined then
b.name = names[i]
else
b.name = "Bone_" + lin.name + "_" + (i as string)
if i == 1 then
(
if base != undefined do
b.parent = base
)
else
(
b.parent = boneChain[i - 1]
while (in coordsys parent b.rotation.x_Rotation > 45) do
x90_Down b
while (in coordsys parent b.rotation.x_Rotation < -45) do
x90_Up b
)
) -- end i loop
) -- end default
) -- end case
for i = 1 to boneChain.count do
(
b = boneChain[i]
case of
(
(type == #head and i == boneChain.count): b.width = 0.6 * (b.height = b.length)
(type == #leg and i > 2): b.height = 0.25 * (b.width = 0.4 * boneChain[boneChain.count].length)
default: b.height = b.width = hw -- set height and width of bone
) -- end case
-- Bone fins
b.frontFin = b.sideFins = false
b.backFin = true
if type == #head and i == boneChain.count then
b.backFinSize = b.height / 8
else
b.backFinSize = b.height / 4
b.taper = -- bone taper
(
case of
(
(type == #head and i == boneChain.count): 30
(type == #leg and i == 2): 70
(type == #leg and i > 2): 30
(type == #arm and i == 3): 70
default: 50
)
) -- end b.taper
) -- end i loop
if not (type == #head or type == #spine) do makeTipBone boneChain[boneChain.count]
if IKChain == true do
(
case type of
(
#arm:
(
IKHI = IKSys.ikChain boneChain[2] boneChain[3].children[boneChain[3].children.count] "IKHISolver"
IKHI.controller.dispGoal = true
IKHI.name = ("IK_" + lin.name)
IKHI_A = IKSys.ikChain autoClavChain[2] autoClavChain[3].children[1] "IKHISolver"
IKHI_A.controller.dispGoal = false
IKHI_A.name = ("IK_" + lin.name + "_A")
p = point pos:IKHI.pos size:hw wireColor:(color 14 255 2)
p[4][2].value = false
p[4][4].value = false
p[4][5].value = true
for c in #(IKHI, IKHI_A) do
(
c.controller.dispSolver = false
c.controller.goalSize = c.controller.endJoint.length
c.pos.controller = position_Constraint()
c.pos.controller.appendTarget p 100
)
)
) -- end case (type)
) -- end if (IKChain == true)
boneChain
) -- end boneChainFromLine
try (destroyDialog RO_BoneChainFromLine) catch()
rollout RO_BoneChainFromLine "Bone Chain From Line"
(
local multiSplines = false
fn filterLine obj =
(
(classOf obj == Line) and
(
case RO_BoneChainFromLine.rad_Type.state of
(
1: numSplines obj == 1 and numKnots obj 1 > 2 -- head and neck
2: numSplines obj == 1 and numKnots obj 1 > 1 -- spine
3: numKnots obj 1 == 6 -- leg
4: numSplines obj > 1 and numKnots obj 1 == 4 -- arm
default: numSplines obj == 1
)
)
) -- end filterLine
-- Rollout controls
-------------------
pickButton pck_Line "Select Line" filter:filterLine autoDisplay:true width:270
label lbl_Type "Chain type:" pos:[15,35]
radioButtons rad_Type labels:#("head and neck", "spine", "leg", "arm", "default") columns:1 default:5 pos:[15,55]
radioButtons rad_Side labels:#("left", "right") enabled: false pos:[15,140]
checkButton chk_IK "IK" enabled:false pos:[15,165]
label lbl_Width "Bone Width:" pos:[164,35]
editText edt_Width width:36 text:"10" pos:[224,35]
checkButton chk_Percent "%" width:20 height:18 checked:true pos:[265,35]
radioButtons rad_Width labels:#("per segment", "of shortest segment", "of longest segment", "of chain length") columns:1 default:3 pos:[164,55]
label lbl_FingerWidth "Finger Width:" pos:[164,125] enabled:false
editText edt_FingerWidth width:36 text:"20" pos:[230,125] enabled:false
checkButton chk_FingerPercent "%" width:20 height:18 checked:true pos:[271,125] enabled:false
radioButtons rad_FingerWidth labels:#("per segment", "of shortest segment", "of longest segment", "of chain length") columns:1 default:3 pos:[164,145] enabled:false
label lbl_CharacterName "Character Name:" pos:[15,195]
editText edt_CharacterName width:278 pos:[10,210]
label lbl_BoneNames "Bone Names:" pos:[15,230]
editText edt_BoneNames width:278 pos:[10,245]
label lbl_Info "Please select a line"
button btn_CreateChain "Create Chain" enabled:false
-------------------
local side
fn setNames =
(
edt_BoneNames.text = ""
prefix = "Bone_" + edt_CharacterName.text
segCount = (numKnots pck_Line.object 1) - 1
case rad_Type.state of
(
1: -- Generate names for neck and head bones
(
for i = 1 to (segCount - 1) do
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Neck" + (i as string) + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Head"
)
2: -- Generate names for spine bones
(
for i = 1 to segCount do
(
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Spine" + (i as string)
if i < segCount do edt_BoneNames.text = edt_BoneNames.text + ", "
)
)
3: -- Generate names for leg chain bones
(
side = if rad_Side.state == 1 then "L" else "R"
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Thigh" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Calf" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Instep" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Toe" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Heel" + side
)
4: -- Generate names for arm bone chains
(
side = if rad_Side.state == 1 then "L" else "R"
edt_BoneNames.text = edt_BoneNames.text + prefix + "_Clavicle" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_UpperArm" + side + ", "
edt_BoneNames.text = edt_BoneNames.text + prefix + "_ForeArm" + side
)
default:
(
for i = 1 to segCount do
(
edt_BoneNames.text = edt_BoneNames.text + prefix + (i as string)
if i < segCount do edt_BoneNames.text = edt_BoneNames.text + ", "
)
)
) -- end case (rad_Type.state)
) -- end setNames
-----------------
on pck_Line picked obj do
(
if edt_CharacterName.text == "" do edt_CharacterName.text = (filterString obj.name "_")[1]
setNames()
lbl_Info.text = "Ready"
btn_CreateChain.enabled = true
) -- end handler (pck_Line)
on rad_Type changed state do
(
rad_Side.enabled = if (state == 3) or (state == 4) then true else false
-- Enable/disable finger related controls
for c in #(lbl_FingerWidth, edt_FingerWidth, chk_FingerPercent, rad_FingerWidth) do
c.enabled = if (state == 4) then true else false
chk_IK.enabled = if (state == 3 or state == 4) then true else false
if pck_Line.object != undefined do
(
obj = pck_Line.object
lbl_Info.text = "Ready"
case state of
(
3: if numKnots obj != 6 do lbl_Info.text = "First spline in shape must have 6 vertices"
4:
(
if numKnots obj 1 != 4 do
lbl_Info.text = "First spline in shape needs 4 vertices"
if numSplines obj < 3 do
lbl_Info.text = "Shape needs 3 or more splines"
)
default: if numSplines obj != 1 do lbl_Info.text = "Shape must have 1 spline"
) -- end case (state)
if lbl_Info.text == "Ready" then
(
setNames()
btn_CreateChain.enabled = true
)
else
(
btn_CreateChain.enabled = false
edt_BoneNames.text = ""
)
)
) -- end handler (rad_Type)
on rad_Side changed state do
(
if pck_Line.object != undefined and lbl_Info.text == "Ready" then
setNames()
else
edt_BoneNames.text = ""
)
on chk_Percent changed state do
rad_Width.enabled = state
on btn_CreateChain pressed do with undo on
(
names = filterString edt_BoneNames.text ", "
chainType =
(
case rad_Type.state of
(
1: #head
2: #spine
3: #leg
4: #arm
5: #default
)
) -- end chainType
wp = if (not chk_Percent.state) then 0 else rad_Width.state -- percent based width type
fwp = if (not chk_FingerPercent.state) then 0 else rad_FingerWidth.state
boneChainFromLine pck_Line.object names base:pck_Line.object.parent fWid:(edt_FingerWidth.text as float) fwp:fWP [color=Lime]IKChain:chk_IK.checked[/color] side:side type:chainType wid:(edt_Width.text as float) wp:wp
) -- end handler (btn_CreateChain)
)
createDialog RO_BoneChainFromLine width:300
Next I’ll do the same for a reverse foot rig.
reverse foot… back to 00th… do you not know that the reverse foot is not in trend anymore?
interesting… who will be the first told us what a ‘reverse foot’ the biggest problem is?