=================================================== Lunaran's Hacked Up Maya 8.5 MDL Creation Sensation version 0.01a - 09.28.2008 - www.lunaran.com =================================================== This package contains everything I've slapped together to allow me to model and animate Quake models in Maya 8.5. The original modelgen loaded mesh data from Alias .TRI files from I really don't know what software, and that format is long dead, and skin data from .LBM files from DeluxePaint, also neither of which anyone uses any more. An updated modelgen loaded .BMP's but the source for that update wasn't released. I tinkered with the modelgen source that was released, reimplemented BMP import, and modified the mesh import to read an ASCII format from the actual .qc file, which normally just contained some header info like skin names and model flags. $base and $frame definitions are now expanded with curly braces to contain the actual triangle data that makes up that frame. Here's an example: $cd d:/games/quake/lunsp2/progs/src/ $modelname zknight $origin 0 0 24 $skin zknight1 $base { ( -2.073 -3.05884 40.00696 2.06148 -6.16084 40.10922 -3.38637 -5.41613 35.64854 ) ... ( 3.38637 -5.41613 35.64854 7.99561 -3.25202 40.69176 7.92312 -0.12951 35.95536 ) } $frame stand1 { ( -2.073 -3.05884 40.00696 2.06148 -6.16084 40.10922 -3.38637 -5.41613 35.64854 ) ... ( 3.38637 -5.41613 35.64854 6.84837 -3.25202 40.69176 6.75231 -0.12951 35.95536 ) } It only occured to me after doing all this that .obj is also an ASCII format, but the above is far easier to parse, because obj can have things all spread out in different orders and some data is optional and I don't have the patience. obj import could probably be added without too much trouble by whoever wants to. This requires Maya 8.5 - earlier versions did not support Python, and writing the script part of this pipeline in MEL when I could be using Python is like going to a fine steakhouse and ordering a braised shoe. I've included the source for the modified lunmodelgen.exe, which requires the original quake tools source and I wasn't about to pile all that in the zip (extract toolsrc/ into the same folder and it should all work fine), and the necessary python script. There's also an unfinished example monster, the Grim Knight - you'll have to fix the maya file references yourself when you open his animation scenes. =================================================== Here is the general process to get this to work. I assume a working knowledge of Maya, Python, and QuakeC, because scope of this document bla bla bla. PART THE FIRST: The model ------------------------- Create the model for your new item/lovecraftian horror/grunt that shoots rockets/tree/etc, and give it an identifiable name. (something unique. like polySurface245.) The script is set up so that a character facing down negative Z will be pointing the right way in Quake, because when I bring mdl frames into maya as reference* that's always how they're pointing so I just worked it out that way for consistency. If you're a die hard film industry animation purist and demand your rigs always point down positive Z, there's a #commented-out line in the script just for you. Once you're positive the model is done and you don't want any more mesh changes, ensure that it's triangles only, then duplicate it. This duplicate will be the version you splat out to turn into the kooky splayed roadkill base frame for texture projection. That should face the same direction as the model. Give it another unique name, stick it in a layer and hide it for now. (Triangle counts and vert order have to match between these two models, so if you find later on you want to change your animated model, you'll have to duplicate him and splat him again, but it's easy enough to v-snap the verts to the old base frame before you delete it.) You can apply whatever rig and animation to your model you want - skeleton w/controllers, blend shapes, etc. Quake is all vertex animation so as long as you don't make any construction changes to the mesh you're free to do whatever else. You can also have whatever other mesh and crap in the scene you want (a cube over the origin the size of your monster's hitbox is handy). PART THE SECOND: The script --------------------------- Open the .py file included in this package. Paste the first big block of text (up to the first divider) into the maya script window (and for god's sake DO IT IN THE PYTHON TAB). There's one line at the top you should change before you do anything else: fileDestination = "c:/where/do/you/want/your/files/today/" I think you can figure this one out. Hit kp_enter. Maya now knows how to write the expanded .qc file for your new item/lovecraftian horror/grunt that shoots trees/etc. There are two functions: compileTris() and exportQC(). compileTris() grabs the world space coordinates of the three verts of each triangle in your mesh, and exportQC() writes all the other fancy stuff around them. exportQC() is the one you want to call. The actual syntax is: exportQC( modelname, baseObj, animObj, skinNames, frameNames, origin ) An example: exportQC( 'shambler', 'shambler_flat', 'shambler_anim', ['sham'], shamframes, '0 0 24' ) (Don't forget these are function parameters, so inline strings need to be quoted or they'll be seen as variables that don't exist.) modelname: the name of the model in Quake. baseObj: the name of your splatted roadkill object. animObj: the name of your rigged animated object. skinNames: a python list (ie, an array) containing the filenames of all the bmp's this model has. frameNames: a python list containing a 2-tuple for every frame you want to include in the export (ie, a 2 dimensional array). More in this in a moment. origin: a vector telling quake where the model's origin is relative to the maya origin. So you can animate with his feet on the grid plane and still have the origin in the middle of the model like Quake expects. (this one's in quake space, so Z is up!) Framenames: Each frame in the .qc needs a name. This is where points on the timeline are mapped to names. The included .py file has a number of examples in it of how to fill a list like this, but here's an example: nourfly = [] for i in range( 1, 14 ): nourfly.append( [ i, 'fly' + str(i) ] ) This creates an empty list in it, then fills it with 13 2-tuples. The first entry in the tuple is time, and the second is frame name. You can also define these literally if a loop is too lazy for you. The above is the equivalent of typing this: nourfly = [ [ 1, 'fly1' ], [ 2, 'fly2' ], [ 3, 'fly3' ], [ 4, 'fly4' ], [ 5, 'fly5' ], [ 6, 'fly6' ], [ 7, 'fly7' ], [ 8, 'fly8' ], [ 9, 'fly9' ], [ 10, 'fly10' ], [ 11, 'fly11' ], [ 12, 'fly12' ], [ 13, 'fly13' ] ] When you run exportQC with all the right parameters, you'll wind up with modelname.qc in the fileDestination folder, and it will contain $frame data for each frame specified in the frameNames list passed to it. A common way of animating a model is to create the model and rig in one file, and then reference it into other files that contain only animation. Animations spread out over multiple files are supported, as follows. If you pass a baseObj into exportQC, it will assume you are starting a new .qc file and overwrite any existing one with new headers and a new base frame declaration. If you instead pass the python null value of None in place of the baseObj, the function will skip the qc headers and the baseframe, only parse the animated frames, and _append_ the data to any existing .qc file. This way you can pass a baseObj for your first animation file, then None for the rest. (If you want to reexport frames that are already in the .qc file, well, it's not that smart. you'll just have to delete stuff out of the .qc in a text editor or reexport everything.) PART THE THIRD: Lunmodelgen ------------------------ Once you've got your .qc file and .bmp files in the same folder, all you need to do is go to a prompt and run 'lunmodelgen pathtoyourqcfile.qc' and, any errors notwithstanding, you'll get a new .mdl in that folder. There's a few things that come before that, though. First is the skin. One thing I changed in lunmodelgen is that it takes the baseframe and scales it to be slightly smaller than whatever skin file it finds, so that it works a little more like ordinary UV's - if you size your skin page up or down the UVs will always align the same way. Currently it pads the UVs by a tiny amount that is actually dependent on the size of the base frame and not the size of the image - 2 units in world space, which is much more relative to a little model like a key or rune than to a big model like a monster. Scaling your base frame up will reduce the padding, and scaling it down will increase it. (This does not affect vertex coordinate integerization quality.) The process I usually follow is to look at my baseframe in Maya and measure its width and height, double the width, and create a blank .bmp in Photoshop with dimensions of the same aspect ratio. (The GLQuakes pad model skins up to the next highest power of 2 without resampling, so you can technically make this whatever size you want.) I run lunmodelgen, load it in qme23 and take a screenshot of the skin view to get an exact representation of how it's laid out, and paint my skin on that. When it's done I save over the blank one and run lunmodelgen again. (sometimes I'll be saving and lunmodelgenning a number of times as I'm working on the skin to keep previewing it in QME.) Another thing is that if you want your model to have flags (like rotate or smoke or whatever) you'll have to add that to the .qc file manually. exportQC already has enough function parameters. Don't forget that the names of the frames don't really matter, just the order they're in. Reexport your frames and anims in the same order every time once you've started implementing behavior in QuakeC, or shit will go haywire. That should be it - after all that you should have a .mdl fully compatible with any quake engine. If I forgot anything or broke something, reach me at matt@lunaran.com. If you forgot anything or broke something, you deal with it. =================================================== * There's no easy way to do this AFAIK, and what I've been doing is using qme30, which can export .mdl frames to .lwo, then opening the .lwo in lightwave and reexporting to .obj, which I then import into Maya.