PDA

View Full Version : Scripting rig controls


SkullboX
07-28-2008, 04:03 PM
I've been rigging for a while now, and will soon begin production on a short that will require 3 nearly identical rigs on different sized and proportioned characters. While each character will obviously have some unique attributes that will require extra controls, the base rigs will consist of the same objects.

I've heard a lot about scripting rigs, but haven't yet had the chance to look into it. From what I've heard is that it's a relatively straightforward procedure, which is not to say I think it's simple.

Basically what I would want is having to manually position a skeleton to fit a certain character (always with the same joint names, obviously), then run a script that will setup all the controls. Now I know there are resources available on this but as I'm pretty new to this aspect of rigging I have some trouble where I should begin.

Any resources on this would be very much appreciated. I'd prefer online tutorials and the likes especially as a starting point, but books on the subject are welcome too.

Thanks in advance!

Buexe
07-28-2008, 09:35 PM
Starting a scripted rigging project can take a looooong time. Prolly it would be a good idea to make it simple and efficient. If you haven`t written a procedural rigging system you should know that there a million little things that can or want to be tweaked and thought of, from naming and grouping stuff to auto-position your pole-vectors etc. So it is really a big topic and there are numerous resources available, but one that really sticks out for me is the Jason Schleifer "Animator friendly rigging" series. It is well explained and there are already tons of scripts that should be extremely valuable to anybody diving into rigging and especially scripted rigging, like decent IK splines etc. Other than that I only can say, keep it modular and check out python and/or c++ to get access to matrix calculation, although that is probably already too steep, I think that is where MEL has its limitations, because not all space position problems can be solved with the rigger`s favourite line of code:
delete`parentConstraint`;
Cheers :cool:

digitalandrew
07-28-2008, 09:59 PM
Hi
As Buexe said, there can be many details to consider when script building rigs. I have recently scripted up a re-useable biped character that uses a base scene to build upon. The base scene lets you reposition and proportion the skeleton to any sized biped. The scripts then uses that to construct identical rigs.
As for tutorials and such, it's all MEL scripting. The thing with scripting this kind of thing is that it's a written verison of what you would usually do in the interface. Certainly is a time saver in the long run. You find that you will end up with a load of shorter procedures that you can use elsewhere in other situations and a string of little tools that prove invaluable along the way.

cool :D

SkullboX
07-29-2008, 01:14 AM
Thanks a lot for the responses guys!

...but one that really sticks out for me is the Jason Schleifer "Animator friendly rigging" series. It is well explained and there are already tons of scripts that should be extremely valuable to anybody diving into rigging and especially scripted rigging, like decent IK splines etc.
Looking at the features list it looks like these books really only deals with the rigging techniques (/ philosophy). I'm already pretty proficient in rigging itself and have (since I'm mainly an animator) used pretty much the same angle in creating my rigs, which only have what I really need, and control the way I animate (and it does use fully functional IK splines :))... so I'm not looking for new ways to rig... or am I missing something from the descriptions on the autodesk site?
Other than that I only can say, keep it modular and check out python and/or c++ to get access to matrix calculation, although that is probably already too steep, I think that is where MEL has its limitations,

What exactly do you mean with modular? Seperate scripts to for different parts of the body (one script to create an IK spline, for example, and another for the hand controls) or different stages of the rigging process (one to create control objects, one to create the constraints, one to connect those, etc)?

While we're on the subject... I don't know how to script, but I do rely quite heavily on expressions rather than nodes where possible... for instance most stretchy Spline IK rigs I've come across use a wide variety of nodes (multiply/divide, condition and blendcolor nodes) while all of it, or at least the functionality I needed out of it, I found much easier and certainly much faster to create using a simple series of expressions, it looks something like this (this is the neck stretchy spline IK):

neck_01.scaleX = max(.1, curveInfo2.arcLength / 3.091450494345613);
neck_01.scaleY = ( 0.5 + ( sqrt(1 / neck_01.scaleX) * 0.5 ) * ( head_ctrl.Squash / 10 ) ) + ( 1 - ( head_ctrl.Squash / 10 ) );
neck_01.scaleZ = ( 0.5 + ( sqrt(1 / neck_01.scaleX) * 0.5 ) * ( head_ctrl.Squash / 10 ) ) + ( 1 - ( head_ctrl.Squash / 10 ) );
neck_02.scaleX = max(.1, curveInfo2.arcLength / 3.091450494345613 );
...


Now this basically works the same just without the nodes, and it's pretty simple math eventhought the syntax might not be optimal, it works and is easy to alter. The problem is I've never seen anybody do it this way, so I was wondering if this is a bad idea, or worse than connecting a bunch of nodes manually... because it's pretty modular as I certainly wouldn't have to redo any of the stretchy spline IK setup for different rigs save for creating the curveInfo node.
The thing with scripting this kind of thing is that it's a written verison of what you would usually do in the interface. Certainly is a time saver in the long run.
That's what I thought, only it seems I was still underestimating it. I'm not really looking to build a full and flexible system, maily looking for ways to speed up the rigging process of creating 3 nearly identical rigs... so it doesn't necessarily have to do every step, but indeed mainly the (repetitive) actions you usually do in the interface (which I know makes it sound more simple than it will turn out to be). So any good for starting point for this lower goal would also be much appreciated!

Thanks again,

KielFiggins
07-29-2008, 06:21 PM
Ive been working on a Auto Rigging script for several years now and each new character I add something new to the functionality or options available.

What I would suggest starting out is keeping it modular, so that a single script is called to create the arm setup, leg setup, spine and so forth. This will allow you to swap out differnt types of arms, make it easier to update / maintain individual scripts and make it a whole lot easier to track down problems.

THis is just my way of working, but heres typically how I interact with my Arm script.

1. Select Shoulder joint
The script then knows to define the selection as a shoulder, pick walk down and define the elbow, pick walk down again to define the wrist (also an inital pick walk up to defien the clavicle).

2. Provide a Name/Side: Such as "L_" for left or "LF_" for Left Front for multi arm character

3. Select Chest Control
Defines selection for parenting purposes of the arm rig.

4. Create Rig
Then all the nuts and bolts happen: IKFK, forearm twist, ctrls made and aligned, channels locked, ect. Leaving you with consistant and quickly made Arm. You can start getting fancy by taking the TY position of the shoulder or length of the joints and determining how large to make the ctrls.

Hope this helps.

Buexe
07-29-2008, 06:39 PM
Thanks a lot for the responses guys!


Looking at the features list it looks like these books really only deals with the rigging techniques (/ philosophy). I'm already pretty proficient in rigging itself and have (since I'm mainly an animator) used pretty much the same angle in creating my rigs, which only have what I really need, and control the way I animate (and it does use fully functional IK splines :))... so I'm not looking for new ways to rig... or am I missing something from the descriptions on the autodesk site?

There are already quite a few useful scripts that come with it, tha`s why I pointed it out.


What exactly do you mean with modular? Seperate scripts to for different parts of the body (one script to create an IK spline, for example, and another for the hand controls) or different stages of the rigging process (one to create control objects, one to create the constraints, one to connect those, etc)?

With modular I mean don`t write a series of commands that will work on one specific rig. Also create a script for legs, for positioning handles, etc. so you can have a simple flow for rigging all your characters, something like this:
Pseudocode:
list = character1.mb, character2.mb, character3.mb

for ( file in list )
{
open file;
armScript left;
armScript right;
legScript left;
legScript right;
torsoScript;
headScript;
cleanUpScript;
save file;
close file;
}

done
I hope you get my point, many people write scripts that rely on certain names are really just a bunch of commands. But if you want to take it a step further so you can reuse your scripts on the next project without too much hazzle, make them modular and generic as much as possible, for example here are two different characters, the three orange dots symbolize a stretchy IK limb that was created by the same script. I was able to do that because the script just wanted the names of the joints and did the rest by itself:
http://www.janberger.de/files/images/tmp/elefant_rig.png


While we're on the subject... I don't know how to script, but I do rely quite heavily on expressions rather than nodes where possible... for instance most stretchy Spline IK rigs I've come across use a wide variety of nodes (multiply/divide, condition and blendcolor nodes) while all of it, or at least the functionality I needed out of it, I found much easier and certainly much faster to create using a simple series of expressions, it looks something like this (this is the neck stretchy spline IK):

neck_01.scaleX = max(.1, curveInfo2.arcLength / 3.091450494345613);
neck_01.scaleY = ( 0.5 + ( sqrt(1 / neck_01.scaleX) * 0.5 ) * ( head_ctrl.Squash / 10 ) ) + ( 1 - ( head_ctrl.Squash / 10 ) );
neck_01.scaleZ = ( 0.5 + ( sqrt(1 / neck_01.scaleX) * 0.5 ) * ( head_ctrl.Squash / 10 ) ) + ( 1 - ( head_ctrl.Squash / 10 ) );
neck_02.scaleX = max(.1, curveInfo2.arcLength / 3.091450494345613 );
...


Now this basically works the same just without the nodes, and it's pretty simple math eventhought the syntax might not be optimal, it works and is easy to alter. The problem is I've never seen anybody do it this way, so I was wondering if this is a bad idea, or worse than connecting a bunch of nodes manually... because it's pretty modular as I certainly wouldn't have to redo any of the stretchy spline IK setup for different rigs save for creating the curveInfo node.)?

I think that is not an issue, people may argue that nodes will calulate faster, but if it is only marginal, than creating and maintaining expressions is absolutely okay.


That's what I thought, only it seems I was still underestimating it. I'm not really looking to build a full and flexible system, maily looking for ways to speed up the rigging process of creating 3 nearly identical rigs... so it doesn't necessarily have to do every step, but indeed mainly the (repetitive) actions you usually do in the interface (which I know makes it sound more simple than it will turn out to be). So any good for starting point for this lower goal would also be much appreciated!

Thanks again,
Yeah keep it simple, a full-featured system can take you a looong time, so focus on the stuff that seems most important. Sometimes projects and timetables don`t allow lengthy r+d phases, but I spent some time on every project to extend my MEL arsenal and now can do a lot of stuff pretty quickly by hacking stuff together that I already have.
Cheers!

Kris00
07-29-2008, 09:38 PM
If your going to start making one, I would say look at whats already been done.
Next Step would be, create a Character Rig on a character.
Now based off of that Character Rig that you make, Split it up into seperate Sections.
For Example (Head:LtArm;LtLeg.....)
These Will be the modules or main Rig Pieces in your Code.
You will have A script that Does A arm Chain with IkFk, and anything else you want, that can be used for either the Left or Right Arm.
Keeping the Main Code in Modules Like this lets you change it quickly and add more onto what your developing later on.


I have been creating one myself for a while now so if you have any questions I'll help the best I can.

SkullboX
07-29-2008, 11:27 PM
Thanks again, great stuff! :)
What I would suggest starting out is keeping it modular, so that a single script is called to create the arm setup, leg setup, spine and so forth. This will allow you to swap out differnt types of arms, make it easier to update / maintain individual scripts and make it a whole lot easier to track down problems.

THis is just my way of working, but heres typically how I interact with my Arm script.[...]
Yeah it seems a modular system as such is the way to go... Luckily the type of rig I need is very limited in the amount of controls it requires, so it'll be easy to break down for this one (again, I would imagine).
I think that is not an issue, people may argue that nodes will calulate faster, but if it is only marginal, than creating and maintaining expressions is absolutely okay.
Thanks, and that's kind of surprising. I'd figure on the calculation front a simple mathematical expressions would be faster than premade nodes (which often do a lot more in such rigging solutions that you'd need).

If your going to start making one, I would say look at whats already been done.
[...]
I have been creating one myself for a while now so if you have any questions I'll help the best I can.
How long were you working on it until you created something that was useful, and did you already have scripting knowledge before you started out?

I was also wondering if somebody has and is willing to post some examples of how such a script would (could) look like, for example a script that created the IK or stretch on an arm. I understand this might be touchy as I understand a lot of work and research goes into it... but maybe you have some old beginner style stuff laying around you wouldn't mind showing.

Again thanks for the help!

KeesCopyright
07-30-2008, 02:49 AM
Interesting topic, and indeed some good advice especially from KielFiggins. It gave me a much better idea of how to go about something like this.

I know way too little about rigging in general so I'll keep an eye on this thread :P

isoparmB
07-30-2008, 03:57 AM
Interesting indeed. Incidentally I'm working on an automated rigging system myself.

Let me propose an approach kind of like what digitalandrew suggested: don't do it all in mel. Get a rig of yours that you feel more or less represents your basic rig and break it down into a template file which will be the basis of your auto rig system. You can then rig your joints with placement controllers to reposition them on any existing model, then have a script at the end of the placement process which takes all your joints and finalizes the rig (i.e., deletes all the placement controllers, applies IK, and anything else necessary.).

The advantages of this style are:
1. The overhead on the mel side of things is pretty low. You basically have a number of procedures which you execute at the end of the rigging phase to just assemble the existing parts and that's just about it.
2. This supports modularity as you can further break down your rig so that you have separate files for left and right arms, hands, legs, spines, head and neck setups, and even rig different versions of each body part (as long at the connection points between parts remains consistent, you can rig in virtually any style you choose). Again, the net result is low mel overhead as you wouldn't have to change your procs to accomodate a new kind of arm or leg setup.
3. This lets you interactively create your base template rigs in the viewport, which is easier for some people (me included).

To support this workflow, ther are obviously some things you'd need to work out. For one, on the template files you more or less can't have ik (for repositioning purposes), so you basically have to have a script which knows which joints have to have IK. You're setup will also have to temporaily control the controllers which will drive them later on, sort of like controllers will have to ride on certain joints, then be unconstrained later on and will then contrain those joints to themselves. There are a number of things which need to be done under the hood after joint placement, so for these purposes, you may have to define something which I'd like to call a Tag system, which is basically a node named in a very particular way and with attributes which are connected to the nodes they're supposed to effect later on. Think of it like a post-it pad put on top of the parts with preset instructions on what to do to these things. The script looks for these tag nodes, then based off the name of each tag it performs specific operations on the connected nodes, then deletes the tags later. This further adds on to the modularity thing because no matter what kind of arm or leg you set up, you can still use the same basic set of tag nodes and tag scripts to perform operations. This method is almost like lego.

Basically there would then be three parts to this methodology:
1. The repositionable rig/s complete with controllers and joints
2. The Tag nodes connected to the appropriate controllers and joints, that tell the script to do this and that to the connected objects
3. The assembly script, which is a bunch of procs which perform pre-defined operations on the controllers and joints and on each type of Tag node it finds

isoparmB
08-01-2008, 03:41 AM
As an example of my earlier post, here is a sample file of a repositionable arm setup. The complete setup is an stretchy IK/FK setup with volume preservation control, this one is just the IK segment.

The rig itself does not have IK on it at the moment, that will be added on later by a proc which looks for any IKRP tags. But the reposition controllers take into account proper joint orientation (now set at xyz orientation).

http://rapidshare.com/files/133977471/sample.ma.html

The main idea is it dosen't matter what kind of rig you're trying to auto-create. It's possible to design a sytem flexible enough to accomodate virtually any kind of rig with minimal mel overhead and maintenance as long as the operations used to create the rigs are reusable and can be broken in to small chunks (i.e., ikRP and ikSC creation, contraints, parenting, etc).

KeesCopyright
08-01-2008, 05:47 PM
This is way over my head so please excuse me if it is a stupid question, but here goes;

It seems to me that most characters would be humanoid and that a base rig is the way to go since im not (yet) concerned with building very specialised rigs. As demonstrated in the 2008 features in this movie tuttorial :
http://download.autodesk.com/us/maya/videos/5_mk8_Web_Rig.mov it should be easier to move bones around even in a bound skeleton. How insanly dificult would it be to make a script that inverts the power of the constraints (meaning the control curve is now constrained by the joint position) reposition the joints, then have a script or maybe a locator system that will reset the joint positions and invert the constraints again? Would this be impossible with IK?

I opened the file provided by isoparmB but i have no idea how to handle the IK FK switching and quite frankly I dont see why you would have IK FK switching if you can build a stretchy rig like this. But im not the traditional animator i guess.

isoparmB
08-02-2008, 01:37 AM
If you try to move joints with IK they tend to resist manipulation, once the IK's been keyed or parented, so you have to apply the ik after the repositioning phase.

Regarding controllers temporarily riding on the joints they're supposed to control, it not that insanely difficult. You just have to make sure that your controllers each have an offset group as a parent with the same pivot point (that's because you generally want the transforms of your controller zeroed out, the offset groups usually have the non-zero transforms which is perfectly fine), and that the controller and group have the same transforms as the joint you want to control (so that there is no snap when you constrain the joints to the controller, even without maintain offset).


You constrain the offset group to the joint and give the point and orient constraints a searchable and consistent name, something like "riderTempPoint_index1Jnt" and riderTempOrient_index1Jnt". So that a script knows to later switch the constraint situation, the method I use is to create a node of type unknown called a rider tag, something named like "riderTag_indexCtrl1" with a few custom attributes. In this situation I have .constraintTarget, .pointConstraint, .orientConstraint, and .scaleConstraint. I then connect the .message attribute of the joint to riderTag_indexCtrl1.constraintTarget, then connect the .message attribute of the controller to .pointConstraint and .orientConstraint (.scaleConstraint is there in case you need it too).

Later on when I finish the rig I will have it do two things for controllers with rider tags.

1. Delete every constraint for the rider controlls: delete `ls -r true "riderTempPoint_*" riderTempOrient_*"`; Possibly after, also lock the formerly constrained attributes as I know they belong to offset groups which should never be controlled.

2. Find all rider tags using the ls command, and put them through a for loop which looks for the connected objects and peforms constraint operations on the constraint target of each, based on what constraint types have been connected to each tag node.

and that's basically it.

I use this tag process throught the rig to perform operations like temporarily disconnecting attributes during joint reposition, ik creation, joint placement, reparenting, basically whatever function I need to complete the rig which I can reuse can be turned into a tag. Each uniquely named tag would have its own procedure in my script, the trick is keeping each tag's function specific and finding the correct order of tag operations to execute first. This way there is never a need to specify any particular node on any rig explicitly, so you have less code to maintain.

So the overall workflow is to basically "Rig" your rigged character so that you can reposition it's joints and setup tags that tell the script how to reassemble assemble it again. You rig manually ONCE, then configure the rig so you can reuse it anytime with all the appropriate controllers already set up.

SkullboX
08-02-2008, 02:07 PM
Well first of all thanks a lot for the detailed explenation. I've been tearing your rig apart for the past two days and along with the info here trying to figure out how it all works. I get the general principle, but not some of the vital inner workings of the rig which I hope you can clear up so my feeble unscripted brain can keep up a little. :)


First of all I can't seem to save it to mb due to some nodes maya doesn't seem to understand (I'm using 2008 ext 2)... I don't know if that indicates there might be a problem though, just making sure.

The main issue for me in understanding this rig is understanding how the local joint orientation will be realised after positioning. Obviously after positioning the crosshair controllers the local joint orientation not have been changed, so they have to be oriented towards the position of their new children. I assume the indicator curve objects will show how the orientation will look like... but shouldn't the joints be orient constrained to the indicator objects in that case?

Again regarding orientation, I can't seem to get the arm into a single plane easily (like IK would), shouldn't that be default... or is this handled by scripts? I can get close using the 'down axis pivot' attribute manually, but that'd never result in the two arm joints being lined up in a perfect plane.

I also do not get the L_handRot, which doesn't follow the joints at all, but in turn its orientation IS affected by joint position.

I suppose the clavicle setup is a preference. I personally generally do not use more than a single bone for the clavicle, which is always oriented straight towards the shoulder joint, I guess yours is simply more ellaborate?

I also saw that 'L_clavTipJntConst1' (group) has a pivot with a huge offset, is that meant to be like that?


So I hope this is what I do get:
This rig will only take care of the local positioning of all the joints, and has all the right data so that the orientation can easily be extracted from the controllers to the joints, as well as tags so any script can easily detect what's what in order to set up the 'advanced' controls.

A script will take care of the orientation of the joints and gets rid of all the objects and constraints that are only necessary for the initial rigging process.

The script will also create the right control objects and sets them up accordingly (IK, stretch, constraints, twist bones?).


I hope I get the general idea of how it works, and I hope my questions aren't too stupid as I have tried to get my head around this, but I guess I still fail to understand some of the vital inner workings of this process.

Thanks again for the awesome help!

isoparmB
08-03-2008, 02:11 AM
First of all I can't seem to save it to mb due to some nodes maya doesn't seem to understand (I'm using 2008 ext 2)... I don't know if that indicates there might be a problem though, just making sure.

This is a common issue where maya sees some plugin or unknown nodes and is trying to preserve the info (i.e., it won't allow you to save in the other format). Common work around is to open a new file, save it with the name you want and in the desired format, and just import the file in (namespaces turned off) and save it again.


The main issue for me in understanding this rig is understanding how the local joint orientation will be realised after positioning. Obviously after positioning the crosshair controllers the local joint orientation not have been changed, so they have to be oriented towards the position of their new children. I assume the indicator curve objects will show how the orientation will look like... but shouldn't the joints be orient constrained to the indicator objects in that case?


In the case of my setup, no. When the joints are repositioned, the joint orientation is automatically updated (you can check the attribute editor of each joint to verify this). Basically, the joint orientation value of each joint is dependent on the parent joint, and that joint nodes are treated specially since you can freeze transform it's rotations, but not it's translations (meaning joints all return to the origin when zeroed if they're in world space), and that joints are the only different type of transform because they have a joint orientation attribute. I figured if you use a curve with the pivot at the origin when zeroed, I could recreate the correct rotation values for each joint. All you need is a curve that is aim constrained to the next curve down the line, which is parented to group which in turn is constrainted to either the joint above the curent one, or the indicator curve above the current one. You then connect the rotation values of this curve to the joint orient values of the current joint. You can then use the offsetX of the aim to rotate the pivot along the proper facing axis (in an xyz orientation setup).

In a nutshell the setup is designed so you don't have to worry about orientations, once deleted, the controllers will have left the proper joint orientation values. Take note, though, that in the arm setup I gave, if you were to create the mirrored portion the aim and up axis of the aim on the indicator curves would have to be in the negative (instead of the current positive), as this is the same natural orientation you see on mirrored joints set created using the behaviour mode.


Again regarding orientation, I can't seem to get the arm into a single plane easily (like IK would), shouldn't that be default... or is this handled by scripts? I can get close using the 'down axis pivot' attribute manually, but that'd never result in the two arm joints being lined up in a perfect plane.


Hmm yes, I see your point. Let me look into that. I could have each aim constraint on the arm indicators have an up object based of the positions of the joints.


I also do not get the L_handRot, which doesn't follow the joints at all, but in turn its orientation IS affected by joint position.


Whoopsie. :) It should be following the arm indicator. It seems I deleted some things I ought not have deleted.... I'll get back with a revised example along with the arm axis indicator.


I suppose the clavicle setup is a preference. I personally generally do not use more than a single bone for the clavicle, which is always oriented straight towards the shoulder joint, I guess yours is simply more ellaborate?


Again, the rig is only an example of my personal rig. The main principle is that you can use the repositioning ang rigging principles for any type of setup, its kind of like rig-agnostic.

Regarding my clav setup, it's designed rotate up once the guide ik moves beyond a certain point, and stop once it becomes parallel with the shoulder. This of course has nothing to do with the rig automation principles in general.


I also saw that 'L_clavTipJntConst1' (group) has a pivot with a huge offset, is that meant to be like that?


Hmm. Let me check up on that as well. My original doesn't have any offsets. Are you talking about constraint offsets, or do you think the pivot is really off?


So I hope this is what I do get:
This rig will only take care of the local positioning of all the joints, and has all the right data so that the orientation can easily be extracted from the controllers to the joints, as well as tags so any script can easily detect what's what in order to set up the 'advanced' controls.

A script will take care of the orientation of the joints and gets rid of all the objects and constraints that are only necessary for the initial rigging process.

The script will also create the right control objects and sets them up accordingly (IK, stretch, constraints, twist bones?).


The orientation bit is already handled, the script will basically just delete the repositioning controllers (maybe break the constraint connections first) search for all tags of each type and run them through a procedure for stuff like ik creation, reparenting, reconnecting certain attributes (you need this if certain nodes cause a cycle when you're setting up the reposition controllers), deletion of temporary constraints, and that's it. The script by design should never be doing anything fancy other that repositioning your rig, because once it gets complex it becoming less generic. The main idea of this system is that it should be able to handle ANY kind of rig and be able to reuse it successfully.

The advanced controls you're talking about will be set up by you. You'll basically have to rig using your standard rigging style ONCE and then make that your reusable template. You have to set up all your controllers, special control setups like clavs, ik/fk, set driven keys and any set up which you may require. And after that, you'll have to "Rig" your rig so you can repostition it (i.e, setting up reposition controllers, setting up Tags, this is the portion where much of your leg work and problem solving will be done, as opposed to writing code for auto scripting in some systems). You can then save this out and it becomes part of your personal rig library. The only things this system requires:

1. Offset groups for your controllers with identical transforms and pivots as their child controller, so repositioning them will be possible.

2. That all joints be properly oriented and that if possible that the xyx orientation be used (though it is feasible to have a different axis facing down the joint chain).

3. That any character specific naming be avoided (like prefixes. For practicality purposes, these should be added on later. You don't want to have to rename a template file named bubba_ every time).

And some things it doesn't require but would make your life eaiser:

1. Consistent naming of bones and controllers. If you reuse the rig more than once, this will be a big help when you have to troubleshoot or do mel operations on your charactes.

2. Have character sets applied on the characters. Again if you reuse a template file, you can transfer the animation of one character to another because you're certain they have the same number of attibutes.

3. Organized hierachy. I usually put Joints and Controllers all under their own group, and have a mainGrp node to parent them all under (that's just me of course, whatever works for you is fine). It makes finding things a whole lot easier, and it's neater than having your characters innards all over the place in the hypergraph.

And the only thing this system does is:

1. Repositions joints with proper orientation

2. Reapply IK.

3. Remove repositioning contraints on controllers and lock the previously constrained transforms, also delete any utility nodes which were used for repositioning purposes.

4. Reconnect any connection that might have caused cycles in the repositioning process (I get this in stretchy joint setups).

5. Reparent certain parts of your rig to each other (in the case of your cutting up you character into arms, hands, legs, feet, spines and head template files).

And that's it. The system will basically consist of an assembly script, a library of template files and some shelf tools you can use to apply tags easily when your making template files.


I hope I get the general idea of how it works, and I hope my questions aren't too stupid as I have tried to get my head around this, but I guess I still fail to understand some of the vital inner workings of this process.

Thanks again for the awesome help!

Hey no problem, helped me clarify some of my thoughts as well. Hope it help you too. :)

As an afterthough though, I may have to write a procedure that gets the connections on an existing IK before I delete it. I realize that some people use no flip knees and elbows and that's something this system isn't handling at the moment (you have to delete iks in the current system for repositioning.). Or it might be possible to keep the previous IK. I'll have to rethink this.


I opened the file provided by isoparmB but i have no idea how to handle the IK FK switching and quite frankly I dont see why you would have IK FK switching if you can build a stretchy rig like this. But im not the traditional animator i guess.


I usually handle IK FK switching via a three joint hierarchy system (one IK, one FK, one controller hierarchy), using just orient and scale constraints (no point, I found that the inbetweens when switching form ik to fk looked better with just orientation. Note on orient constraints though, best use shortest distance for interpolation). The weights are then Set driven keyed to a float attribute. I put FK in because in some situations it's best to directly rotate and arm (for swinging actions). Again, this is completely separate from the auto rig system I'm suggesting and is completely optional.

Buexe
08-03-2008, 09:55 AM
This is a common issue where maya sees some plugin or unknown nodes and is trying to preserve the info (i.e., it won't allow you to save in the other format). Common work around is to open a new file, save it with the name you want and in the desired format, and just import the file in (namespaces turned off) and save it again.

or just run this command:

delete `ls -typ "unknown"`;

isoparmB
08-03-2008, 01:26 PM
Actually in this case, you can't delete all nodes of type unknown as they are being used as tag nodes. I know I can use a different node type such as a transform, but it seemed to make sense to use unknown nodes at the time as they don't add clutter in the DAG view of the hypergraph, and they wouldn't be confused with any other node type used for rigging (unless someone else uses unknown type nodes).

isoparmB
08-04-2008, 03:09 AM
Apologies for the previous upload, I discovered some "errors" due to deletion which caused some controllers to not follow the joints. I've also incorporated the suggestion of Skullbox to have the shoulder and the elbow orientation follow a single plane. The joint orientation is correct now (previous file had some constraints on an indicator missing).

http://rapidshare.com/files/134673643/sample.ma

SkullboX
08-05-2008, 02:34 PM
Apologies for the previous upload, I discovered some "errors" due to deletion which caused some controllers to not follow the joints. I've also incorporated the suggestion of Skullbox to have the shoulder and the elbow orientation follow a single plane. The joint orientation is correct now (previous file had some constraints on an indicator missing).

http://rapidshare.com/files/134673643/sample.ma

Yeah this is indeed like I imagined it should have been working, and it's working great. The original .ma file didn't seem to work all that well as the bones didn't automatically get oriented at all. The arm setup also works as I thought it would have.

The only thing I don't like is how the hand orientation is dependent on the position of the wrist... again this is a personal preferece (like the shoulder setup, I hate automated shoulders when animating personally).

There's just two things I don't have a clear idea about at this point.

First of all how would the different parts of the system be combined in the end? Wil lthis be done manually, or also with a script?

I'm also very interested in how the scripting side would look like, that will recreate the controllers and reapply the IK. While I might not get the actual commands I'll probably get a better idea of how to break down the rig. I understand you might not want to share a functioning rig, but you might have a more lengthy example even if it's for something totally different you are willing to share.


Again thanks a lot for all the detailed help, especially Buexe and IsoparmB. This is a great resource and I like a lot of the suggestions from both. I've begun breaking down the rig in a modular way and have started picking up the basics of scripting. I hope it will prove to be useful in production which will start fairly soon, but I do realise that it might take slightly more than an upcoming project to truly get the benefits out of this. I will probably return to this thread many times while I'm breaking down and building up the rig again and hopefully post promising progress at some point as well.

Buexe
08-05-2008, 10:33 PM
First of all how would the different parts of the system be combined in the end? Wil lthis be done manually, or also with a script?.

in the example dummy code I provided earlier there is a cleanup script mentioned. The way I do it is that there is a single proc for each major limb (left arm, right arm, etc) this creates all the nodes and connections and one or more groups of handles, joints or whatnot. This would make the outliner look rather messy and difficult for other folk to work with that scene. So there is this cleanup script that wil take the groups created earlier and group all the stuff accordingly, put it in layers etc. For example let`s say there is this command to create an arm:

jbCreateArm shoulder_Lft_Joint elbow_Lft_Joint wrist_Lft_Joint;

Due to the way my scripts work there will be handles called Shoulder_Lft, Elbow_Lft etc. and they will be parented to groups called Shoulder_Lft_Grp, Elbow_Lft_Grp etc all named automatically. So my cleanup script wil take those Groups and put them into the appropriate group under my main character node, which will usually be some sort of locator. There I usually have three main groups:

mainCharacter
__joints
__handles
__meshes

The names are pretty self explanatory of what goes where and it is just an example of how one could do it. Just have soe sort of system and stick to it and things will get easier over time for everybody involved.

Another important step that happens in my cleanup script is that the character nodes are created there, because at that point it is safe to assume that all handles with all attributes exist and can be put together into the character set structure one would like to have. It s very important to add attributes into a character set in the same order if you need to redo the character script, because otherwise if you have animation scenes with referenced chracters and those attributes are not in the same order anymore, you will not make yourself friends in the animation department ; )


Again thanks a lot for all the detailed help .
no problem, I hope I could help a little bit. cheers!

isoparmB
08-06-2008, 06:04 AM
First of all how would the different parts of the system be combined in the end? Wil lthis be done manually, or also with a script?


With regards to parenting and reconnecting nodes, this will be handled by a procedure which looks for all parentEventually tags. It's basically an unknown node with a boolean attribute named .parentThis and a string attribute named .parentTo. .

Say you have two files. One of them is an arm setup, and another is a spine setup. You want to parent the joints of the arm at a particular point on the spine (or a group constrained to that joint in my case). The parentEventually tags will be in the arm setup file, and the joints and controllers to be parented will each have their own tag and will be connected to them via their .message attributes. The .parentTo attribute will have the name of the node in the other file to which these nodes will be parented to. This requires that, when you setup the two files, you know in advance where your arm should be parented. So you have these two files in one scene, and your run the parentEventually proc, which find all unknown nodes named in a particular way, checks the connections of each tag, then checks the .parentTo attribute to see if an object by that name exists (checks first to see if more than one object by the name exists in all namespaces). The script will also lock the child's transforms after parenting. The proc will look like this:

global proc RM_parentEventuallyTagsProc ()
{
string $list[] = `ls -et unknown -r true "parentEventuallyTag_*"`;
string $item;
string $parentTarget;
string $parentDestination;
string $possibleDestinations[];
string $tokenResult[];
string $attrs[] = {".translateX", ".translateY", ".translateZ", ".rotateX", ".rotateY", ".rotateZ", ".scaleX", ".scaleY", ".scaleZ"};
string $attr;

for($item in $list)
{
$parentTarget = `connectionInfo -sfd ($item + ".parentThis")`;
tokenize $parentTarget "." $tokenResult;
$parentTarget = $tokenResult[0];
if($parentTarget != "")
{
$parentDestination = `getAttr ($item + ".parentTo")`;
$possibleDestinations = `ls -r true $parentDestination`;
if(`size $possibleDestinations` == 1)
{
parent $parentTarget $possibleDestinations[0];
for($attr in $attrs)
{
if(`connectionInfo -sfd ($parentTarget + $attr)` == "")
{
setAttr -lock true ($parentTarget + $attr);
};
};
delete $item;
}
else if(`size $possibleDestinations` == 0)
{
warning ("\nCould not parent " + $parentTarget + ", " + $parentDestination + " does not exist.");
}
else
{
warning ("\nCould not parent " + $parentTarget + ", " + $parentDestination + " has more than one occurance.");
};
};
};
};


Reconnecting attributes between nodes uses a similar technique, employing it's own connectEventuallyTag and procedure, as well as the IK creation bits, etc.



I'm also very interested in how the scripting side would look like, that will recreate the controllers and reapply the IK. While I might not get the actual commands I'll probably get a better idea of how to break down the rig. I understand you might not want to share a functioning rig, but you might have a more lengthy example even if it's for something totally different you are willing to share.


Again regarding controllers, in the system I'm proposing those should already be present on your base rig, meaning the system will simply take the controllers and joints from your rig and just make them repositionable. It's easier to think of it as a rig repositioning system rather than an auto rigger. SDK's, IK setup, any constraint setups will all be done by you initially. And only once. You then take your rig and apply positional controllers and tags so that you can reposition the joints and controllers, and have the assembly script know how to put your rig back together again.

This configuration part is the bit that takes most of the time, becuase you have to figure out how to best setup the reposition controllers, temporary constraints and utility nodes, and tags so that the rig retains all your rig functionality. This will rely mostly on how complex your rig is in the first place. The example I gave is just a sample of one rig (it takes into account my personal clav system), the repositional controllers on your rig can look completely different because they will rely on how your rig works.

All you have to know is that, to reassemble your rig, the script will go through these operations in this order, irregardless of what kind of rig you have, so you basically have these constants to think about:

finalize jointOrientation by breaking joint orientation from the reposition controls
delete temporary utility nodes used for rig or joint repositioning
parent nodes to each other
delete placement constraints (and lock the transforms of previously constrained objects that were not joints)
reconstrain joints to controllers
reapply ik
delete reposition controller groups
(optional, if you have sets) Connect the character sets of each body part in a particular order under one main character set.

Based on those parameters, you have to set up your repositional templates using these bits to play around with. Take note that they should be named predictably so that the script knows about them:

Temporary multiplyDivide and plusMinusAverage nodes (usually for joint placement)
Temporary constraints
Positional controllers that also influence joint orientation
parentEventuallyTags
connectEventuallyTags
riderTags (for controllers to temporarily follow the joints they control)
ikSC and ikRP tags

To put it another way, as in modular script based auto riggers, think of each part of the rig as separate pieces. You have arms, legs, hands, feet, a spine and a head. You can rig each part whatever way you like (taking into account some guidelines, like offset groups for controllers and no sdk's directly on joints), but where those parts meet up to form a whole rig they have to be consistent.

Another afterthough which I just realized. If you use this system it's best not to use any form of SDK animation directly on joints in your rigs, but instead have the joints constrained to groups or controllers which have the SDK animation applied there. Also, additional ideas would be to have a node where all the joints that should be used for bind skinning can be connected, as in some rigs not all joints are used for skinning (if anyone has to use your template, you want to make sure they skin the right joints).

Sorry about the IK proc, I can't share it because I haven't written it down yet. :) But the basic idea is in the parentEventually example I showed. You basically derive the arguments you should use for IK creation from the connections found on each ikSCTag and ikRPTag you find.


Another important step that happens in my cleanup script is that the character nodes are created there, because at that point it is safe to assume that all handles with all attributes exist and can be put together into the character set structure one would like to have. It s very important to add attributes into a character set in the same order if you need to redo the character script, because otherwise if you have animation scenes with referenced chracters and those attributes are not in the same order anymore, you will not make yourself friends in the animation department ; )


You hit a very important point here Buexe. In any rigging system animation transfer and non linear animation tools are important, so whatever system you decide to adapt it's always best to have consistent character sets.

For a rig repositioning system, if your dealing with different rig versions of, say an arm rig, it's best you derive your versions from the simplest version of your arm (with the character sets already applied). That way you can build of that rig and just add attributes to the existing character set. At least you'll be able to transfer animation from a lower version to a higher version, because the attribute order for both rigs will be the same. The rule seems to be: you can add attributes, just don't rename and don't subtract attributes, and don't rename the control objects.

isoparmB
08-21-2008, 03:29 AM
Been working on these lately. Someone asked what the proc for SC and RP IKs would look like, so here they are.

The first proc will basically mark out the components that need IK to be applied to them. It works in three ways:

1. Select two joints and a parent transform for an SC ik tag

2. Select two joints, a parent transform, and a pole vector target for an RP ik tag

3. Select either an IK SC or RP Handle, the script will figure out all the connections and create a transform with all the relevant information for recreating the IK setup (you should delete the old IK once your done creating the tag).

These will just provide tags so that another script knows where to apply IK. You can also manually connect other nodes to the tags once you've created them.

global proc ikTagProc()
{
string $list[] = `ls -sl`;
string $ikTagType;
string $type[];
string $connection;
string $outConnections[];
string $tokenResult[];
string $parent[];
string $ikTag;
string $attrs[] = {"translateX", "translateY", "translateZ", "rotateX", "rotateY", "rotateZ", "scaleX", "scaleY", "scaleZ", "poleVectorX", "poleVectorY", "poleVectorZ"};
string $consts[] = {"pointConstraint", "orientConstraint", "scaleConstraint", "poleVectorConstraint"};
string $constAttrs[];
string $currentConstraint;
string $targets[];
string $target;
int $constrainedAxis[];
int $i;
int $i2;
int $i3;
string $mode;
string $destinationExclusionAttributes[] = {"startJoint", "endEffector", "ikSolver", "poleVectorX", "poleVectorY", "poleVectorZ", "translate", "translateX", "translateY", "translateZ", "rotate", "rotateX", "rotateY", "rotateZ", "scale", "scaleX", "scaleY", "scaleZ", "offset", "roll", "twist", "ikBlend"};
string $sourceExclusionAttributes[] = {"parentInverseMatrix", "rotatePivot", "rotatePivotTranslate", "rotateOrder", "offset", "roll", "twist", "ikBlend"};
int $excluded;

if(`size $list` == 0)
{
warning "/nPlease select either an Ik handle or three or four components for a new handle: Two joints, a parent, and optionally a pole vector target./n";
}
else
{
$type = `ls -st $list[0]`;
if($type[1] == "ikHandle")
{

$connection = `connectionInfo -sfd ($list[0] + ".ikSolver")`;
tokenize $connection "." $tokenResult;
$type = `ls -st $tokenResult[0]`;
if($type[1] == "ikSCsolver")
{

$mode = "ikSCHandleSelection";
$ikTagType = "ikSCTag_";
$parent = `listRelatives -parent $list[0]`;
}
else if($type[1] == "ikRPsolver")
{
$mode = "ikRPHandleSelection";
$ikTagType = "ikRPTag_";
$parent = `listRelatives -parent $list[0]`;
};
}
else if(`size $list` == 3)
{
$type = `ls -st $list[0] $list[1]`;
if(($type[1] == "joint")&&($type[3] == "joint"))
{
$mode = "ikSCComponentsSelection";
$ikTagType = "ikSCTag_";
}
else
{
warning "\n First and second selection elements require a joint.";
return;
};
}
else if(`size $list` == 4)
{
$type = `ls -st $list[0] $list[1]`;
if(($type[1] == "joint")&&($type[3] == "joint"))
{
$mode = "ikRPComponentsSelection";
$ikTagType = "ikRPTag_";
}
else
{
warning "\n First and second selection elements require a joint.";
return;
};
}
else
{
warning "\n Wrong number of arguments selected.";
return;
};
// Creation of ik Tag node, based off selection. If an IK is selected, a transform is used. If 3 or four objects for IK creation are selected, an unknown node is used.
if(($mode == "ikSCHandleSelection") || ($mode == "ikRPHandleSelection"))
{
$ikTag = `createNode -n ($ikTagType + $list[0]) transform`;
parent $ikTag $parent[0];
}
else if(($mode == "ikSCComponentsSelection") || ($mode == "ikRPComponentsSelection"))
{
$ikTag = `createNode -n ($ikTagType + $list[1]) unknown`;
};

addAttr -ln startEffector -at bool $ikTag;
setAttr -e -keyable true ($ikTag + ".startEffector");
addAttr -ln endEffector -at bool $ikTag;
setAttr -e -keyable true ($ikTag + ".endEffector");
addAttr -ln parent -at bool $ikTag;
setAttr -e -keyable true ($ikTag + ".parent");
addAttr -ln pointConstraint -at bool -multi $ikTag;
setAttr -e -keyable true ($ikTag + ".pointConstraint");
addAttr -ln orientConstraint -at bool -multi $ikTag;
setAttr -e -keyable true ($ikTag + ".orientConstraint");
addAttr -ln scaleConstraint -at bool -multi $ikTag;
setAttr -e -keyable true ($ikTag + ".scaleConstraint");

// First segment of script for storing data for recreation of previous IK setup
if($ikTagType == "ikRPTag_")
{
addAttr -ln poleVectorConstraint -at bool -multi $ikTag;
setAttr -e -keyable true ($ikTag + ".poleVectorConstraint");
};

if(($mode == "ikSCHandleSelection") || ($mode == "ikRPHandleSelection"))
{
// Add attributes to remember previous IK settings, children, parent and connections.
addAttr -ln children -at bool -multi $ikTag;
setAttr -e -keyable true ($ikTag + ".children");
addAttr -ln poleVectorX -at double $ikTag;
setAttr -e -keyable true ($ikTag + ".poleVectorX");
addAttr -ln poleVectorY -at double $ikTag;
setAttr -e -keyable true ($ikTag + ".poleVectorY");
addAttr -ln poleVectorZ -at double $ikTag;
setAttr -e -keyable true ($ikTag + ".poleVectorZ");
addAttr -ln offset -at double $ikTag;
setAttr -e -keyable true ($ikTag + ".offset");
addAttr -ln roll -at double $ikTag;
setAttr -e -keyable true ($ikTag + ".roll");
addAttr -ln twist -at double $ikTag;
setAttr -e -keyable true ($ikTag + ".twist");
addAttr -ln ikBlend -at double $ikTag;
setAttr -e -keyable true ($ikTag + ".ikBlend");

$targets = `listRelatives -children $list[0]`;
for($target in $targets)
{
$type = `ls -st $target`;
if($type[1] == "transform")
{
setAttr -lock false ($target + ".tx");
setAttr -lock false ($target + ".ty");
setAttr -lock false ($target + ".tz");
setAttr -lock false ($target + ".rx");
setAttr -lock false ($target + ".ry");
setAttr -lock false ($target + ".rz");
setAttr -lock false ($target + ".sx");
setAttr -lock false ($target + ".sy");
setAttr -lock false ($target + ".sz");
parent $target $ikTag;
connectAttr -f ($target + ".message") ($ikTag + ".children[" + (size(`listAttr -multi ($ikTag + ".children")`)) + "]");
};
};

$connection = `connectionInfo -sfd ($list[0]+ ".startJoint")`;
tokenize $connection "." $tokenResult;
connectAttr -f ($tokenResult[0] + ".message") ($ikTag + ".startEffector");
$connection = `connectionInfo -sfd ($list[0]+ ".endEffector")`;
tokenize $connection "." $tokenResult;
$connection = `connectionInfo -sfd ($tokenResult[0] + ".translateX")`;
tokenize $connection "." $tokenResult;
connectAttr -f ($tokenResult[0] + ".message") ($ikTag + ".endEffector");
connectAttr -f ($parent[0] + ".message") ($ikTag + ".parent");

// Segment used to store previous constraint information on translate, rotate, scale or pole vector, if any. If no constraints exist, check for either connection or a value.
for($i2 = 0; $i2 <= 3; $i2++)
{
for($i = 0; $i <= 2; $i++)
{
$connection = `connectionInfo -sfd ($list[0] + "." + $attrs[($i + ($i2 * 3))])`;
if($connection == "")
{
if($i2 == 3)
{
setAttr ($ikTag + "." + $attrs[($i + ($i2 * 3))]) (`getAttr ($list[0] + "." + $attrs[($i + ($i2 * 3))])`);
}
else
{
continue;
};
}
else
{
tokenize $connection "." $tokenResult;
$type = `ls -st $tokenResult[0]`;
if($type[1] == $consts[$i2])
{
$currentConstraint = $tokenResult[0];
$constrainedAxis[$i] = 1;
}
else
{
connectAttr -f $connection ($ikTag + "." + $attrs[($i + ($i2 * 3))]);
$constrainedAxis[$i] = 0;
};
};

$outConnections = `connectionInfo -dfs ($list[0] + "." + $attrs[($i + ($i2 * 3))])`;
if(`size $outConnections` != 0)
{
for($connection in $outConnections)
{
setAttr -lock false $connection;
connectAttr -f ($ikTag + "." + $attrs[($i + ($i2 * 3))]) $connection;
};
};
};
// If constraints are found, create attributes for constraint with the constraint's name.
if(($constrainedAxis[0] + $constrainedAxis[1] + $constrainedAxis[2]) > 0)
{
$targets = `listConnections ($currentConstraint + ".target")`;
$targets = stringArrayRemoveDuplicates($targets);
$targets = stringArrayRemove({$currentConstraint}, $targets);
for($target in $targets)
{
connectAttr -f ($target + ".message") ($ikTag + "." + $consts[$i2] + "[" + (size (`listAttr -multi ($ikTag + "." + $consts[$i2])`)) + "]");
};
// Common constraint's attributes section
$type = `ls -st $currentConstraint`;
$constAttrs = {"nodeState", "offsetX", "offsetY", "offsetZ"};
addAttr -ln ($currentConstraint + "_" + $type[1]) -at bool $ikTag;
setAttr -e -keyable true ($ikTag + "." + $currentConstraint + "_" + $type[1]);
addAttr -ln ($currentConstraint + "_nodeState") -at double $ikTag;
setAttr -e -keyable true ($ikTag + "." + $currentConstraint + "_nodeState");
addAttr -ln ($currentConstraint + "_offsetX") -at double $ikTag;
setAttr -e -keyable true ($ikTag + "." + $currentConstraint + "_offsetX");
addAttr -ln ($currentConstraint + "_offsetY") -at double $ikTag;
setAttr -e -keyable true ($ikTag + "." + $currentConstraint + "_offsetY");
addAttr -ln ($currentConstraint + "_offsetZ") -at double $ikTag;
setAttr -e -keyable true ($ikTag + "." + $currentConstraint + "_offsetZ");

if($consts[$i2] == "orientConstraint")
{
$constAttrs[4] = "interpType";
addAttr -ln ($currentConstraint + "_interpType") -at double $ikTag;
setAttr -e -keyable true ($ikTag + "." + $currentConstraint + "_interpType");
};

for($i3 = 0; $i3 <= (`size $constAttrs` - 1) ; $i3++)
{
$connection = `connectionInfo -sfd ($currentConstraint + "." + $constAttrs[$i3])`;
if($connection == "")
{
setAttr ($ikTag + "." + $currentConstraint + "_" + $constAttrs[$i3]) (`getAttr ($currentConstraint + "." + $constAttrs[$i3])`);
}
else
{
connectAttr -f $connection ($ikTag + "." + $currentConstraint + "_" + $constAttrs[$i3]);
};

$outConnections = `connectionInfo -dfs ($currentConstraint + "." + $constAttrs[$i3])`;
if(`size $outConnections` != 0)
{
for($connection in $outConnections)
{
setAttr -lock false $connection;
connectAttr -f ($ikTag + "." + $currentConstraint + "_" + $constAttrs[$i3]) $connection;
};
};
};
// Target weight attributes section
$constAttrs = `listAttr -k $currentConstraint`;
if(`size $constAttrs` > 6)
{
for($i3 = 6; $i3 <= (`size $constAttrs` - 1); $i3++)
{
addAttr -ln ($currentConstraint + "_W" + ($i3 - 6)) -at double $ikTag;
setAttr -e -keyable true ($ikTag + "." + $currentConstraint + "_W" + ($i3 - 6));

$connection = `connectionInfo -sfd ($currentConstraint + "." + $constAttrs[$i3])`;
if($connection == "")
{
setAttr ($ikTag + "." + $currentConstraint + "_W" + ($i3 - 6)) (`getAttr ($currentConstraint + "." + $constAttrs[$i3])`);
}
else
{
connectAttr -f $connection ($ikTag + "." + $currentConstraint + "_W" + ($i3 - 6));
};

$outConnections = `connectionInfo -dfs ($currentConstraint + "." + $constAttrs[$i3])`;
if(`size $outConnections` != 0)
{
for($connection in $outConnections)
{
tokenize $connection "." $tokenResult;
$type = `ls -st $tokenResult[0]`;
if($type[1] != $consts[$i2])
{
setAttr -lock false $connection;
connectAttr -f ($ikTag + "." + $currentConstraint + "_W" + ($i3 - 6)) $connection;
};
};
};
};
};
// Skip attributes used to mark out which axis to skip in the constraint operations for IK
if(($constrainedAxis[0] == 0) &&($consts[$i2] != "poleVectorConstraint"))
{
addAttr -ln ($currentConstraint + "_skipX") -at bool $ikTag;
setAttr -e -keyable true ($ikTag + "." + $currentConstraint + "_skipX");
};
if(($constrainedAxis[1] == 0) &&($consts[$i2] != "poleVectorConstraint"))
{
addAttr -ln ($currentConstraint + "_skipY") -at bool $ikTag;
setAttr -e -keyable true ($ikTag + "." + $currentConstraint + "_skipY");
};
if(($constrainedAxis[2] == 0) &&($consts[$i2] != "poleVectoConstraint"))
{
addAttr -ln ($currentConstraint + "_skipZ") -at bool $ikTag;
setAttr -e -keyable true ($ikTag + "." + $currentConstraint + "_skipZ");
};
};
$currentConstraint = "";
clear $constrainedAxis;
clear $constAttrs;
};

// Segment for other keyable IK attributes
clear $attrs ;
$attrs = {"offset", "roll", "twist", "ikBlend"};
for($i = 0; $i <= (`size $attrs` -1 ); $i++)
{
$connection = `connectionInfo -sfd ($list[0] + "." + $attrs[$i])`;
if($connection == "")
{
setAttr ($ikTag + "." + $attrs[$i]) (`getAttr ($list[0] + "." + $attrs[$i])`);
}
else
{
connectAttr -f $connection ($ikTag + "." + $attrs[$i]);
};

$outConnections = `connectionInfo -dfs ($list[0] + "." + $attrs[$i])`;
if(`size $outConnections` != 0)
{
for($connection in $outConnections)
{
setAttr -lock false $connection;
connectAttr -f ($ikTag + "." + $attrs[$i]) $connection;
};
};
};
// Segment for finding other possibly connected attributes on previous IK
$targets = `listConnections -d 0 -s 1 -c 1 -p 1 $list[0]`;
for($i = 0; $i <= (`size $targets` - 2); $i = ($i + 2))
{
$excluded = 0;
tokenize $targets[$i] "." $tokenResult;
for($target in $destinationExclusionAttributes)
{
if(match($target, $tokenResult[1]) == $target)
{
$excluded = 1;
break;
};
};
if($excluded == 0)
{
addAttr -ln $tokenResult[1] -at bool $ikTag;
setAttr -e -keyable true ($ikTag + "." + $tokenResult[1]);

connectAttr -f $targets[($i + 1)] ($ikTag + "." + $tokenResult[1]);
};

};

$targets = `listConnections -d 1 -s 0 -c 1 -p 1 $list[0]`;
for($i = 0; $i <= (`size $targets` - 2); $i = ($i + 2))
{
$excluded = 0;
tokenize $targets[$i] "." $tokenResult;
for($target in $sourceExclusionAttributes)
{
if(match($target, $tokenResult[1]) == $target)
{
$excluded = 1;
break;
};
};
if($excluded == 0)
{
if (!`attributeExists $tokenResult[1] $ikTag`)
{
addAttr -ln $tokenResult[1] -at bool $ikTag;
setAttr -e -keyable true ($ikTag + "." + $tokenResult[1]);
};

connectAttr -f ($ikTag + "." + $tokenResult[1]) $targets[($i + 1)];
};
};
$excluded = 0;
// delete $list[0];
}

// Second half of script for creation of new IK based off selected arguements.

else if(($mode == "ikSCComponentsSelection") || ($mode == "ikRPComponentsSelection"))
{
connectAttr -f ($list[0] + ".message") ($ikTag + ".startEffector");
connectAttr -f ($list[1] + ".message") ($ikTag + ".endEffector");
connectAttr -f ($list[2] + ".message") ($ikTag + ".parent");

if($mode == "ikRPComponentsSelection")
{
connectAttr -f ($list[3] + ".message") ($ikTag + ".poleVectorConstraint[0]");
};
};
};
};
ikTagProc;

The second proc goes through the scene and searches for all IK tags, and then applies IK by querying the connections to those tags.

global proc RM_ikTagProc ()
{
int $i;
int $i2;
int $constAttrCounter;
int $holdCount = 0;
string $list[];
string $item;
string $source;
string $destination;
string $sj[];
string $ee[];
string $tokenResult[];
string $parent[];
string $attrs[] = {".translateX", ".translateY", ".translateZ", ".rotateX", ".rotateY", ".rotateZ", ".scaleX", ".scaleY", ".scaleZ"};
string $attr;
string $constTypes[] = {"pointConstraint", "orientConstraint", "scaleConstraint", "poleVectorConstraint"};
string $constCommands[] = {"pointConstraint -offset 0 0 0 -weight 1 ", "orientConstraint -offset 0 0 0 -weight 1 ", "scaleConstraint -offset 1 1 1 -weight 1 ", "poleVectorConstraint -weight 1 "};
string $exclusionAttributes[] = {"startJoint", "endEffector", "ikSolver", "parent", "pointConstraint", "orientConstraint", "scaleConstraint", "poleVectorConstraint"};
string $skipAttrs[] = {"skipX", "skipY", "skipZ"};
string $skipString;
string $possibleConstNames[];
string $constName[];
string $constAttrs[];
string $newConstAttrs[];
string $targets[];
string $target;
string $ikAttrs[];
string $inConnections[];
string $outConnections[];
string $outConnection;
string $cleanAttr;
string $ikName;

$list = `ls -et unknown -r true "ikSCTag_*"`;

for($item in $list)
{
$ikName = `substitute "ikSCTag_" $item ""`;
$ikName = ($ikName + "_IK");
$sj = `listConnections -d 0 -s 1 ($item + ".startEffector")`;
$ee = `listConnections -d 0 -s 1 ($item + ".endEffector")`;
$parent = `listConnections -d 0 -s 1 ($item + ".parent")`;
ikHandle -sj $sj[0] -ee $ee[0] -sol ikSCsolver -n $ikName;
parent $ikName $parent[0];

for($i = 0; $i <= 2; $i++)
{
$targets = `listConnections -d 0 -s 1 ($item + "." + $constTypes[$i])`;
if(`size $targets` != 0)
{
$target = stringArrayToString($targets, " ");
eval($constCommands[$i] + $target + " " + $ikName);
};
};
for($attr in $attrs)
{
if(`connectionInfo -sfd ($ikName + $attr)` == "")
{
setAttr -lock true ($ikName + $attr);
};
};
setAttr ($ikName + ".visibility") 0;
delete $item;
};

$list = `ls -et unknown -r true "ikRPTag_*"`;

for($item in $list)
{

$ikName = `substitute "ikRPTag_" $item ""`;
$ikName = ($ikName + "_IK");
$sj = `listConnections -d 0 -s 1 ($item + ".startEffector")`;
$ee = `listConnections -d 0 -s 1 ($item + ".endEffector")`;
$parent = `listConnections -d 0 -s 1 ($item + ".parent")`;
ikHandle -sj $sj[0] -ee $ee[0] -sol ikRPsolver -n $ikName;
parent $ikName $parent[0];

for($i = 0; $i <= 3; $i++)
{
$targets = `listConnections -d 0 -s 1 ($item + "." + $constTypes[$i])`;
if(`size $targets` != 0)
{
$target = stringArrayToString($targets, " ");
eval($constCommands[$i] + $target + " " + $ikName);
};
};
for($attr in $attrs)
{
if(`connectionInfo -sfd ($ikName + $attr)` == "")
{
setAttr -lock true ($ikName + $attr);
};
};
setAttr ($ikName + ".visibility") 0;
delete $item;
};

$list = `ls -et transform -r true "ikSCTag_*"`;

for($item in $list)
{
$ikAttrs = `listAttr -k $item`;

$ikName = `substitute "ikSCTag_" $item ""`;
$ikName = ($ikName + "_IK");
$sj = `listConnections -d 0 -s 1 ($item + ".startEffector")`;
$ee = `listConnections -d 0 -s 1 ($item + ".endEffector")`;
$parent = `listConnections -d 0 -s 1 ($item + ".parent")`;
ikHandle -sj $sj[0] -ee $ee[0] -sol ikSCsolver -n $ikName;
parent $ikName $parent[0];

clear $possibleConstNames;

for($i = 25; $i <= (`size $ikAttrs` - 1); $i++)
{
$possibleConstNames[`size $possibleConstNames`] = $ikAttrs[$i];
};
for($i = 0; $i <= 2; $i++)
{
for($target in $possibleConstNames)
{
tokenize $target "_" $tokenResult;
if(`size $tokenResult` > 1)
{
if($tokenResult[(`size $tokenResult` - 1)] == $constTypes[$i])
{
$constName[$i] = `substitute ("_" + $constTypes[$i]) $target ""`;
break;
};
};
};
};

for($i = 0; $i <= 2; $i++)
{
$targets = `listConnections -d 0 -s 1 ($item + "." + $constTypes[$i])`;
if(`size $targets` != 0)
{
$constAttrCounter = 6;
$skipString = "";
clear $constAttrs;
for($target in $possibleConstNames)
{
if(match($constName[$i], $target) == $constName[$i])
{
tokenize $target "_" $tokenResult;
if($tokenResult[(`size $tokenResult` - 1)] == $constTypes[$i])
{
continue;
}
else if(match(("_" + $skipAttrs[0]), $target) == ("_" + $skipAttrs[0]))
{
$skipString += "-skip x ";
}
else if(match(("_" + $skipAttrs[1]), $target) == ("_" + $skipAttrs[1]))
{
$skipString += "-skip y ";
}
else if(match(("_" + $skipAttrs[2]), $target) == ("_" + $skipAttrs[2]))
{
$skipString += "-skip z ";
}
else
{
$constAttrs[`size $constAttrs`] = $target;
};
};
};
$target = stringArrayToString($targets, " ");
eval($constCommands[$i] + $skipString + "-n " + $constName[$i] + " " + $target + " " + $ikName);
$newConstAttrs = `listAttr -k $constName[$i]`;
for($target in $constAttrs)
{
$inConnections = `listConnections -d 0 -s 1 -p 1 ($item + "." + $target)`;
$cleanAttr = `substitute ($ikName + "_") $target ""`;
if(`attributeExists $cleanAttr $constName[$i]`)
{
if(`size $inConnections` != 0)
{
connectAttr -f $inConnections[0] ($constName[$i] + "." + $cleanAttr);
}
else
{
setAttr ($constName[$i] + "." + $cleanAttr) (`getAttr ($item + "." + $target)`);
};
}
else if(($constAttrCounter <= (`size $newConstAttrs` - 1)) &&(match("_W", $target) == "_W"))
{
if(`size $inConnections` != 0)
{
connectAttr -f $inConnections[0] ($constName[$i] + "." + $newConstAttrs[$constAttrCounter]);
}
else
{
setAttr ($constName[$i] + "." + $newConstAttrs[$constAttrCounter]) (`getAttr ($item + "." + $target)`);
};
$holdCount = 1;
};
$outConnections = `listConnections -d 1 -s 0 -p 1 ($item + "." + $target)`;
if(`size $outConnections` != 0)
{
for($outConnection in $outConnections)
{
if(`attributeExists $cleanAttr $constName[$i]`)
{
connectAttr -f ($constName[$i] + "." + $cleanAttr) $outConnection;
}
else if(($constAttrCounter <= (`size $newConstAttrs` - 1)) &&(match("_W", $target) == "_W"))
{
connectAttr -f ($constName[$i] + "." + $newConstAttrs[$constAttrCounter]) $outConnection;
};
};
};
if($holdCount == 1)
{
$constAttrCounter++;
$holdCount = 0;
};
};
};
};
$inConnections = `listConnections -d 0 -s 1 -c 1 -p 1 $item`;
for($i = 0; $i <= (`size $inConnections` - 1); $i = $i + 2)
{
$constAttrCounter = 0;
for($i2 = 0; $i2 <= 2; $i2++)
{
if($constName[$i2] != "")
{
if(match($constName[$i2], $inConnections[$i]) == "$constName[$i2]")
{
$constAttrCounter = 1;
break;
};
};
};
for($target in $exclusionAttributes)
{
tokenize $inConnections[$i] "." $tokenResult;
if($tokenResult[1] == $target)
{
$constAttrCounter = 1;
break;
};
};
if($constAttrCounter == 0)
{
tokenize $inConnections[$i] "." $tokenResult;
connectAttr -f $inConnections[($i + 1)] ($ikName + "." + $tokenResult[1]);
};
};
$outConnections = `listConnections -d 1 -s 0 -c 1 -p 1 $item`;
for($i = 0; $i <= (`size $outConnections` - 1); $i = $i + 2)
{
$constAttrCounter = 0;
for($i2 = 0; $i2 <= 2; $i2++)
{
if($constName[$i2] != "")
{
if(match($constName[$i2], $inConnections[$i]) == "$constName[$i2]")
{
$constAttrCounter = 1;
break;
};
};
};
if($constAttrCounter == 0)
{
tokenize $outConnections[$i] "." $tokenResult;
connectAttr -f ($ikName + "." + $tokenResult[1]) $outConnections[($i + 1)];
};
};
for($attr in $attrs)
{
if(`connectionInfo -sfd ($ikName + $attr)` == "")
{
setAttr -lock true ($ikName + $attr);
};
};
setAttr ($ikName + ".visibility") 0;
delete $item;
};
};
RM_ikTagProc;

CGTalk Moderation
08-21-2008, 03:29 AM
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.