Forum: Poser Technical


Subject: Reading Geometry (How Poser Does it - Mini Tut)

_dodger opened this issue on May 03, 2003 ยท 28 posts


_dodger posted Sat, 03 May 2003 at 7:44 AM

I've decided that since I actually comprehend what Poser is doing when it reads a file, I'm going to start posting some technical documentation.

The place to start, to me, seems to be explaining how a Poser file reads in geometry. So I'll do that.


Poser only uses Wavefront OBJect geometry. If you import Lightwave or 3DS or CAD or VRML, a subroutine converts the geometry to Wavefront OBJect format and then reads it into memory as such.


Terminology

Hereafter, the term 'object' will be used to refer to all forms of Poser objects, be they actors, props, lights, waveDeformerProps, sphereZoneProps, magnetDeformerProps, baseProps or cameras.

The term 'geometry' will be used to refer to actors and props specifically.

The term 'non-geometry' will be used to refer specifically to all objects except actors and props, despite the fact that lights and deformers do, in fact, load non-rendering geometry.

The term 'pseudo-geometry' wil be used to refer to non-geometries that load an object but do not render; lights, waveDeformerProps, magnetDeformerProps, baseProps, and sphereZoneProps.

The term 'definition block' is used to name the code block of a Poser file that sets up the channels and other properties of an object.

The term 'declaration block' is used to name the block in which an object is declared and at which point its geometry, if any, is read in.

When you see something in <> brackets, it means it's required but you're responsible for putting something in there. For instance, means any integer, be it 1, 23, or 144.

Similarly, something in [] brackets means it's optional. Poser's syntax is pretty tight, but not every line is always required. You will inevitably find someone somewhere who left out a line that appears in [] that hasn't ever had anyone report a problem.

Anywhere a string is called for, you can replace this with a call to GetStringRes(), a runtime function which takes two numeric arguments. Rbtwhiz's webpage has a wonderful chart showing the various results of arguments to GetStringRes in English. If the user's language is not English, this will be replaced with their language equivalent word or phrase. It's only recommended to do this with human-readable names (i.e, generally anything defined by a name directive).

The term 'bool' meand a boolean, a true-false value. Poser seems to define these by using 0 for false and anything that's not 0 as true (or at least anything positive -- I've never tried a negative number). You'll notice that Poser files often have, for instance, forceLimits 4 in them. 1 works as well as 4. Don't be fooled by Poser's eccentricities. The term 'float' means a floatingpoint number; that is to say, one with a decimal point as opposed to an int which doesn't have a decimal point. 3 is an int, 3.14159 is a float.


Part 1: Standard Geometry

geomResource

The first and most basic type of geometry is an internal Geometry Resource. I have found three of these:

4 Ball 6 Ground Plane 7 Light Direction Indicator (No faces -- will not render!)In a poser file, this is indicated by using the line:

geomResource <int>

inside of the declaration block for a geometry or pseudo-geometry. This will generally be indented for clarity.

When Poser encounters a geomResource directive, it loads in the geometry referred to by the resource ID. If the ID doesn't match anything, no error message is displayed, but no geometry is loaded. This can, in theory, be used to define 'fake' items that have channels, fit into a heirarchy, and have JPs, but do not have any vertices. This would be effectively equivalent to loading in an external OBJect file with no actual geometric content.

objFileGeom

The second simplest form of geometry loading is by using the objFileGeom directive. This command tells Poser to load a geometry in a specific Wavefront OBJect file. It appears with the following syntax:

objFileGeom <int> <int>
<path>

Like geomResource, this directive can be found in the declaration block of an object.

The path can be either in the form of an absolute location (i.e. "C:Program FilesPoser 4RuntimeGeometriesMyStuffmyprop.obj") or as a Poser Relative path (i.e. ":Runtime:Geometries:MyStuff:myprop.obj"). If the latter type of path is used the quotes are not necessary (except, perhaps, if you have spaces in the filename -- I haven't tested that, so either use quotes or test it).

The Poser Relative path style is vastly preferred, and if you intend the file to be distributed and usable by people who may have Poser installed in a different directory than you have (for instance, under 'Mac Drive 1:Applications:Poser 4'), it's a must. Renderosity and DAZ both require this path style.

When Poser encounters an objFileGeom directive it reads in an entire Wavefront OBJect geometry file as the geometry associated with the object regardless of the groups defined in the Wavefront OBJect.

geomCustom

The third simplest, and frustratingly common form of geometry loading is the geomCustom directive. This type of geometry actually reads in custom geometry from the file itself. This is the default for how Poser stores props imported rom either Wavefront OBJ files manually or from other file formats. This format includes a block which contains the OBJect file itself plus a few other informational directives, as shown below:

geomCustom 
        { 
        numbVerts <int>
        numbTVerts <int>
        numbTSets <int>
        numbElems <int>
        numbSets <int>
        v <Vertex Coordinates>
        ...
        [usemtl <Material ID>]
        [g <Group ID>]
        f <Face Coordinates>
        [t <Texture coordinates>]
        }

The numbVerts should tell the number of vertices in the geometry. The numbTVerts directive should tell the number of texture coordinates in the geometry. The numbElems directive tells, I believe, the number of facets in the geometry. I'm not sure what numbTSets and numbSets indicate.

Note that this method of reading geometry, while convenient, is best avoided. Poser creates .rsr files for geometry it has read and stores the geometry in a binary format within that .rsr resource when it reads from an external OBJect file. This file is faster for Pose to read. It cannot do this with a geomCustom internal OBJect, and thus props and other objects using geomCustom are best avoided. There are a few easy ways to solve this, however.

If the original file was read in from a Wavefront OBJect and the entire OBJect (all groups) makes up the Poser object, that Wavefront OBJect file can be directly referred to with an objFileGeom directive (see above). If the file was read in from ome other file format (such as 3DS or LWO), the custom geometry can be removed from the file and placed in an external Wavefront OBJect file using the following simple technique:

  1. Select everything within the of the geomCustom directive.
  2. Cut it and paste it into a new text file.
  3. Add an OBJ comment (#) indicator in front of the numb- directives at the beginning
  4. remove all the leading whitespace (generally tabstops) from the lines*
  5. Save the file with a .obj extension (for instance, 'myprop.obj'), preferably somewhere under the poser Runtime/Geometries/ folder (i.e. 'Runtime/Geometries/MyStuff/myprop.obj').
  6. Remove the geomCustom line and thes
  7. Add an objFileGeom (and, at least traditionally, a storageOffset directive (see below)).

figureResFile and geomHandlerGeom

The directives figureResFile and geomHandlerGeom work in combination to allow multiple object geometries to be loaded from a single Wavefront OBJect file. This is most often seen in CR2 files (and in PZ3 files that contain posable figures), but it can be used with any geometry. Here's how it works:

First, a figureResFile is declared outside of any block (except the enclosing unnamed block that surrounds the whole figure, though don't put one before the *version{ block). The directive tells Poser to open a Wavefront OBJect file and read it into memory, but it does not associate that geometry with any object by itself. The syntax is as follows:

figureResFile <path>

Then as the objects are declared, they define their geometry with geomHandlerGeom directives, which use the following syntax:

geomHandlerGeom <int> <Group ID>

This assigns a group within the Wavefront OBJect to be the geometry for the Poser object. The integer (int) is (at least traditionally) 13. I'm not sure why.

Traditionally, the figureResFile is declared again after all of the props or actors are assigned, between the declaration blocks and the definition blocks. I'm not sure why this is done, nor have I seen any difference when removing it.

You can, of course, use multiple figureResFile directives. If you do this, all geomHandlerGeom directives refer to the most recently opened Wavefront OBJect (and thus require the group requested be in that OBJect).

The mysterious storageOffset directive

You'll notice, if you crack open a CR2 or PP2 with external geometry, or a PZ3 (or even an LT2) that all the above geometry definition techniques except geomCustom are preceded by a storageOffset line. This line has the following syntax:

storageOffset <int> <int>
<int>

For objFileGeom and geomResource directives, the traditional numbers following the directive are 0 0.3487

. For geomHandlerGeom directives, the numbers are simply 0 0 0. I've played with these numbers trying to see what changes and found -- nothing. Nothing at all. As a matter of fact, I've removed the line altogether and it didn't seem to make a bit of difference in Poser 4.03. I can only surmise that this is a legacy directive from Poser 3 or earlier that's left in as a matter of course. It doesn't seem to be necessary at all. As a note, geomCustom directives are not preceded by this line.

If you include it (I do, though I do not know why, nor can I surmise what difference it een could make since the numbers are always set), it goes before the geometry assignment directive (but not before a figureResFile directive, which only reads geometry, it doesn't assign it).

Complex Geometry Reading

You can get as complex as you want with these. You can open a figureResFile OBJect for reading, read some body parts in from groups therein with geomHandlerGeom directives, open another figureResFile and read in more groups to other actors, read other actors in from internal geomCustom blocks, read in yet others from objFileGeom files, and even add a sphere or plane using geomResource. As long as your syntax is right, Poser will be happy.

As a note, you can theoretically load up geometry within a declaration and never assign it any definition -- if you do this, you will make Poser load up an object that cannot be moved or manipulated in any way (though it can be selected). Of course, it cannot properly work in a figure, but it might make a decent way of loading immovable walls without having to force 0 limits on the trans dials or something.


Extended Geometry - Dial-a-shape

The alternateGeom, defaultGeomName, and objFile directives

Ever wanted to make a Changing Something-or-other? Enamoured with Anton's Changing Fantasy Suits? Like dial-a-tile? Even just think the P4 skeleton hands are pretty nifty? Here's how it works:

All of this takes place down in the object definitions, not the declarations. All the object loading stuff in the prior section dealt with declarations, so just keep in mind that this is in the bottom set of object blocks, not the top set.

First of all, within the definition block for your object that has a change-dial, you need to declare the other geometries to replace the normal one. For this, you use the alternateGeom directive, each of which precedes a sub-block (a section). The syntax for this looks like what follows:

alternateGeom <string>
        {
        name <string>
        objFile <int> <path>
        }

The string should be a unique name to distinguish it from any other object or sub-object in the file. This will serve as the sub-object ID.

Within this block, you give the sub-object a human name with the name directive (for legacy purposes, if I recall correctly; pre-4 versions of Poser showed this name -- it doesn't seem to show anywhere in Poser 4.03). Next you define the Wavefront OBJect file to read the geometry from with the objFile directive, as shown above. The integer (int) should be set to a unique number from the other sub-objects, and to make it easier for you and others to see your code, you should probably make this make sense with the ID you gave it (and possibly the name if applicable). For instance, if you have changing left hands on a figure, your first alternateGeom ID might be lHand_2, and the objFile integer might be 1002 (this is what you will find in the P4 skeleton).

Here's a little note about how Poser does this: Poser reads in the alternateGeom declarations in the order it finds them and assigns them to a zero-indexed array. When you set up your geomChan channel (see below), each of the whole numbers available on the dial apply to the item in the array corresponding to that number. Poser doesn't give a damn what you use as the ID, name, or objFile integer. It doesn't put them 'in order' or anything like that, it just puts them in the order you declared them. This means that it might be prudent and easier to read if you don't do what the folks at MetaCreations did. Instead of making your first alternateGeom #2, make it #1 so that the numbers on the dial line up with what you put in them.

Making it work: the geomChan dial

Of course, now that you have all this wonderful geometry, you'll want to give someone a way to actually select that geometry. To do this, you need to add a geomChan channel to the object. This looks like so:

geomChan <Channel ID>
        {
        uniqueInterp
        name <string>
        initValue <float*>
        hidden <bool>
        forceLimits <bool>
        min <float*>
        max <float*>
        trackingScale <float>
        keys
                {
                static <float>
                k <int (frame)> <float* (value)>
                }
        interpStyleLocked <bool>
        }

For those of you not familiar with channel definitions and key definitions, the above directives mean, in order of appearance (and to the extent of my knowledge):


That's it for the geometry lesson (heh).