MD3 loader

I’ve completed (I think) an md3 loader that will be part of a set of loaders, that I may or may not get around to writing :slight_smile: I’ve written a generic api for dealing with 3d models, with the idea in mind that I could extend the api with a set of classes for each specific loader. The application then just uses generic 3d model objects and has no knowledge of what format the model was originally in. I will make the api open source when it is complete, but I was hoping I could get some feedback on the api and make some final changes before releasing it. I’ll give an example of how the api is used to load a couple differerent md3 formats. Please let me know if you think it sounds awkward to use or if it wouldn’t integrate well with other code for some reason.

On md3 formats: If you look for md3 models on the net, you are likely to come across a few different formats that a loader would need to deal with in different ways. The first format comes from the game Quake III, it consists of .skin files, an animation.cfg file, and three .md3 files making up the legs, head, and torso of the model (titled: upper.md3, lower.md3, and head.md3). Another format could be a mix of these pieces. For example the model parts may not follow the quake naming scheme and an animation.cfg or skin files may or may not be present. Another format could be a single md3 with a single texture that should be used with it, my guess is you’ll come across these if the md3 has been converted from another format.

For these different formats I’ve provided several unique load methods to deal with them, they are as follows:

ModelPattern[] loadSkinnedModel(String modelDirectory, String[] modelPartNames, String[] skinNames, boolean useAnimConfig)
ModelPattern[] loadTexturedModel(String modelDirectory, String[] modelPartNames, String textureDirectory, String[] textureNames, boolean useAnimConfig)
ModelPattern[] loadSkinnedModel(String modelPath, String skinName, boolean useAnimConfig)
ModelPattern[] loadTexturedModel(String modelPath, String texturePath, boolean useAnimConfig)
ModelPattern[] loadQuakeModel(String modelDirectory)

loadSkinnedModel assumes the model uses some .skin files that are in the same directory as the model, it may or may not use an animation config file.
loadTexturedModel specifies the textures directly instead of through a .skin file. When you pass in an array of model parts and an array of texture names, the model piece and texture with the same index will be used together.
loadQuakeModel assumes you are pointing it to a directory that is set up similar fashion to one of the player directories in quake III. The directory should contain an upper.md3, lower.md3, head.md3, upper_default.skin, lower_default.skin, head_default.skin, and an animation.cfg file.

To construct an MD3Loader object, you need to pass in an object created from a class that implements the TextureLoader interface, which looks like this atm(it could change…):

public int getTexture(String relPath,
int filter,
int wrapStyle,
int destPixelFormat);

here is a simple example of loading a skinned model without animation (its the railgun model from quake III):


        ModelPattern[] weaponModel = modelLoader.loadSkinnedModel("models/weapons/railgun/railgun.md3",       "railgun_default.skin", false);
        ModelPattern weaponPattern = weaponModel[0];
        ModelInstance weapon = weaponPattern.getModelInstance();

here is a more complex example of loading an md3 that was converted from mdx (warcraft 3 format), it doesn’t use skin files or an animation config file:


        ModelPattern[] wcModelParts = modelLoader.loadTexturedModel("models/wc3/orc/orc_stand.md3", "models/wc3/orc/orc.tga", false);
        wcModelPattern = (AnimatedModelPattern)wcModelParts[0];
        wcModelPattern.calcVertexNormals();
        wcModel = (AnimatedModelInstance)wcModelPattern.getModelInstance();
        Animation[] wcAnimations = new Animation[]{new Animation(0, wcModelPattern.getNumFrames()-1, 22, wcModelPattern.getNumFrames())};
        String[] wcAnimationNames = new String[]{"WALK"};
        wcController = new AnimationController(wcModel, wcAnimations, wcAnimationNames);
        wcController.playAnimation(wcAnimationNames[0], true);

The last example shows how you would load in a quake III model. In this example I’m going to use some convenience classes from the md3 loader package, so I’m sorta breaking the rule of only using the generic 3d model classes. This isn’t necessary and I could just use classes from the generic api (the convenience classes put them together for you), but this makes it so much more convenient if you know that you are using a multi-part md3 with an animation.cfg file.


        ModelPattern[] modelParts = modelLoader.loadQuakeModel("models/players/sarge"); 
        quakeModelPattern = new MD3ModelGroupPattern((MD3Model[])modelParts);
        quakeModel = quakeModelPattern.getModelGroupInstance();

        legsController = quakeModel.getLegsController();
        torsoController = quakeModel.getTorsoController();
        legAnimations = quakeModel.getLegAnimationNames();
        torsoAnimations = quakeModel.getTorsoAnimationNames();

        torsoController.addAnimationListener(this);
        torsoController.playAnimation(torsoAnimations[0], true);        

        quakeModel.setWeapon((MD3ModelInstance)weapon);

This saves you from having to create a ModelGroup and add ModelGroupNodes and simplifies the AnimationController retrieval process, which looks like this (whenever you’ve used an animation.cfg that is):


AnimationController legsController = legsPattern.createAnimationController(legsInstance);

Ok, one last piece to complete this quasi-tutorial: Once you’ve created these objects, there are two things you have to do to use them. The first is updating the animation controller (if the model is supposed to be animated). To do this you just need to call update on the controller and pass in the time passed in seconds since the last frame. The second part is rendering the model. If you are using a ModelGroup or just ModelInstances, you have the option of using the render() method which will render the model in immediate mode, or you can get an array of vertices/normals/tex coords from each model piece and do the rendering yourself. If you are using a ModelGroup (MD3ModelGroupInstance is a subclass of this), then you will need to call transformGroup() before rendering and reset() after. Here is the simple example, using the pre-written rendering method (immediate mode == slow):


            model.transformGroup();
            model.render();
            model.reset();

here is a demo: http://www.cyntaks.com/projects/md3loader/webstart/md3loader.jnlp
Controls:
Left: play previous leg animation
Right: play next leg animation
Up: replay current leg animation
A: play previous torso animation
D: play next torso animation
O: uses the warcraft model in place of the railgun (quite funny)
T: toggles texturing
W: toggles wireframe
L: toggles lighting
R: toggles rotating

Let me know what you think of the demo/api and hopefully I can make changes and finish it to release the source soon.

http://www.cyntaks.com/projects/md3loader/screen.jpg

;D Very cool so far!! I really like using the warcraft figure instead of the rail gun! I thought I was going to die I was laughing so hard!