NWN Armory

At its most abstract, NWN Armory is a quick application that performs renaming, scaling, rotating and translating on NWN PC model parts to generate other model parts.

Why would you ever want to do this? It is intended for two main reasons although there may be other applications for it as well

  1. Creating all race models from a human template. Let's say you create a new chest piece for a special type of armour you are building. You need to model it for a human male and create another version for a human female. There are also two phenotypes (phenotype = body size, standard and large) so you need to create these as well. That part you need to do by using a modeling tool. Now there are five more race models (halfing, gnome, elf, dwarf and half-orc). You would need to make twenty more of those armour pieces just to be able to have one for all the elves, halflings, etc. This utility will do most of that work for you based on your four starting models.
  2. Creating all armour models for a new race. Let's say you want to create a brand new race. Maybe it will be slightly taller and skinnier than an elf (a githerzai, say). There are hundreds of model pieces out there if you want your new race to be able to wear all available types of armor. But you can tell NWN Armory how your new race differs from one of the standard races (say an elf) and it will transform those hundreds of pieces for you.

How do you make it work?

To create a race model from a human template, it is easy:

  1. Make sure the NWNArmory.ini file contains the standard transforms (the transforms were graciously provided by BearThing). This is the default although if you have modified it you will need to copy standard.ini in place of NWNArmory.ini. I probably should make a wizard to do this. Maybe next version.
  2. Then you fire up NWN Armory and tell it which models to use as base models. These models must follow the standard NWN naming conventions (e.g., pfh0_belt009.mdl). They must also be ASCII model files. If they are not ASCII model files, use NWNMdlComp to make them into ASCII model files if they are binary.
  3. Tell NWN Armory which directory to use to store the transformed models.
  4. Go. What this program will do is compare each input file against the standard list of transform groups. Where there is a match, it will rename the model and apply the transforms you specify, and save the resulting model in the output directory.

To create all armour models for a new race , you need to do a little more work:

  1. First you need to create a transform initialization (.ini) file and copy this into NWNArmory.ini. The .ini file consists of a series of transform groups. Each group consists of a match string (you tell NWN Armory which models this group applies to), a substitute string (you tell NWN Armory how to rename the model based on the original name), and the definition of the transform for that group. In the transform you essentially tell NWN Armory how to scale the original model (x, y and z-axis scaling factors), how to rotate it (about the x, y, and z-axis respectively), and how to translate it (x, y, and z).
  2. Next, extract all of the standard armor models (typically pfh0_*, pfh2_*, pmh0_*, and pmh2_*) from models_02.bif. Use NWNViewer or NWNExplorer.
  3. Decompile all of these models from binary to ASCII format using NWNMdlComp.
  4. Fire up NWN Armory and tell it which models to use as base models (if it isn't clear, you want it to use the freshly decompiled models from step 3).
  5. Tell NWN Armory where to put the transformed models.
  6. Go. What this program will do is compare each input file against the list of transform groups you gave it. Where there is a match, it will rename the model and apply the transforms you specify, and save the resulting model in the output directory.

The INI File Format

The transform file is fairly simple but, I think, powerful (this is why the tool may be used for other duties as well, such as simply renaming models without changing them). Standard.ini is supplied with this program and contains the standard racial transforms. Sample.ini is an example of different transforms that can be used with this program.

This is from the sample.ini file example.

; This .ini is a sample to show how to set up different examples
;
; By Eligio Sacateca, May 2003
;
; Instructions
; Specify the number of transform groups with ntransforms.
; The program will only read this many transforms even if
; you include more in this file.
;
; For each transform, create an index row [s>TRANSFORM#<]
; These do not have to be in order (altho they are easier
; to keep track of if they are, obviously).
;
; If you skip a row the corresponding group will not match
; any input files so it should not hurt anything (it won't
; help either but if you delete one you don't need to go
; back and renumber everything).
;
;
; The format of each group is:
; match= : if this string matches the input source model name, this
; group will be applied against the input file to produce an
; output file. This string uses globbing where a '?' is a wildcard
; and will match any single character while '*' is a full glob
; wildcard and will match any number of characters. Examples:
; abc* matches abcdef
; abc??? matches abcdef but not abcdefg ; abc*f matches abcdef and abcdeanythingf
; abc does not match abcdef
;
; substitute= : the file and model name of the new file will consist
; of the input name, modified by substituting characters in this
; string against it. a '?' is a wildcard and will not change the
; input string at that point. A '*' is a full glob wildcard and
; will not change any more characters past the end of the input
; name. If the input model name exceeds the substitute string in
; length (after taking into consideration wild cards like '?' and
; '*'), excess characters will be truncated. If the input model
; is shorter, extra characters from the substitute string will be
; appended at the end. Examples:
; abcnopqrs to defghi gives abcnopqrs
; abc to defghi gives abc
; abc? to defghi gives abcg
; abc* to defghi gives abcghi
; abc*n to defghi gives abcghin ; ??abc to defghi gives deabc
;
; scale=(x, y, z) : x, y and z axis scaling respectively. If you
; don't put in a scale parameter for a group, it defaults
; to (1, 1, 1) (i.e., no scaling).
;
; rotate=(x, y, z) : x, y, and z axis rotation respectively. If
; you don't put in a rotate parameter for a group, it defaults
; to (0, 0, 0) (i.e., no rotation).
;
; translate=(x, y, z) : x, y, and z axis translation respectively. If
; you don't put in a translate parameter for a group, it defaults
; to (0, 0, 0) (i.e., no translation). Translation is the only
; parameter with absolute values so don't forget that most models
; are scaled by 100x when viewing them in Gmax or Max so if you
; experiment in these tools to get the right values, don't forget
; to reduce the translations appropriately.
;
; translate=(x, y, z) : x, y, and z axis translation respectively. If
; you don't put in a translate parameter for a group, it defaults
; to (0, 0, 0) (i.e., no translation).
; This is a parameter with absolute values so don't forget
; that most models are scaled by 100x when viewing them in Gmax or
; Max - if you experiment in these tools to get the right values,
; don't forget to reduce translations appropriately.
;
; position=(x, y, z) : x, y, and z axis specification for the position
; parameter within the model file. For armor parts, the position
; parameter identifies the pivot point for the model.
; This is a parameter with absolute values so don't forget
; that most models are scaled by 100x when viewing them in Gmax or
; Max - if you experiment in these tools to get the right values,
; don't forget to reduce translations appropriately.
;
; minimum=(x, y, z) : if a vertex is outside of the minimum
; x, y, and z values, it will not be transformed. I don't expect
; this and the maximum= parameters to be used much but they can
; be set up to do selective transforms on vertices. I created
; these parameters so that I could scale parts of a model differently
; than the rest of the model.
; This is a parameter with absolute values so don't forget
; that most models are scaled by 100x when viewing them in Gmax or
; Max - if you experiment in these tools to get the right values,
; don't forget to reduce translations appropriately.
;
; maximum=(x, y, z) : if a vertex is outside of the maximum
; x, y, and z values, it will not be transformed.
; This is a parameter with absolute values so don't forget
; that most models are scaled by 100x when viewing them in Gmax or
; Max - if you experiment in these tools to get the right values,
; don't forget to reduce translations appropriately.
;
; If the scale, rotate, and translate parameters amount to no
; no change, the program will just copy the input file and rename
; the model according to the substitute string. This is useful for
; copying and renaming models with no other changes.
;
;
; trotate=(z) : (new!) z-axis rotation of the texture map. If
; you don't put in a rotate parameter for a group, it defaults
; to (0) (i.e., no rotation).
;
; tscale=(x, y): (new!) x and y axis scaling respectively for a
; texture map. If you don't put in a scale parameter for
; a group, it defaults to (1, 1) (i.e., no scaling).
;
; ttranslate=(x, y) : (new!) x and y axis translation of your texture
; map,
respectively (that's right, you can now move texture maps around).
; If you don't put in a translate parameter for a group, it defaults
; to (0, 0, 0) (i.e., no translation).
; This is a parameter with absolute values so don't forget
; that most models are scaled by 100x when viewing them in Gmax or
; Max - if you experiment in these tools to get the right values,
; don't forget to reduce translations appropriately.
;
; tminimum=(x, y) : (new!) if a texture vertex is outside of the
; minimum x and y values, it will not be transformed. These is
; pretty much the same as the minimum and maximum values applied
; to texture maps.
;
; tmaximum=(x, y) : (new!) if a vertex is outside of the maximum
; maximum x and y values, it will not be transformed.
;
; tbitmap=<bitmap name> : (new!) lets you specificy that only texture maps
; for this 
bitmap will be transformed.
;
; That's it!

Notes

For the output file, each vertex is rounded to 7 decimals of accuracy. Which means after you scale by 100 to make them visible in-game, each vertex has 5 decimals of accuracy. I did this for three reasons:

  1. it is easier than doing arbitrary decimal points like the input files have - C++ requires floating point numbers to be output with a fixed number of decimal places and going out to 11 like most of the other tools out there seemed kind of pointless
  2. I can't tell the difference when I change the 3rd decimal point, let alone the 5th
  3. did I mention it was easier?

The order of operations is important. It is scaling followed by rotation followed by translation.

Rotations are done successively and in VIEW coordinates. Let me explain that. It is the equivalent of

  1. selecting the entire object in Max at the mesh level,
  2. setting the Reference Coordinate System to View,
  3. selecting the rotate transform
  4. typing the corresponding values into the x, y and z boxes.

If you use other coordinate systems, your mileage will vary.

Note that rotation takes place about the origin only (0,0,0). Also, the program now scales, rotates and transforms the pivot point in addition to the other vertices. Usually it has a value of (0,0,0) so rotation and scaling do not generally affect the pivot point - only translation does.

Note the new texture map translation feature. I did this to create black weapons from default ones that use Bioware's standard weapon texture. It can be used for all sorts of odd things. No, really, it can. Just don't ask me to list the uses. There are, uhm, too many.

Default Values

Most parameters have a default value so they do not have to be specified:

  1. match=NULL - if this parameter is not specified, the transform will not match any parts and will never be used
  2. substitute=* - if this parameter is not specified, the input file name will be re-used for the output file name
  3. scale=(1, 1, 1) - no scaling
  4. rotate=(0, 0, 0) - no rotation
  5. translate=(0, 0, 0) - no rotation
  6. position=NULL - the position value is transformed like any other vertex if not specified. If specified it takes on the absolute value of the parameter given
  7. minimum=(-999, -999, -999) - if this parameter is not specified, pretty much all vertices are affected
  8. maximum=(999, 999, 999) - if this parameter is not specified, pretty much all vertices are affected
  9. tscale=(1, 1) - no scaling
  10. trotate=(0) - no rotation
  11. ttranslate=(0, 0) - no rotation
  12. tminimum=(-999, -999) - if this parameter is not specified, pretty much all vertices are affected
  13. tmaximum=(999, 999) - if this parameter is not specified, pretty much all vertices are affectedFeedback

Please send bugs, either in the program or the standard.ini transform file, to eligio@cogeco.ca.

Credits

That stunning background image is batinthehat's Arcanoloth from the City of Doors initiative. Feel the leather. It's real. No, really, I swear it is.

Legal Mumbo Jumbo

All this stuff is copyright Eligio Sacateca, 2003. Well, most of it. Not the background image - it is bat's. Didn't I already tell you that?

All rights are reserved. All of them. Really. Whatever that means.

Having said that, I also give you permission to use it if you want for anything you want. As long as it is legal and doesn't get me in trouble - if you are going to do something illegal with it then you can't use it.

Oh, and feel free to email me if you want a copy of the source code.

Did I mention all rights were reserved? They are - notwithstanding the fact that you can use it for pretty much anything and you can have the source code too.

I think I'm obligated to tell you that there is no warranty either. Absolutely none. Zero. Zilch. This program may not do what it was intended to do and it certainly may not do everything or even anything you want to do with it. If you live in one of those weird jurisdictions where I can't offer no warranty, then I take back what I said about you being able to use this piece of software. You can't use it. Using it would be illegal. And if you sue my ass for coverage under a warranty like that, then I'll sue your ass for illegally using my software when I said you couldn't. And then where will we both be?

There. I no longer feel like my ass is hanging out there with a cold breeze blowing across it. Do you feel better too?

Change History

  1. Version 1.0 - Initial Release
  2. Version 1.1 - Added the ability to do translations and to fix the Position line in place. Took out the warning message if the pivot point was not zero. Now scales, rotates and translates the pivot point too. And includes minimum and maximum range values so you can affect only a subset of vertices if you want.
  3. Version 1.2 - Added the ability to do transformations of texture vertices.

Technical Notes

The problem is to find a method to get the exact transform matrices for scaling to the different races. If BioWare didn't use anything but the scale tool we could do it fairly quickly. Unfortunately I am told they did a little more than a pure scale/rotate. Apparently most parts consist of a scale and then a couple of vertices that are shifted in an unknown and totally inconsistent fashion. Most modelers I have talked to say they want the tool to do 98% of the work and then they will go in and tweak the last vertex or two by hand.

Other people smarter than I (i.e., Saep) are working on how to reverse engineer the base transforms by trying to solve the following system of equations:

vertex_i_ = TransformMatrix * vertex_i_basemodel

where vertex_i is the i'th vertex of a mesh.

The matrix should be 4x4, but certain entries are given:

| s r r 0 |
| r s r 0 |
| r r s 0 |
| t t t 1 |

where r means rotate, s means scale and t means translate.

If we find a mesh with more than 12 vertices (should be easy) the system of equations is solvable anyway. Once we have this matrix we can simply scale any model.