Thursday, April 11, 2013

How to Poser: Rigging Tricks

I'm pressed for time, so this is going to be an overview.  No pretty pictures this time.


First off I want to re-iterate; there are tools inside Poser, and there are third-party tools. I don't use them.  I've found I can do everything I need to do with a little text editing, and it feels more in control that way.

It isn't programming.  It is more like being a Script Kiddie.  In fact, I am told that the current maintainers of the code don't even know what some of those lines in the markup do.  The Poser files are filled with more and more cruft as time goes on.  Fortunately, it's just ASCII text so it doesn't take up much file space!

A little history of Poser, and a brief breakdown of the cr2 with an eye towards tricks you can use in your custom content, are below the fold.






So anyhow.  Once you've got a basic familiarity with the structure of the "cr2," you will gain comfort in cutting and pasting huge blocks of the code inside.  At that point, adding new dials or removing morphs and so on is mostly a matter of having the patience to scroll through to the right spot.

("cr2" is the suffix of the Figure file.  This is the file that calls a geometry and textures and arranges them in the workspace.  But the basic attributes of the cr2 are common to all Poser files, from the Scene file -- pz3 -- to a simple camera file.  In this lies one of the keys to the intense hack-ability of the Poser file system.)

When Poser was first created, it was to create a figure that could be then exported into an architectural or similar render.  The first technological advancement was instancing; Poser was expanded to allow any number of figures to be added to the workspace.

Thing is, because of how it originated, internal referencing continues to be done on the fly; at the moment of import.  So instead of a new figure being referred to inside the code as "Bob" -- thus allowing specific code to be written to look for a Bob and interact with a Bob -- the code refers to the new figure in the workspace in the order it was loaded; "Figure_1, Figure_2," etc.  This has led to both problems and some interesting hacks as time went on.



The next clever thing to mention along the development of Poser history -- this was around Poser 3 -- was conforming clothing.  In games, clothing is usually part of the character mesh.  Or sometimes it is objects parented to specific body parts (like a helmet).  In Poser, clothing is a figure of the exact same proportions, with the exact same joints.  When it is given the exact same pose as a human figure, it looks like the one is wearing the other.

The "Conforming" part of the code merely passes along the values of all the joint rotations from the "Conform To" figure to the "Conforming" figure.

Which means, among other things, if you hack in a BODY displacement along the X axis, the suit of clothes will dance alongside of the host figure like that gag that shows up on shows like America's Got Talent.



Notice I didn't say "morphs" above.  But here is where the "bug" of the instancing function becomes a "feature" instead.  If you put corresponding morphs into a clothing figure -- that is, morphs that carry the exact same internal names as those on the figure you are going to use as a host, when the master morph dials are set on the host figure the channels on the clothing figure get confused; they hear what they think is their name, and they move to the same spot.

One of the keys here is loading order.  When Poser gets to checking the morphs on the clothing figure, it looks back "up" into the workspace to see if there is a master dial of the proper name residing on "Figure_1."  If it doesn't find one, it continues the search on "Figure_2."  Eventually it would get down to itself, but before that it runs into the previously loaded figure you are conforming to.

This was called "super-conforming."  The bug that it makes a feature is called, however, crosstalk.  And crosstalk can show up any time you have more than one of the identical figure loaded into the workspace.  Many are the clever attempts both by third parties and the managers of the core software to fix these problems.



I've skipped over something important in the above.  Don't worry, I'll be coming back to it, but first I wanted to mention hands.

See, the first Poser figure didn't have fingers.  It was just a solid mesh.  And rather than try to create a morph for different hand positions, the programmers invented Alternate Geometry.  When you spin an AltGeom dial, it shunts away the original mesh for that body part, and loads in a new mesh from a selected object file.

Poser figures have fingers now.  This has lasted in the code mostly as a way to enable a "Genitals/No Genitals" switch.  But it is also of use to the prop/clothing builder.  Among the tricks I've done with it is to create identical copies of the geometry with different material zones assigned; thus, by the turn of a dial in the Poser workspace, different colors can be selected.



So.  Hands with fingers include, hard-coded into Poser, a little routine that looks for a dial named "grasp." If it finds one, it will run all the X rotates of the child finger channels.  And, I don't know what the actual history is, but I like to think some clever programmer saw that and realized you could use the same kind of code to apply a bunch of morphs at the same time.

Stop a moment.  A morph is a list of all the vertices in a selected part, only instead of having the absolute Cartesian coordinates, it lists them by delta; by how much they are to move and in what direction.  So instead of ".00123, .033, .000567" you have "d -.0001, 0, +.111"

Because of how the program handles actors (different body parts) each bit of mesh for arm, hand, head, etc., has its own morph.  So if you wanted to adjust the shape of the Poser figure to be muscular and heroic, you'd have to go into all the major body parts and set their "Charles Atlas" dials to a positive value.

Anyhow, by stealing the hand code, it was possible to make a channel that controlled nothing and put that in the BODY part, and then add slave code to all of the individual morph channels, so when that master dial was turned, they'd all turn.

This was called the "FBM" or Full Body Morph.

And enter some clever third-party users.  They looked at the four short lines of code that made a dial slave itself to a selected master dial, and they asked "What happens if we put these somewhere else?  Like, say, in a rotation channel?"

Thus is born ERC.



ERC stands for Extended Remote Control, and is a bit of a compromise term that evolved out of a small but polite fight over how to name the new discovery.  As it turns out, anything that has a dial can be called as a master.  And many of the things that can be controlled by a dial can be slaved.

This includes cross-figure slaving -- but that, again, runs into the crosstalk problems.

Anyhow.  People will still talk about FBMs, and about JCMs.  The latter stands for "Joint-Controlled Morph," and slaves a special morph to the rotation of a joint to make it look more realistic when posed.  Other than that, people tend to leave the other terms behind and lump all the rest of the slaving behavior into the term "ERC."

I would't say ERC is essential for prop creation, but it is a useful tool.  Among the minor advantages; when you have a complicated bit of machinery with only a few switches and dials that work, instead of asking the end-user to carefully select each wee switch by clicking on it, you provide corresponding master dials in a convenient place (usually the default pick body part.)

Another example from a recent thread; a rocking chair.  Since a real rocking chair translates as it rotates (think of it as being a short segment of a wheel), you'd have to play with more than one dial to animate it.  And due to the peculiarities of how Poser "tweeners" work, you might end up with some sliding.  But...bring the translation and rotation out to a single dial, preset the correct ratio using your local approximation of pi, and you can pose or animate smoothly and without fuss.



Right.  So, first, a little navigation.

The top of the cr2 references the geometry (twice) and instances the new figure and its body parts:

Program version (does next to nothing) and the first geometry file path:

{
version
    {
    number 6
    }
figureResFile :Runtime:Geometries:Princess:Steam_Props:acoustic_torsioner.obj

Initing the body parts (and some other random things Poser likes to throw in)

actor BODY:1
    {

    }
controlProp GoalCenterOfMass:1
    {

    }
controlProp CenterOfMass:1
    {

    }
actor handle:1
    {
    storageOffset 0 0 0
    geomHandlerGeom 13 handle 
    }
...

I should stop here and note that the actor name is the name used by Poser internally for the instanced body part.  The name given in the "geomHandlerGeom" is the name of the polygon group in the object file.  And weird things happen if you mess with the storageOffset number...don't do it.



The next part is the main body of the cr2; this is where each actor (body part) gets the various parameters including initial values:

actor handle:1
    {
    name    handle
    on
    bend 0
    dynamicsLock        1
    hidden        0
    addToMenu    1
    castsShadow        1
    includeInDepthCue        1
    useZBuffer        1
    parent BODY:1
    creaseAngle 80
    channels
...

The top part does such things as set the smoothing angle, the name that will be displayed in the workspace, whether it show up as selectable, whether it is totally invisible, how to display it, etc.  More of these show up in a corresponding bottom part, after the channels:

...
    endPoint 0.000285948 0.326 0
    origin 0.000312 0.300005 1e-05
    orientation 0 0 0
    displayOrigin        0
    displayMode USEPARENT
    customMaterial    0
    locked 0
    backfaceCull 0
    visibleInReflections 1
    visibleInRender 1
    displacementBounds 0
    shadingRate 0.2
    smoothPolys 0
    }

One of my recent prop set is a magnifying glass.  Since a refraction node doesn't display in preview mode, to make it possible to look through the glass in preview mode I used "displayMode WIREFRAME" to over-ride the display defaults for the rest of the figure, forcing the lens and the lens alone to display in wireframe mode.

"customMaterial" is also a cool function.  You can define a completely different set of texture maps et al for materials that occur throughout the figure.  For instance, you can define the skin on the hands of a figure to be differently colored than the "skinBody" that would otherwise be applied to hands, arms, torso, etc.

There's an even more advanced trick...on the second clothing set I sold I built a MAT pose that over-rode the selected skin texture on the figure's legs to replace it with a tights texture.  That meant one "tights" texture was compatible with any figure texture map -- all without PhotoShop.  (The major limitation of this is it is on a per-actor basis, and not all figure meshes are sliced in convenient places).

Since there's a lot of historical cruft in there, not everything works like you think it would work.  Changing some of these lines will break Poser.  Others will have no effect even though you think they should (for instance, changing "actor handle:1" to "actor handle:2" will do nothing at all.  Poser assigns the numeric at load time, based on the order you add things to the workspace.

Between these two blocks, and making up the bulk of the code in each actor, are the channels.

(Oh, yeah, and in Poser 5 and above the channels are also assigned to channel groups, but that just has to do with how they display in the workspace and is pretty self-explanatory.  You can go ahead and delete the groups entirely; Poser will rebuild them from the channel names at run time.)

Channels fall into four basic formats.  You have your transform channels (scale and rotations):

        scale scale
            {
            name scale
            initValue 1
            hidden 0
            forceLimits 0
            min 0.1
            max 100000
            trackingScale 0.004
            keys
                {
                static  0
                k  0  1
                }
            interpStyleLocked 0
            }

"Name" is the name displayed in the workspace (whereas the first name is used internally).  min and max and trackingScale are the same things as in the pop-up you get when you double-click a dial, but forceLimits will make the dial behave as if limits are set for that dial even if they aren't set for that figure.  keys are animation related, which is why one of them is identical (1 in this example) with the initValue (the value the dial starts with).  And "hidden," as I mentioned before, removes it from the list of dials in the workspace display.

You'll notice the majority of the dials are hidden by default.  This is particularly true of a subset of the channels, the Affectors;

        twistY rBunear_twisty
            {
            name rBunear_twisty
            initValue 0
            hidden 1
            forceLimits 0
            min -100000
            max 100000
            trackingScale 1
            keys
                {
                static  0
                k  0  0
                }
            interpStyleLocked 0
            otherActor rBunear:4
            matrixActor NULL
            center -0.0162426 0.67091 0.00326313
            startPt 0.657258
            endPt 0.754053
            flipped
            calcWeights
            }


These are channels (rarely displayed as dials) that set the joint parameters at the parent end of the children of an actor.  These are what cause the parent to bend smoothly when the child is moved.  In the case of mechanical props, you are best off deleting them all.

(You can recognize them by the fast that all of them list an "otherActor" and all of them have that other actor in their channel name.)



Another dial format is the morph channel.  The easiest way to recognize a morph channel is from the giant list of deltas that are listed in it:

        targetGeom leaves
            {
            name leaves
            initValue 0
            hidden 0
            forceLimits 1
            min 0
            max 1
            trackingScale 0.02
            keys
                {
                static  0
                k  0  0
                }
            interpStyleLocked 0
            indexes 304
            numbDeltas 832
            deltas
                {
                d 8 0.0001220215 0 -8.15313e-05
                d 9 2.863156e-05 0 -0.0001439345
                d 10 -8.15299e-05 0 -0.0001220204
                d 11 -0.0001439303 0 -2.863065e-05
                d 12 -0.0001220179 0 8.152984e-05
                d 13 -2.862819e-05 0 0.0001439331
                d 14 8.153344e-05 0 0.0001220204
                d 15 0.0001439327 0 2.862926e-05
                d 16 8.151833e-05 0 -5.446823e-05
...

Although, as of I believe Poser 6, morphs could be stored externally to the cr2, in compressed form.  But the "targetGeom nameOfMorph" format is conserved.


The third form of channel is the channel that controls nothing:

        valueParm sonicX
            {
            name sonic
            initValue 0
            hidden 0
            forceLimits 1
            min -100000
            max 100000
            trackingScale 0.01
            keys
                {
                static  0
                k  0  0
                }
            interpStyleLocked 0
            }

By itself, a valueParm channel has no function.  What they are used at entirely is for slaving code; any channel that contains the code below will search for the valueParm channel of the correct attributes and take its values for its own.

            valueOpDeltaAdd
                Figure 1
                handle:1
                sonicX
            deltaAddDelta 2.000000


The last kind of channel is the altGeom channel;


        geomChan handGeom
            {
            uniqueInterp
            name change clip
            initValue 0
            hidden 1
            forceLimits 1
            min 0
            max 2
            trackingScale 0.045
            keys
                {
                static  0
                k  0  0
                }
            interpStyleLocked 1
            }
     
 It must be named "handGeom" internally (that's how Poser recognizes it as a distant progeny of the old change-hands trick).  And it also requires that at the top of the actor, just before the first channel, you instance the new geometries and identify the associated object file:


    alternateGeom    clipBase_2
        {
        name clipBase2
        objFile 2101 :Runtime:Geometries:Princess:Stage_Mics:clipBaseALT.obj
        }
    alternateGeom    clipBase_3
        {
        name clipBase3
        objFile 2102 :Runtime:Geometries:Princess:Stage_Mics:nullClip.obj
        }
defaultGeomName  clipBase_1


As you may notice, the format of this channel allows it to look anywhere for the alternate geometry; not just in the loaded figure object.

The "objFile 2102" are arbitrary but invariant magic numbers.  Poser will never check to see if they are actually unique identifiers.  Using values above 2000, at least, keeps you from accidentally sharing the ID of the part you want with the ID of, say, M2's hips.

It is also necessary to use null objects (in this prop, a single tiny polygon set to a transparent material).  If you don't, once you call that version, it will be impossible to revert.

Incidentally, Poser sort of tracks morphs for the different geometries.  It will sometimes correctly display only the morphs that belong with that particular one.  And sometimes it won't.



Right.  So after the actors, with all their channels, the cr2 has a brief bit where it defines the hierarchy of the figure, attempts to weld together the body parts of organics or similar smoothly connected body parts, and establishes the parts and end parts of IK chains:


figure
    {
    name    Bunny Ears
    root    BODY:4
    addChild    head:4
             BODY:4
    addChild    lBunear:4
             head:4
    addChild    lBunearTip:4
             lBunear:4
    addChild    rBunear:4
             head:4
    addChild    rBunearTip:4
             rBunear:4
    defaultPick lBunear:4
    displayOn 1
    weld    lBunear:4 
             head:4
    weld    lBunearTip:4 
             lBunear:4
    weld    rBunear:4 
             head:4
    weld    rBunearTip:4 
             rBunear:4
    allowsBending 1
    figureType 1318
    origFigureType 1318
    canonType 8
    conforming 0

(There were no IK chains in that particular figure).

A few things to note; defaultPick is the part that will be in the parameters window if you select the figure or if you've just loaded it.  It is usually the top-most actor following BODY, which is a special actor Poser creates, Poser maintains, has no geometry, and Poser will become very angry if you omit it or rename it.

This is also a nice place for a handy hack.  If you insert a new body part into the hierarchy HERE (aka add a new "addChild" line, AND if you initialize that new part using the

actor handle:1
    {
    storageOffset 0 0 0
    geomHandlerGeom 13 handle 
    }

stuff from the top of the cr2, the very next time you load that figure Poser will create a new actor, and fill it full of the appropriate channels.

This is particularly handy when you discover you want to add the ability to rotate a dial that was originally just part of the base mesh.  Or to add body handles.



What are body handles? 

Remember, children affect their parents.  So if you add an extra actor, represented by an arbitrary bit of geometry (or nothing at all...and you run it entirely through ERC), you can pull at a mesh just as if you were using a magnet in the Poser workspace.




And it is past time for me to see to my own body handles.  Today is a gym day (if I can fit it around microphone repairs).  I'll go into more later on the mysteries of ERC, on creating morphs, on how to use Point At to simulate a piston and how an IK chain could allow you to patch a virtual Moog.

The last big chunk of the cr2 is the materials definitions, and after the last post I'm tired of those already.  So we'll leave those, too, for some other day.

No comments:

Post a Comment