Home Technical Talk

mirroring specific area in skin mode

fenderbender
polycounter lvl 15
Offline / Send Message
fenderbender polycounter lvl 15
hi everyone i have a question.How to mirror a specific area while in skin mode.lets say i have a character which is the body is not symmetry but the face is symmetry.i already done skinning the whole body now I'm moving to facial rigging.I'm using Max 2009 by the way.THX.

Replies

  • cw
    Options
    Offline / Send Message
    cw polycounter lvl 17
    In mirror mode in the skin modifier, you can select vertices and choose to paste selected which will mirror across the verts you have selected.
  • pasha_sevez
    Options
    Offline / Send Message
    pasha_sevez polycounter lvl 13
    cw wrote: »
    In mirror mode in the skin modifier, you can select vertices and choose to paste selected which will mirror across the verts you have selected.

    Mirror mode is not a cure for a situation, when there is a topology mismatch or the mesh is simply asymmetric! I was very angry then I couldn't simply copy a weight of a specific vertex to memory but paste it to the arbitrary vertex on the opposite side of the model - max simply can't do that - the weight contains the boneIDs of the side it was copied from.

    I've solved this problem via a custom "copy weight MIRRORED" script.
    macroScript t2_CopyMirroredWeight
    	category:"t2"
    	toolTip:"Copy mirrored weights"
    	buttonText:"CMW"
    (
    	on execute do
    	(
    		local rbn = #(
    			#( "hgbip_Spine",
    				"hgbip_Spine1",
    				"hgbip_Eye_R",
    				"hgbip_R_Clavicle_Help",
    				"hgbip_RUpArmTwist_Help",
    				"hgbip_Eye_L",
    				"hgbip_RUpArmTwist2_Help",
    				"hgbip_R_ForeTwist_Help",
    				"hgbip_Lid_R",
    				"hgbip_R_ForeTwist2_Help",
    				"hgbip_Lid_L",
    				"hgbip_R_Hand",
    				"hgbip_Head",
    				"hgbip_R_BackCloth",
    				"hgbip_L_BackCloth",
    				"hgbip_Brow_Inner_R",
    				"hgbip_R_Thigh_Help",
    				"hgbip_R_Calf_Help",
    				"hgbip_Brow_Outer_R",
    				"hgbip_R_Foot",
    				"hgbip_L_Thigh_Help",
    				"hgbip_Brow_Inner_L",
    				"hgbip_Brow_Outer_L",
    				"hgbip_L_Calf_Help",
    				"hgbip_L_Clavicle_Help",
    				"hgbip_R_Finger0",
    				"hgbip_R_Finger01",
    				"hgbip_R_Finger02",
    				"hgbip_R_Finger1",
    				"hgbip_R_Finger11",
    				"hgbip_R_Finger12",
    				"hgbip_R_Finger2",
    				"hgbip_R_Finger21",
    				"hgbip_R_Finger22",
    				"hgbip_R_Finger32",
    				"hgbip_R_Finger31",
    				"hgbip_R_Finger3",
    				"hgbip_R_Finger4",
    				"hgbip_R_Finger41",
    				"hgbip_R_Finger42",
    				"hgbip_Yaw",
    				"hgbip_R_Toe01",
    				"hgbip_LUpArmTwist_Help",
    				"hgbip_Lip_Down_R",
    				"hgbip_LUpArmTwist2_Help",
    				"hgbip_Lip_Down_L",
    				"hgbip_L_ForeTwist_Help",
    				"hgbip_L_ForeTwist2_Help",
    				"hgbip_Lip_Up_R",
    				"hgbip_L_Hand",
    				"hgbip_L_Finger0",
    				"hgbip_L_Finger01",
    				"hgbip_L_Finger02",
    				"hgbip_L_Finger1",
    				"hgbip_L_Finger11",
    				"hgbip_L_Finger12",
    				"hgbip_L_Finger2",
    				"hgbip_L_Finger21",
    				"hgbip_L_Finger22",
    				"hgbip_L_Finger3",
    				"hgbip_L_Finger31",
    				"hgbip_L_Finger32",
    				"hgbip_L_Finger4",
    				"hgbip_L_Finger41",
    				"hgbip_L_Finger42",
    				"hgbip_L_Foot",
    				"hgbip_Lip_Up_L",
    				"hgbip_L_Toe01",
    				"hgbip_Neck1",
    				"hgbip_Spine2_Scripted" ),
    
    			#( "hgbip_Spine",
    				"hgbip_Spine1",
    				"hgbip_Eye_L",
    				"hgbip_L_Clavicle_Help",
    				"hgbip_LUpArmTwist_Help",
    				"hgbip_Eye_R",
    				"hgbip_LUpArmTwist2_Help",
    				"hgbip_L_ForeTwist_Help",
    				"hgbip_Lid_L",
    				"hgbip_L_ForeTwist2_Help",
    				"hgbip_Lid_R",
    				"hgbip_L_Hand",
    				"hgbip_Head",
    				"hgbip_L_BackCloth",
    				"hgbip_R_BackCloth",
    				"hgbip_Brow_Inner_L",
    				"hgbip_L_Thigh_Help",
    				"hgbip_L_Calf_Help",
    				"hgbip_Brow_Outer_L",
    				"hgbip_L_Foot",
    				"hgbip_R_Thigh_Help",
    				"hgbip_Brow_Inner_R",
    				"hgbip_Brow_Outer_R",
    				"hgbip_R_Calf_Help",
    				"hgbip_R_Clavicle_Help",
    				"hgbip_L_Finger0",
    				"hgbip_L_Finger01",
    				"hgbip_L_Finger02",
    				"hgbip_L_Finger1",
    				"hgbip_L_Finger11",
    				"hgbip_L_Finger12",
    				"hgbip_L_Finger2",
    				"hgbip_L_Finger21",
    				"hgbip_L_Finger22",
    				"hgbip_L_Finger32",
    				"hgbip_L_Finger31",
    				"hgbip_L_Finger3",
    				"hgbip_L_Finger4",
    				"hgbip_L_Finger41",
    				"hgbip_L_Finger42",
    				"hgbip_Yaw",
    				"hgbip_L_Toe01",
    				"hgbip_RUpArmTwist_Help",
    				"hgbip_Lip_Down_L",
    				"hgbip_RUpArmTwist2_Help",
    				"hgbip_Lip_Down_R",
    				"hgbip_R_ForeTwist_Help",
    				"hgbip_R_ForeTwist2_Help",
    				"hgbip_Lip_Up_L",
    				"hgbip_R_Hand",
    				"hgbip_R_Finger0",
    				"hgbip_R_Finger01",
    				"hgbip_R_Finger02",
    				"hgbip_R_Finger1",
    				"hgbip_R_Finger11",
    				"hgbip_R_Finger12",
    				"hgbip_R_Finger2",
    				"hgbip_R_Finger21",
    				"hgbip_R_Finger22",
    				"hgbip_R_Finger3",
    				"hgbip_R_Finger31",
    				"hgbip_R_Finger32",
    				"hgbip_R_Finger4",
    				"hgbip_R_Finger41",
    				"hgbip_R_Finger42",
    				"hgbip_R_Foot",
    				"hgbip_Lip_Up_R",
    				"hgbip_R_Toe01",
    				"hgbip_Neck1",
    				"hgbip_Spine2_Scripted" )
    				)
    				
    		local rbc =#()
    				
    		local n = $
    		local m = n.modifiers[#skin]
    		local op = if classof n.baseobject == Editable_poly then polyop else meshop
    			
    		global ba = #(#(),#())
    		if ba[1].count > 0 then deleteItem ba[1] ba[1].count
    		if ba[2].count > 0 then deleteItem ba[2] ba[2].count
    		--print "------------"
    		--print ba[1].count
    		--if ba[1].count > 0 then for ind = 1 to ba[1].count do print print ba[ind]
    		--print "------------"
    		
    		for bc = 1 to skinOps.GetNumberBones m do
    		(
    			rbc[bc] = findItem rbn[2] (skinOps.GetBoneName m bc 1)
    			--print rbc[bc]
    		)
    
    		for vert=1 to op.getNumVerts n do
    		(
    			if skinOps.IsVertexSelected m vert == 1 then
    			(
    				for w=1 to skinOps.GetVertexWeightCount m vert do
    				(
    					local b = skinOps.GetVertexWeightBoneID m vert w
    					local bw = skinOps.GetVertexWeight m vert w
    
     					if ( finditem ba[1] rbc[b] ) == 0 then 
    					(
    						append ba[1] rbc[b]
    						append ba[2] bw
    						print b
    						print rbc[b]
    						print bw
    					)
    				)
    			)
    		)
    		
    		if ba[1].count == 0 then	return () else
    		(
    			print "------------"
    			print ba[1].count
    			for ind = 1 to ba[1].count do print ba[1][ind]
    			print "------------"
    		)
    		
    	)
    )
    
    
    macroScript t2_PasteMirroredWeight
    	category:"t2"
    	toolTip:"Paste mirrored weights"
    	buttonText:"PMW"
    (
    	on execute do
    	(
    		if ba[1].count == 0 then return ()
    		
    		local n = $
    		local m = n.modifiers[#skin]
    		local op = if classof n.baseobject == Editable_poly then polyop else meshop
    			
    		for vert=1 to op.getNumVerts n do
    		if skinOps.IsVertexSelected m vert == 1 then
    		(
    			skinOps.ReplaceVertexWeights m vert ba[1] ba[2]
    		)
    	)
    )
    
    
    macroScript t2_MirrorVertices
    	category:"t2"
    	toolTip:"Mirror Vertices"
    	buttonText:"MV"
    (
    	on execute do
    	(
    		
    		local n = $
    		local m = n.modifiers[#skin]
    		local op = if classof n.baseobject == Editable_poly then polyop else meshop
    			
    		for vert=1 to op.getNumVerts n do
    		if skinOps.IsVertexSelected m vert == 1 then
    		(
    				m.mirrorEnabled = on
    				skinOps.mirrorPaste m
    				m.mirrorEnabled = off
    		)
    	)
    )
    

    Look at the 2d-array in the variable rbn. The fist element if this array is a list of all bones of the hierarchy. The second element - is a list off all bones too, but each bone's name is replaced with the opposite bone's one. You have to get a list of bones, choose your skinned object execute the following code
    for bc = 1 to (skinOps.GetNumberBones $.modifiers[#skin]) do ( print (skinOps.GetBoneName $.modifiers[#skin] bc 1) )
    

    Button CMW - Copy Mirrored Weight
    Button PMW - Paste Mirrored Weight
    Button MV - Mirror Weight using the max's default mirroring technique.
  • cw
    Options
    Offline / Send Message
    cw polycounter lvl 17
    Sorry man I got the sense from the OP that he has already skinned the asymmetrical body, and was moving onto the symmetrical face, hence the request for info about mirroring specific verts.

    That's an interesting script. i wonder if the weight tool's copy/paste could be made to work this way and copy mirrored. :)
  • pasha_sevez
    Options
    Offline / Send Message
    pasha_sevez polycounter lvl 13
    cw wrote: »
    Sorry man I got the sense from the OP that he has already skinned the asymmetrical body, and was moving onto the symmetrical face, hence the request for info about mirroring specific verts.

    That's an interesting script. i wonder if the weight tool's copy/paste could be made to work this way and copy mirrored. :)

    You're right, just shared a script - may be some else will find it useful ))
  • Mark Dygert
    Options
    Offline / Send Message
    I'm a big fan of adding asymmetry after the fact and depending on the project, the object, the engine and the technical artists involved it be done a few ways.

    The straight max method is to create a symmetrical mesh, skin it and deform it after skin either by adding a edit poly on top and being careful not to add/remove geometry just push/pull it around. This doesn't always export well with some engines so you can...

    Use a morpher modifier (underneath skin) that does pretty much the same thing as above (deforms a symmetrical mesh into asymmetrical mesh). You create a copy of your symmetrical mesh and deform it to be your asymmetrical mesh, by only pushing and pulling verts not by adding or removing geometry. Then use the morpher modifier to deform the already skinned symmetrical mesh and do a "collapse to" to apply the asymmetry.

    I believe you can also save out the skin weights for the symmetrical mesh and load them into the asymmetrical mesh, if "collapsing to" fubars something, which it shouldn't.

    Transmographier has a GREAT way of working symmetrically while your end result is asymmetrical and I would recommend it for any symmetrical mesh you wish to deform into a asymmetrical mesh and wishes to still work symmetrically after its deformed. Why that hasn't been included in 3dsmax is still a mystery... the fuckin plugin rocks so hard it needs to be standard issue.
  • pasha_sevez
    Options
    Offline / Send Message
    pasha_sevez polycounter lvl 13
    I'm a big fan of adding asymmetry after the fact and depending on the project, the object, the engine and the technical artists involved it be done a few ways.

    Sometimes you have deal with an asymmetric mesh with asymmetric topology. And there's nothing you can do to make it symmetrical ))
    But morphing AFTER skinning is a good ideal =) Sometimes it seems to be applicable!
    I'll try transmographer - thank for pointing it out!
  • fenderbender
    Options
    Offline / Send Message
    fenderbender polycounter lvl 15
    ummm a big thanks pasha, but i have a very limited knowledge about using script.i understand how to save the first code as .mcr.The part that i don't understand is the second part how to execute the code.

    is there any site out there that can help me learn the basic of "what to do" when someone gave me script like this?

    noob trying to learn here...Thanks again.
  • Funky Bunnies
    Options
    Offline / Send Message
    Funky Bunnies polycounter lvl 17
    I have this problem at work sometimes. But I think we've gone off on a tangent from fenderbender's original question.

    Assuming he wants to mirror the symmetrical portion of the mesh while maintaining any skinning on the asymmetrical portion of the mesh, I agree with cw to use the skin modifier mirror in this case.
    This is probably the easiest way to mirror only a small portion of the skinVerts on a partially symmetrical mesh. Just select the verts you want to mirror in the skin modifier and hit those buttons

    mirrorweights_example.jpg

    the skin modifier's mirror works based on position, which you can change by spinning 'Mirror Tresh'*:
    any vertex that's within X units away from a mirrored version of the vert gets the same weight from any bone that's found at the mirrored position within X units away.

    *NOTE: the red lines like in the eyes are supposed to be green/blue. Those lines indicate the bones and if they're green/blue that means a mirror was found. You should only have red shit in the middle where there's no applicable mirror.



    Vig wrote:
    I believe you can also save out the skin weights for the symmetrical mesh and load them into the asymmetrical mesh, if "collapsing to" fubars something, which it shouldn't.
    Yeah, a .env file and a skinData mesh can copy skin info based on position or vertex ID, Also with bone re-interpretation
    [ex. you could map a 'finger_L1' bone to 'stomach' if you really wanted to]. BUT max's default skinning transfer from these things only works on the entire object vs. a selection.


    I actually had to write a script for stuff like this at work, but only because our rigger put a bunch of bones right ontop of eachother :poly127:
    Pasha's cool script is based on bone name instead of position, so you'd have to paste in the bone names of your particular rig :)
    ----
    as for your maxscript question:
    3ds max has 2 main types of scripts, .ms and .mcr. .ms's require you to run the script and .mcr's load a script into its own little bubble When you start max that you can call through hotkeys, toolbar buttons, quad menus, etc.

    so drop the .mcr into Program Files/Autodesk/3dsMaxXXXX/ui/macroscripts/
    restart max, then go to Customize->Customize User-Interface and navigate to the proper 'Category' which in pasha's script is listed as 't2'
  • fenderbender
    Options
    Offline / Send Message
    fenderbender polycounter lvl 15
    hi funky, i already tried your flow, and here's what I've got.after i mirror it, my mesh (just the face part) does not follow the opposite weight.the vert goes every where.have you encounter this kind of problem before?

    edit
    i already reset xform and repeat the process.but it still does not follow the opposite weight
  • Funky Bunnies
    Options
    Offline / Send Message
    Funky Bunnies polycounter lvl 17
    if that's the case, it could be that you also have bones right ontop of eachother (which would mean you probably need a scripted solution) or your Mirror Tresh. is set too high
  • pasha_sevez
    Options
    Offline / Send Message
    pasha_sevez polycounter lvl 13
    Pasha's cool script is based on bone name instead of position, so you'd have to paste in the bone names of your particular rig :)
    ----
    as for your maxscript question:
    3ds max has 2 main types of scripts, .ms and .mcr. .ms's require you to run the script and .mcr's load a script into its own little bubble When you start max that you can call through hotkeys, toolbar buttons, quad menus, etc.

    so drop the .mcr into Program Files/Autodesk/3dsMaxXXXX/ui/macroscripts/
    restart max, then go to Customize->Customize User-Interface and navigate to the proper 'Category' which in pasha's script is listed as 't2'

    The main feature of my script is that you can mirror biped weights but simple bones hierarchy as well.
    Sometimes putting script in Max's home dir makes script not recognized/loaded (i had some issues) the try directory

    C:\Users\<account name>\AppData\Local\Autodesk\3dsmax\200x - xxbit\enu\UI\usermacros
Sign In or Register to comment.