Seán Cannon


Model3D::WavefrontObject - Perl extension for reading, manipulating and writing polygonal Alias Wavefront 3D models


      use Model3D::WavefrontObject;
      my $model = Model3D::WavefrontObject->new;
      $model->Rotate(x => 45, y => 15);


Model3D::WavefrontObject allows a polygonal Alias Wavefront Object file to be loaded into memory, manipulated, analysed, and re-output.

At this time the model only supports the polygon functions of the Wavefront Object format, not bezier splines, procedural surfaces, and so on. It is currently coded only far enough to support the sorts of Wavefront Object meshes that are also supported by the Poser 3D animation program, and of the sort exported by 3DSMax by using the HABWare exporter.

It supports groups and materials, but not multi-grouped polygons (only the first group is recognised if a polygon is declared to be in multiple groups).

Polygons with greater numbers of vertices are supported, even though these are not supported by the Poser software.

The models will also recognise (and support) the Region extension to the format as defined by Steve Cox's UVMapper and UVMapper Pro program. As a result, it may well be the only piece of code that writes Wavefront Objects without leaving these out.




The new() method returns a new Model3D::WavefrontObject object.

You may additionally supply other parametres to the constructor, including, if you so choose, all the data needed to construct an object (following the format of the object as shown in PROPERTIES, below).

The most common parametre to send into the constructor would probably be the objfile parametre. This can be set either to a file path or name, or to a reference to a filehandle. If this is done, the ReadObj() method will be called automatically by the internal _init() method when the object is created and before it's returned. Uhm, that is to say, you don't need to call it yourself.

It all depends on how specific you want to be.

Public IO Methods

ReadObj(filehandle or filename)

Use the ReadObj() method to read a Wavefront Object file into memory. Unless you are building one from scratch, you'll most likely want to do this.

You can provide a filename (either absolute or relative path, which means that both 'C:\Program Files\Curious Labs\Poser 4\Runtime\Geometries\XFXPeople\grpAeonTeenF.obj' and '../whatever/someObj.obj' will work.

It should be noted that Poser-relative paths will NOT work at this time. I.e., the string ':Runtime:Geometries:XFXCritters:grpChickenThing.obj' will not be parsed. This may change in a future release, but right now it's a bit tricky to tell whether the path is a relative one, or a path on an older Macintosh, as both have the same directory seperators (for reasons obvious to those familiar with the history of Poser).

Returns 1 on success, undef on failure. Check {errstr} for why.


Writes the Wavefront Object to outfile. If no outfile is provided, writes to STDOUT.

WriteObj() can accept either a filename or a filehandle reference, as ReadObj(), above. The difference is that WriteObj() doesn't require the file in question already exist.

Returns 1 on success, undef on failure. Check {errstr} for why.


The ReadMtlLib() method reads a material library into memory and associates the material lib data with the materials defined in the object mesh. If, while reading the Wavefront Obj, a mtllib directive is encountered, this method is automatically called (though in experience, it will rarely actually find the lib, as people almost never provide it with).

Public Manipulation Methods


The Translate() method moves the object to the left, right, up, down, and/or forwards and back. Specify the translations you want to perform when you call the method in hash format. For instance: $model->Translate(x => .1) # Translates 0.1 units to the object's left. $model->Translate(x => -0.3) # Translates -0.3 units to the object's right. $model->Translate(y => 2) # Translates the model 2 units upward. $model->Translate(z => -5) # Translates the model 5 units back.

Rotate(rotations, optional centre)

The Rotate() method rotates the object. You specify the rotations per axis in hash format, i.e. {x => val, y => val, z => val}.

You may also specify an optional centre to perform the rotations around, by setting a centre => property when you call the method. This centre may be specified with a hashref ({x => val, y => val, z => val}), an arrayref ([x, y, z]), or a string in two formats: either x,y,z or x:val,y:val,z:val. A scalar reference to such a string is also acceptable.

Additionally, the strings 'natural' and 'apparent' can be provided. If the centre is specified as 'apparent', the centre will be positioned in the centre of the bounding box that would surround the object. If 'natural' is specified, the centre will be positioned at the absolute average co-ordinate of the object. I.e., the average Y position of all of the vertices, and so on. This will cause objects that have, for instance, a heavy concentration of smaller, higher-resolution polygons in one location to have a centre closer to that concentration.

Note that if the arrayref method or the x,y,z string method is used, the order cannot be specified; it will always be in X,Y,Z order. However, as the rotation actually modifies the object and is not local to the object's axis, the rotations are not subject to gimbal lock. The order of rotation will, however affect the result, moreso the further the centre is from the actual object.

As a concession to the rebel colonies, the centre may also be supplied with the popular but improper spelling of 'center'.

Scale(scale/scales, optional centre)

The Scale() method scales the object. You can specify the scales in hash format, as per Rotate() and Translate() above, or as a single value which will be applied to all three axes. You may also supply a centre, as in Rotate(), above.

Since it is conceivable that you don't want to specify all three scales, but you do want to specify a centre, and since the one-argument method above does not provide for the option of a centre to be specified (since it needs to be just one argument), you may also provide an argument of scale, which will suffice as the scale to apply to all three. As a side effect of this approach, you can specify a single axis for one scaling factor, as per the normal hash format above (x => 110%), and then use the centre argument to specify both the other two at once.

You may specify the scaling factor as an absolute factor of 1 (i.e., .9, 1.7, and so on), as a percentage factor (90%, 170%), or as a relative amount (-.1, +.7). You may also combine relative and percentage approaches (-10%, +70%).

Any axis not specified defaults to a factor of '1', not 0, which means that unspecified axes do not flatten along that axis, as this would be unbearably annoying.


The Mirror() method causes an object to be mirrored along the axis specified. For instance, if you Mirror along the X axis, translate forward along the z axis, and rotate 180 degrees on the y axis, you will get a version of the figure that would be looking out of a mirror at the original figure.

Mirror() does not mirror UVW coordinates. To do that, call FlipUVs (see below). Note that this is important to know, as depending on the method used to interpret standard bumpmaps, and always in the case of Poser 4 'greenscale' bumpmaps, if an object is mirrored but the UVs and images used as maps are not inverted, the effect of the bumpmap will be inverted (causing white to indicate low areas and black to indicate high areas).

It should be additionally noted that mirroring the contents of a P4 BUM bumpmap will NOT work. You need to invert the greyscale bumpmap, then convert *that* to a greenscale bumpmap.

If no axis is specified, X is used.

FlipUVs('u' or 'v')

This method flips the UVs of the model. If 'v' is specified, the UVs are flipped vertically. If 'u' is specified, or if no axis is specified, the UVs are flipped horizontally.


The ReverseWinding() method reverses the winding order of the polygons' vertices. In effect (and in Poser), this reverses the normals of those polygons, effectively flipping a model inside-out or outside-in. The Mirror() method automatically calls this method after mirroring, to preserve the original appearance of the surfaces.

Note that Poser 4 and ProPack, and the Poser renderer in Poser 5 and 6 do not pay any attention to the normals and treat all polygons as 2-sided. It does, however, affect the preview mode, as well as affecting P5/6 Firefly renders by default.

Object files exported from ZBrush will usually have inverted normals because ZBrush inverts the Z coordinate internally (if you've used ZBrush and imported an Object, you've most likely noticed that you had to turn it around after loading it into your canvas). This method will fix that handily.

Utility Methods


This method returns a hashref containing the x, y, and z coordinates of the specified vertex, as well as the id and 'pid' (Poser ID) of it.

The vertex specified is the 0-indexed vertex. It should be noted that this is not the id as given in any given polygon (f) specification in an Alias Wavefront Object file, as the file format uses an index od 1 rather than 0.

The vertex retrived will have an id property set to the 1-indexed value, as well as a pid property set to the 0-indexed value. 'pid' stands for Poser ID, as Poser refers to vertices by their 0-indexed elements (i.e., in a TargetGeom delta directive in a Poser file).

If you want to select by the ID that the file format itself uses, simply subtract one from the value you want to specify: my $vert = $model->GetVertex($vertid - 1);


This method works just like GetVertex(), above, except that it returns the spherical co-ordinates of the vertex requested, in the order: Radial (rho), Azimuthal (theta), Polar (phi).


This method returns the natural centre of the model, in the form of a hashref containing the keys x,y and z with those values applied. If the model does not have any vertices when the method is called, the values are all 0.

The natural centre is the average x, y and z coordinate of all vertices, as explained in Rotate(), above.

This method is not spelled in American.


This method returns the apparent centre of the model, in the form of a hashref as with GetNaturalCentre, above. The difference, as explained under the Rotate() method, is that the apparent centre is the midway point between the top, bottom, left, right, front anc back vertices.


This method, primarily used internally but made to be accessible publically as well, returns two variables. The first is a hashref containing the minimum x, y and z co-ordinates, and the second is a hashref containing the maximum x, y and z co-ordinates of the model.

These values can be used to construct a bounding box, of course.

Because it's only meant to be called in a list context, if called in a scalar context the method will always return 2. Don't use it like that.


This method returns the highest Y value in the model.


This method returns the lowest Y value in the model.


This method returns the highest (leftmost) X value in the model.

This method returns the lowest (rightmost) X value in the model.


This method returns the highest (foremost) Z value in the model.


This method returns the highest (rearmost) Z value in the model.

Private Methods

There are (currently) three private methods, _getScaleVal, _getTransCentre, and _init. These are only for internal convenience parsing and are very unlikely to do an API user any good, so leave them alone. If you must know, read the source. It hasn't been bleached or eyedropped or anything.


Sean 'Dodger' Cannon qbqtre@ksk3q.arg =~ tr/a-mn-z/n-za-m/


  • Does not handle beziers/splines

  • Does not handle procedural geometry

  • Does not save normals out (but it does read them)

  • Does not yet write out MTLLibs


  • A Clone() method that duplicates the object without the innards being references to the first innards (allowing seperate manipulation of each)

  • Figure out how to handle beziers/splines and poly-fy them

  • Figure out how to handle procedural geometry and poly-fy it

  • Save out normals

  • Save out mtllibs (material libraries)

  • Apply morphs by group, material, or region

  • Extract geometry by group

  • Extract geometry by material

  • Extract geometry by region

  • Insert/append geometry

  • Increase mesh resolution (all or by group, material, region)

  • Project UVWs from one model onto another (I have an idea...)

  • Decrease mesh resolution. Dream on.

  • Apply boolean functions to geometry. Hahahaha! Yeah right! *splutter*

  • Interface with other 3D model modules. So far I think VMRL is all there is

  • Build in several utility scripts I have (as file processors) as methods

  • Feed the cat



Model3D::Poser (coming soon)