MD5 reader and new model animation system

chaos if you,re out there, the md5 loader seem heavily reliant on an existing shader/texture state or nothing loads, would be possible to add a default material state for untextured models.



I made a water mesh for my project to test and had a hell of a time getting it to display until I realised what was going on. after I added a texture it worked though.

I’ve only been using it myself with the Doom 3 models, so I’ve always wanted to load the textures too - I’m sure I put a way to do this into the code, I’ll check later tonight when I get home.



Thanks for the feedback!

first of all, GREAT JOB, second what is ur loader limited from loading?

I’m not sure what you mean by that - I’ve only tested it with the doom3 MD5 models and they all seem to load fine, as far as I can see. The only problem may be that they all use the same skin, although I may re-implement the default skin stuff later.

like normal maps

Ah, I see what you mean. Currently it only loads the diffuse map (standard texture image). I tried loading normal maps, but as my graphics card doesn’t support pixel shaders I had to try DOT3 bump mapping - which I couldn’t get to work properly. Specularity maps I haven’t even looked at yet. If you want to try and get DOT3 bump mapping working or use a pixel shader version, feel free :slight_smile:

so they don’t work on ur computer so u don’t include them? that sounds like a horrible idea. include all the stuff then i could make a really nice lookin game i figuered normal mapping wouldn’t work because i haven’t seen anybody do a pixel shader program so i figuered it couldn’t be done. And what do u mean use a pixel shading version?

Well, how can he debug whether its working or not if his graphics card doesn’t support fragment programs? Its just out of his capabilities and i do think its too much to ask of him to go out and buy a

As DP said :slight_smile:



I’ll be implementing proper bump mapping when I have a decent card which supports it - which’ll probably be a little while yet. As I said, feel free to implement it yourself, I don’t have any way to do it right now.



Anyway, MD5 is currently usable, my main priority right now is to get animation blending working properly as this is the most-needed feature at the moment (or so I think). And obviously documenting the existing code.

if u posted the code you got i bet somebody might work on it not everybody wants to see the final product

I think if they took this to GDC and had like a could of done something like this and got a lot of looks.

http://www.monitorstudios.com/bcloward/tutorials_normal_maps5.html

Quick question on your stuff could you define exactly Mesh,Shader,Skin. Think i’m getting Mesh and Shader mixed up and i might have questions about shader.

i uncommented your dot3 bump shader stuff and tried to get it working but nothing seems to happen. See anything wrong?

here is the code


import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.io.FileInputStream;

import md5reader.MD5AnimReader;

import md5reader.MD5MeshReader;
import model.Model;
import model.Shader;
import model.SimpleShader;
import md5reader.MD5BumpShader;
import model.Mesh;
import model.*;
import model.ModelInstance;
import model.ModelTriMesh;
import model.SimpleSkin;
import model.animation.Animation;
import model.animation.AnimationController;

import com.jme.app.SimpleGame;
import com.jme.math.TransformMatrix;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.image.Image;
import com.jme.image.Texture;
import com.jme.renderer.ColorRGBA;
import com.jme.util.LoggingSystem;
import com.jme.util.TGALoader;

/*
 * JmeDDSTest.java
 * Created on 06-Feb-2005
 *
 * Copyright Chaosdeathfish.com, 2005
 */

/**
 * TODO Document me
 *
 * @author Gareth Jenkins-Jones
 */
public class md5test extends SimpleGame
{
    private static final String BODYMODEL = "models/md5/chars/marine.md5mesh";
    private static final String HEADMODEL = "models/md5/heads/sarge/sarge.md5mesh";
   
    private static final String HEADANIM = "models/md5/heads/sarge/sargeidle.md5anim";
    private static final String BODYANIM = "models/md5/chars/stand_point_forward.md5anim";
   
    private Model headModel;
    private Model bodyModel;
    private Animation bodyAnimation;
    private Animation headAnimation;
    private AnimationController bodyAnimator;
    private ModelTriMesh mtm;
    private MD5BumpShader bs;
    private Shader s;
    private Image bumpImage;
   
    private ModelInstance bodyInstance;
   
    public static void main(String[] args) throws IOException {

        final md5test app = new md5test();
        app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
        // Turn the logger off so we can see the XML later on
   
       
        app.start();
    }
   
    public md5test() throws IOException
    {
        bodyModel = loadModel( BODYMODEL );
        headModel = loadModel( HEADMODEL );
        bodyAnimation = loadAnimation( bodyModel, BODYANIM );
        headAnimation = loadAnimation( headModel, HEADANIM );
    }

    protected void simpleInitGame() {

 lightState.get( 0 ).setAmbient( new ColorRGBA(1.5f, 1.5f, 1.5f, 1.0f) );

       
        try
        {
           FileInputStream fis = new FileInputStream("AddedBumpOutput.tga");
           bumpImage=TGALoader.loadImage(fis);
       }
        catch(FileNotFoundException e){}
        catch(IOException e){}
           Texture bumpTexture= new Texture();
           bumpTexture.setImage(bumpImage);
             s = new MD5BumpShader(bumpTexture,bumpTexture);
             bs = new MD5BumpShader(bumpTexture,bumpTexture);
            
             mtm=new ModelTriMesh("bump");
       
       
          bs.setup(mtm);
          SimpleSkin ss = new SimpleSkin();
          ss.setShader("bump",bs);
        bodyInstance = new ModelInstance( bodyModel );
        bodyAnimator = new AnimationController( bodyInstance );
        // Add head

        ModelInstance head = new ModelInstance( headModel );
        AnimationController headAnimator = new AnimationController( head );
        headAnimator.addAnimation( headAnimation );
        bodyInstance.addChild( head, "Shoulders" );
        bodyAnimator.addAnimation( bodyAnimation );
       
        bodyInstance.setLocalScale( 0.25f );
       
        rootNode.detachAllChildren();
//        bodyInstance.setSkin(ss);
        rootNode.attachChild( bodyInstance );

        KeyBindingManager.getKeyBindingManager().set(
                "rotateLeft",
                KeyInput.KEY_Z );
        KeyBindingManager.getKeyBindingManager().set(
                "rotateRight",
                KeyInput.KEY_X );
    }
   
   
   
    protected void simpleUpdate() {
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("rotateRight",true))
            MD5BumpShader.pos += 0.01f;
       
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("rotateLeft",true))
            MD5BumpShader.pos -= 0.01f;
    }
   
    private Model loadModel( String path ) throws IOException
    {
        InputStream in = getClass().getResourceAsStream( "/" + path );
       
        if ( in == null )
            throw new FileNotFoundException( "Cannot find " + path );
       
        MD5MeshReader reader = new MD5MeshReader( in );
        reader.setClassLoader( getClass().getClassLoader() );
        return reader.scan();
    }
   
    private Animation loadAnimation( Model model, String path )
        throws IOException
    {
        InputStream in = getClass().getResourceAsStream( "/" + path );
       
        if ( in == null )
            throw new FileNotFoundException( "Cannot find " + path );
       
        MD5AnimReader animReader = new MD5AnimReader( in );
        animReader.setSkeleton( model.getSkeleton() );
        Animation animation = animReader.scan();
        return animation;
    }
}

Sorry for the slow response.



A Mesh holds the definition of all the triangles in the model, which vertices they are made of and which bones the vertices are attached to. The ModelInstance calls a Mesh during animation and tells it to skin a ModelTriMesh; that is, set up the positions of the vertices based on the bone transformations passed in (nothing to do with the Skin class - maybe I should change my naming scheme). A Mesh also has a name; this is used for selecting the Shader to use on a ModelTriMesh (see below).



Each model definition (i.e. each instance of the Model class) contains one or more meshes, defining the parts of the model; for instance, one Mesh for the body, one Mesh for a spanner held by the creature.



The Skin and Shader classes are there to allow skinning in the usual sense expected by 3d game players - to change the textures used on a model without changing its geometry.



The Shader class contains the definition of the skin for a mesh in the model. Just as each Model has many Meshes inside it, a ModelInstance has many ModelTriMesh objects inside it, one for each Mesh in the parent Model. Each ModelTriMesh has a Shader set, which will usually be used just to set up RenderStates on the ModelTriMesh (see http://www.chaosdeathfish.com/jme/jme-anim/src/showsrc.php?src=model/SimpleShader.java ), although it can do much more. For instance, the BumpShader calculates tangent vectors every frame.



In answer to your question about BumpShader, to enable it you also need to change MD5DefaultSkin, comment out line 127 and uncomment line 128 in http://www.chaosdeathfish.com/jme/jme-anim/src/showsrc.php?src=md5reader/MD5DefaultSkin.java - although d3anim.jar doesn’t (or shouldn’t) contain the normal maps, so you probably won’t see a huge change (or it may break completely). Up to you to try and work it out if you want.



Hope some of that helps, feel free to ask if you have any more questions.

are .mtr scripts supported for texture loading, over the weekend I realized… painfully, that I am no good at hand painting maps from unwrapped UVs and decided to stay the multi submaterial root for texturing which I’m much better at, if not an Idea of how to support them would be appreciated and I’ll try and do it myself.



I’m still thinking of ways to get my materials into single maps though and have been semi-successful, have another idea that I’ll be trying this weekend though.



thanks

Currently the only support for materials is the default diffuse map, and any other skin type you code yourself. I’ve been looking more at the maths and model management so far, my tries at bump mapping have failed so far… Another thing to add to my TODO list!

diffuse maps are good,



all I really need to know is can I use/do you support .mtr defs to read in the textures that relate to the material state, that is the only way to have multiple textures/diffusemaps on a single mesh from 3dsmax



as an example



models/mymodel/mat01 {    diffusemap   models/mymodel/mat01_d.tga    bumpmap      models/mymodel/mat01_local.tga    specularmap   models/mymodel/mat01_s.tga } models/mymodel/mat02 {    diffusemap   models/mymodel/mat02_d.tga    bumpmap      models/mymodel/mat02_local.tga    specularmap   models/mymodel/mat02_s.tga }




hope thats clear

Sorry, I probably wasn’t clear enough in my last post. I’ve had a hard day! Currently I don’t support .mtr file reading, but I’ll put it on my ever-growing TODO list.

if you can drop me a few pointers I’ll try.

VERSION 0.2 RELEASE



OK, I’ve finally had time to do some work on this again, and you’re all going to hate me - I’ve refactored and changed the API quite a lot. The old code is still available at http://www.chaosdeathfish.com/jme/jme-anim/v0.1/ if you’re still using it. The new code is available at the original location:



http://www.chaosdeathfish.com/jme/jme-anim



The main new features are:

  • Animation blending - self explanatory, allows animations to be blended together and cleanly faded in and out.
  • Translation/rotation updating - if this is turned on using setUpdatingLocalTransform( true ) the the local translation and rotation will be updated each update.
  • Milkshape3D binary importer - Does what it says on the tin :)
  • Easy animation subsets - use animation.subset( name, firstFrame, lastFrame ) where firstFrame is inclusive and lastFrame is exclusive. This creates a new animation from a subset of an existing animation, which is useful for model formats which only have one animation track (e.g. Milkshape3D)
    [/list:u]
    The main change to the API is a complete refactoring of most of the code :) This was mainly to separate out the skeletal/non-skeletal code to avoid hundreds of if checks to see if the object is skeletal. Now most of the core classes (Model, Animation, Animator, AnimationController, KeyFrame, ModelInstance) have a skeletal version which extends the non-skeletal version.

    The other change that'll impact you is a change in the model reading code - MD5MeshReader now extends model.file.ModelReader, and has an empty constructor - the input stream needs to be passed into the readModel method instead.

    The main change that you'll need to make to existing code is:
    • Change new ModelInstance( model ) to model.createInstance()
    • Change instance.addController( new AnimationController( instance ) ) to instance.addAnimationController()
    • Change new AnimationAnimator( animation ) to AnimationUtils.createAnimator( animation )
      [/list:u]
      And also change the way models are loaded. Look at the new version of MD5Test if you need an example.

      The javadocs are still a bit sparse, I'll get around to sorting them out one of these days. Sorry about that :(

      Currently the next thing on my TODO list is to get rid of the vertex and texture arrays used in ModelTriMesh and just use the Buffer versions instead to cut down on the memory usage (that's half the reason for the architecture I've chosen anyway).

      Next I'll probably be back to getting bump shaders and suchlike to work, and hopefully get .mtr scripts working.

      Sorry for the massive post. Any feedback is very much appreciated, as always. In particular, any bugs or features that I may've removed and not realised would be very useful!