PDA

View Full Version : Bone Mirrorring through MaxScript


Guibou
01-08-2009, 07:14 PM
Hi everyone, I'm trying to create a copy of a bone chain and then mirror it while flipping the Z axis. I found this in Maxscript Reference, but i haven't been able to use properly. (sorry for the color of the text, it's copied directly from Maxscript Reference)

Interface : INode.boneAxis : enum : Read|Write

boneAxis enums: {#X|#Y|#Z}

Gets/Sets the node’s Bone Axis.

.boneAxisFlip : boolean : Read|Write

Gets/Sets the node’s Bone Flip Axis option.

I've tried different things and none of them seem to work. Thanks for any help and have a good day.

martroyx
01-08-2009, 09:37 PM
(
--//-- $ is the root bone!
maxOps.CloneNodes $ expandHierarchy:true clonetype:#copy newNodes:&newnode
about $.pos scale newnode[1] [1,1,-1]
)


you may be able to do this with the bone interface but haven't tried yet !

best regard,
martin

JHN
01-08-2009, 10:17 PM
I would strongly advise against negative scaling a bonestring, for various reasons. I would gather the points from one side and build a new bone based on the -x coordinates.

so take a leftBone for example



lBone = selection[1]

sPoint = lBone.pos
ePoint = (preTransLate lBone.transform [(lBone.inode.stretchTM.scale.x *lBone.length),0,0]).pos -- get bone end pos, compensate for stretch

rBone = boneSys.createBone [-sPoint.x,sPoint.y,sPoint.z] [-ePoint.x,ePoint.y,ePoint.z] -lBone.dir



This will create a new bone with a flipped Z axis.
Hope this get's you on the way, the best thing about this approach is that there's no children or parent looping quering involved since we solved the length of the bone by itself using the preTranslate function.

-Johan

martroyx
01-08-2009, 10:33 PM
yes recreating the bone chain from scratch is the best solution but he will still need to iterate through the bone chain to find ik solver, if any !

Guibou
01-09-2009, 12:34 AM
Works just great! Thanks a lot man, it's pretty awesome thing to be able to get help like that, I love these forum. One day, i'll be good enough to return the favor to somebody else. Have a good one

martroyx
01-09-2009, 01:58 AM
I always create bone from spline with something like this ...


(
RigName="rig"
--//--
fn CreateBones spln name dir color1 color2 Side front back taper width height =
(
local Xknot=(numKnots spln)
local GradColor=(color1-color2)/Xknot
local PrevKnot=undefined
local PrevBone=undefined
local b0=undefined
--//--
for f in 1 to Xknot do
(
Case f of
(
1:(
PrevKnot=(getKnotPoint spln 1 f)
)
Xknot:(
b0=BoneSys.createBone PrevKnot (getKnotPoint spln 1 Xknot) dir
b0.parent=PrevBone
)
Default:(
b0=BoneSys.createBone PrevKnot (getKnotPoint spln 1 f) dir
b0.parent=PrevBone
PrevBone=b0
PrevKnot=(getKnotPoint spln 1 f)
)
)
--//--
if b0 != undefined then
(
b0.name=uniquename (RigName+"_"+name+"_")
b0.wirecolor=(color1-(GradColor*f))
b0.sidefins=Side
b0.frontfin=front
b0.backfin=back
b0.taper=taper
b0.Width=width
b0.height=height
)
)
--//--
)
--//--
for f in selection do
(
CreateBones f "test" f.dir (color 255 0 255) (color 255 0 0) off off off 60 2 2
)
--//--
)


so if I want symmetry , I mirror the spline and recreate again ;)

Hope this help,

martin

Guibou
01-09-2009, 03:18 PM
Hi guys,
i used JHN's piece of code and it worked well to flip the axis of the bone. What i am realizing now is that the bone is flipped but it's rotation isn't exactly mirrored to the other side. There might be some logic that i'm not getting here. (sorry for putting the code like that, but i don't know how to create that little Code window in the post). So here is a piece the code that i am using to create a new chain of bone that is flipped (using JHN advice thx a lot btw)
(
local
bnsM=#(),
i= 1
-- Creates a Z flipped chain of bones of the leg
for b in selection do
(
flipBone= selection[i]

sPoint= flipBone.pos
-- get the position of the end of the bone (results in the position of the next bone in the chain)
ePoint= (preTransLate flipBone.transform [(flipBone.inode.stretchTM.scale.x *flipBone.length),0,0]).pos

mirBone = boneSys.createBone [-sPoint.x,sPoint.y,sPoint.z] [-ePoint.x,ePoint.y,ePoint.z] -flipBone.dir

append bnsM mirBone

i=i+1
)

-- Creates the hierarchy between the flipped bones
for i = 2 to selection.count do
(
bnsM[i].parent= bnsM[i-1]
)
)

I've attached this image to represent better my problem. If you look at the point helper on the left, he is mirrored with the point helper on the right (that is aligned with the original bone chain). My newly create bone chain on the left is positionned correctly, but the orientation is off.
If anybody knows what i'm doing wrong it would be of great help. Thanks a lot guys for the previous responses. Have a good one

Guibou
01-09-2009, 03:20 PM
Sorry, but i wasn't able to attach the jpg to the previous post.

MatanH
01-10-2009, 02:34 PM
Is that what your looking for?
just follow the instructions in the example part of the code to see it in action..

(
-- return the mirror of this point on a given axis
-- the axis argument can be 0 for X, 1 for Y or 2 for Z
-- any other value will simply copy the point
fn mirrorPoint pt axis =
(
pt * (
case axis of (
0: [-1,1,1]
1: [1,-1,1]
2: [1,1,-1]
default: [1,1,1]
)
)
)

-- return the end point of a given bone in world space
fn getBoneEnd bone =
(
[bone.length, 0, 0] * bone.transform
)

-- will copy the base object and wire color from one bone to another
fn copyBoneProperties fromBone toBone =
(
toBone.baseObject = copy fromBone.baseObject
toBone.wireColor = fromBone.wireColor
)

-- recursively mirror copy a chain of bones on a given axis
-- starting from the last bone and ending on the first bone in the hierarchy chain
fn mirrorBoneChain lastBone axis =
(
undo "Mirror Bones Script" on (
max create mode

local parentBone = undefined
if isKindOf lastBone.parent BoneGeometry then
parentBone = mirrorBoneChain lastBone.parent axis

local start = mirrorPoint lastBone.pos axis
local end = mirrorPoint (getBoneEnd lastBone) axis
local dir = mirrorPoint lastBone.dir axis

local newBone = boneSys.createBone start end dir
copyBoneProperties lastBone newBone
newBone.parent = parentBone

newBone
)
)

-- Example:
-------------------------------------------------

-- manually create a chain of bones and select the last bone in the cahin

-- this line will mirror copy the chain on the X axis:
mirrorBoneChain $ 0

-- this line will mirror copy the chain on the Y axis:
mirrorBoneChain $ 1

-- this line will mirror copy the chain on the Z axis:
mirrorBoneChain $ 2
)

martroyx
01-10-2009, 06:16 PM
or using some kind of hack from Zeboxx you can do something like this ...


(
--//--
--//-- select a bone chain including ik and run the script.
--//--
fn XMirror =
(
local Xhwnd=dialogMonitorOps.getWindowHandle()
local Xstring=UIAccessor.GetWindowText Xhwnd
--//--
if Xhwnd != undefined then
(
if Xstring != undefined and (findString Xstring "Mirror") != undefined then
(
UIAccessor.PressButtonByName Xhwnd "X"
UIAccessor.PressButtonByName Xhwnd "Instance"
UIAccessor.PressDefaultButton()
)
)
true
)
--//--
if selection.count != 0 then
(
dialogMonitorOps.registerNotification XMirror id:#findmirror
dialogMonitorOps.enabled=true
--//--
max mirror
--//--
dialogMonitorOps.unregisterNotification id:#findmirror
dialogMonitorOps.enabled=false
--//--
Newselection=selection as array
--//--
for f in 1 to selection.count do (max select parent)
--//--
for f in selection do
(
InstanceMgr.GetInstances f &x
for i in x where (i != f) do f.pos.x=-i.pos.x
)
--//--
select Newselection
InstanceMgr.MakeObjectsUnique Newselection #group
)
--//--
)


witch will mirror bone chain and ik as well ;)

best regard,
martin

martroyx
01-10-2009, 07:16 PM
hum... it doesn't always work well with ik but bone chain should be fine :-P

martroyx
01-10-2009, 08:19 PM
(
--//--
--//-- select a bone chain including ik and run the script.
--//--
fn XMirror =
(
local Xhwnd=dialogMonitorOps.getWindowHandle()
local Xstring=UIAccessor.GetWindowText Xhwnd
--//--
if Xhwnd != undefined then
(
if Xstring != undefined and (findString Xstring "Mirror") != undefined then
(
UIAccessor.PressButtonByName Xhwnd "X"
UIAccessor.PressButtonByName Xhwnd "Instance"
UIAccessor.PressDefaultButton()
)
)
true
)
--//--
if selection.count != 0 then
(
dialogMonitorOps.registerNotification XMirror id:#findmirror
dialogMonitorOps.enabled=true
--//--
disableSceneRedraw()
max mirror
--//--
dialogMonitorOps.unregisterNotification id:#findmirror
dialogMonitorOps.enabled=false
--//--
Newselection=selection as array
--//--
for f in 1 to selection.count do (max select parent)
--//--
for f in selection do
(
InstanceMgr.GetInstances f &x
for i in x where (i != f) do f.pos.x=-i.pos.x
)
--//--
select Newselection
--//--
for f in selection do
(
InstanceMgr.GetInstances f &x
for i in x where (i != f) do f.name=(i.name+"_Mirror")
)
--//--
InstanceMgr.MakeObjectsUnique Newselection #group
enableSceneRedraw()
)
--//--
)


added enable\disable scene redraw but we still see the mirror dialog showing up ... so it all depend if you need speed or not :shrug:

JHN
01-11-2009, 07:56 PM
Hi guys,
i used JHN's piece of code and it worked well to flip the axis of the bone. What i am realizing now is that the bone is flipped but it's rotation isn't exactly mirrored to the other side. There might be some logic that i'm not getting here. (sorry for putting the code like that, but i don't know how to create that little Code window in the post). So here is a piece the code that i am using to create a new chain of bone that is flipped (using JHN advice thx a lot btw)


I've attached this image to represent better my problem. If you look at the point helper on the left, he is mirrored with the point helper on the right (that is aligned with the original bone chain). My newly create bone chain on the left is positionned correctly, but the orientation is off.
If anybody knows what i'm doing wrong it would be of great help. Thanks a lot guys for the previous responses. Have a good one

I see know what I did wrong, it was in the dir of the newBone, I should have multiplied the dir vector with the mirror axis, so -lBone.dir should have been (lBone.dir * [-1,1,1]) this effectively mirrors the direction on X, Matan is doing it in his example, but he's missing the stretch component in calculating the bone lenght. But now should have plenty of options too choose from!

Cheers,
-Johan

Guibou
01-12-2009, 10:17 PM
Great it works! :)

Thanks a lot guys

While i'm here ;) question for you guys, if you don't mind.
Looking at martroyx (QuebecPower) script i could see that he used these command...
disableSceneRedraw()
enableSceneRedraw()
I believe this is to make you script run faster, but i'm not sure exactly how and when to use it in a script. If anyone is kind enough to explain that would be appreciated, if not it's all good you guys helped me a lot already.

Have a good one and thanks again all

martroyx
01-12-2009, 11:38 PM
Hi Guibou,

you disableSceneRedraw() when you need the viewport to stop doing refresh, this way you can select and move stuff around without 3dsmax having to draw those operation... just remember to enableSceneRedraw() when you are done :-)

martin

CGTalk Moderation
01-12-2009, 11:38 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.