Error with Collada

Hi !



I want use the Collada format for my game but my model doesn't load correctly.



My log file is :


17 f

Looks like the model and skeleton load, but the skeleton is not bound to the model?

The skeleton is bound to the model, the animation work correctly under 3DS Max in MAX format and in DAE format. So, I don't understand why jME tell that two bones are not attached to any vertices !

You're right, the model file is fine.  It's the code that you copied and pasted together.  You are resetting the collada importer before you actually get anything out of it:




        //tell the importer to load the mob boss
        ColladaImporter.load(modele, url, "model");
       
        //we can then retrieve the skin from the importer as well as the skeleton
  //      SkinNode sn = ColladaImporter.getSkinNode(ColladaImporter.getSkinNodeNames().get(0));
        Bone skel = ColladaImporter.getSkeleton(ColladaImporter.getSkeletonNames().get(0));
       
        //clean up the importer as we are about to use it again.
        ColladaImporter.cleanUp();  <


  BAD.  :)
      
        //this file might contain multiple animations, (in our case it's one)
        ArrayList<String> animations = ColladaImporter.getControllerNames();




Here's the cleaned up code, with the camera setup to be able to see the animation at all times:

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import com.jme.animation.AnimationController;
import com.jme.animation.Bone;
import com.jme.animation.BoneAnimation;
import com.jme.animation.SkinNode;
import com.jme.app.AbstractGame;
import com.jme.app.SimpleGame;
import com.jme.input.FirstPersonHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.light.PointLight;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Controller;
import com.jme.util.BoneDebugger;
import com.jmex.model.collada.ColladaImporter;


public class TestAnimDAE extends SimpleGame
{
    AnimationController ac;
    boolean boneOn = false;
    public static void main(String[] args)
    {
        TestAnimDAE app = new TestAnimDAE();
        app.setDialogBehaviour(AbstractGame.ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }
   
    protected void simpleUpdate()
    {
        if( KeyBindingManager.getKeyBindingManager().isValidCommand("bones", false))
        {
            boneOn = !boneOn;
        }
    }

    protected void simpleRender()
    {
        //If we want to display the skeleton use the BoneDebugger.
        if(boneOn)
        {
            BoneDebugger.drawBones(rootNode, display.getRenderer(), true);
        }
    }

    protected void simpleInitGame()
    {
        KeyBindingManager.getKeyBindingManager().set("bones", KeyInput.KEY_SPACE);
       
        //Our model is Z up so orient the camera properly.
        cam.setAxes(new Vector3f(-1,0,0), new Vector3f(0,0,1), new Vector3f(0,1,0));
        cam.setLocation(new Vector3f(0, 100, 20));
        cam.setFrustumFar(2000);
        cam.lookAt(new Vector3f(0,-200,20), Vector3f.UNIT_Z);
        input = new FirstPersonHandler(cam, 80, 1);
       
        //url to the location of the model's textures
        URL url = TestAnimDAE.class.getClassLoader().getResource("collada/");
       
        //this stream points to the model itself.
        InputStream modele = TestAnimDAE.class.getResourceAsStream("testModele.dae");
        if (modele == null)
        {
            System.out.println("Unable to find file, did you include jme-test.jar in classpath?");
            System.exit(0);
        }
       
        //tell the importer to load the mob boss
        ColladaImporter.load(modele, url, "model");
       
        //we can then retrieve the skin from the importer as well as the skeleton
        SkinNode sn = ColladaImporter.getSkinNode(ColladaImporter.getSkinNodeNames().get(0));
        Bone skel = ColladaImporter.getSkeleton(ColladaImporter.getSkeletonNames().get(0));
      
        //this file might contain multiple animations, (in our case it's one)
        ArrayList<String> animations = ColladaImporter.getControllerNames();
        if (animations != null) {
            System.out.println("Number of animations: " + animations.size());
            for(int i = 0; i < animations.size(); i++)
            {
                System.out.println(animations.get(i));
            }
        }
       
        //Obtain the animation from the file by name
        BoneAnimation anim1 = ColladaImporter.getAnimationController(animations.get(0));
       
        //set up a new animation controller with our BoneAnimation
        ac = new AnimationController();
        ac.addAnimation(anim1);
        ac.setRepeatType(Controller.RT_WRAP);
        ac.setActive(true);
        ac.setActiveAnimation(anim1);
       
        //assign the animation controller to our skeleton
        skel.addController(ac);
       
        //attach the skeleton and the skin to the rootnode. Skeletons could possibly
        //be used to update multiple skins, so they are seperate objects.
        rootNode.attachChild(sn);
        rootNode.attachChild(skel);
       
        rootNode.updateGeometricState(0, true);
        //all done clean up.
        ColladaImporter.cleanUp();
       
        lightState.detachAll();
       
        PointLight pl = new PointLight();
        pl.setAmbient(new ColorRGBA(0.5f,0.5f,0.5f,1));
        pl.setDiffuse(new ColorRGBA(1,1,1,1));
        pl.setLocation(new Vector3f(10,-50,20));
        pl.setEnabled(true);
        lightState.attach(pl);
    }
}


thanks for your help but there is another problem at this line


skel.addController(ac);



This is the error message :


java.lang.NullPointerException
   at TestAnimDAE.simpleInitGame(TestAnimDAE.java:100)
   at com.jme.app.BaseSimpleGame.initGame(Unknown Source)
   at com.jme.app.BaseGame.start(Unknown Source)
   at TestAnimDAE.main(TestAnimDAE.java:29)

If you are using the latest jME code from CVS, probably there is newer code in ColladaImporter that hasn't been checked in yet.  (because it works fine locally)

Have you tested the code with my Collada file ? I think that the export from 3DS Max have generate a bad Collada file because skel equals null when I use my Collada File and the code works fine when I use the example file (man.dae).



So, my question is how to make a good collada file with 3DS Max ?

Yes, I used your file to test.

ok, so, if there are new code locally when this code will be commit to the CVS ?

I'm in the midst of changing how it deals with extra tags, using a plugin system. Once this is done I'll do another cvs commit. Hopefully, mid-next week.

ok, great, I'm very impatient :smiley:

Hi,



I have seen that the ColladaImporter was updated on the CVS. So, I have downloaded the last nightly build to test it but I have already the same problem :frowning:



I give you my new Collada File : http://rorolepro01.free.fr/omega/test.DAE

This file was exported from 3DS Max 8 with the plugin ColladaMax 3.02



This is my class file :



import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import com.jme.animation.AnimationController;
import com.jme.animation.Bone;
import com.jme.animation.BoneAnimation;
import com.jme.animation.SkinNode;
import com.jme.app.AbstractGame;
import com.jme.app.SimpleGame;
import com.jme.input.FirstPersonHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.light.PointLight;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Controller;
import com.jme.util.BoneDebugger;
import com.jmex.model.collada.ColladaImporter;

public class TestAnimDAE extends SimpleGame
   {
    AnimationController ac;
    boolean boneOn = false;
    public static void main(String[] args)
    {
       TestAnimDAE app = new TestAnimDAE();
        app.setDialogBehaviour(AbstractGame.ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }
   
    protected void simpleUpdate()
    {
        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "bones", false ) )
        {
            boneOn = !boneOn;
        }
    }

    protected void simpleRender()
    {
        //If we want to display the skeleton use the BoneDebugger.
        if(boneOn)
        {
            BoneDebugger.drawBones(rootNode, display.getRenderer(), true);
        }
    }

    protected void simpleInitGame()
    {
        KeyBindingManager.getKeyBindingManager().set( "bones", KeyInput.KEY_SPACE );
       
        //Our model is Z up so orient the camera properly.
        cam.setAxes(new Vector3f(-1,0,0), new Vector3f(0,0,1), new Vector3f(0,1,0));
        cam.setLocation(new Vector3f(0,-100,20));
        input = new FirstPersonHandler(cam, 80, 1);
       
        //url to the location of the model's textures
        URL url = TestAnimDAE.class.getClassLoader().getResource("./collada/");
        //this stream points to the model itself.
        InputStream mobboss = TestAnimDAE.class.getClassLoader().getResourceAsStream("./test.DAE");
        //this stream points to the animation file. Note: You don't necessarily
        //have to split animations out into seperate files, this just helps.
        InputStream animation = TestAnimDAE.class.getClassLoader().getResourceAsStream("./test.DAE");
        if (mobboss == null)
        {
            System.out.println("Unable to find file, did you include jme-test.jar in classpath?");
            System.exit(0);
        }
        //tell the importer to load the mob boss
        ColladaImporter.load(mobboss, url, "model");
        //we can then retrieve the skin from the importer as well as the skeleton
        SkinNode sn = ColladaImporter.getSkinNode(ColladaImporter.getSkinNodeNames().get(0));
        Bone skel = ColladaImporter.getSkeleton(ColladaImporter.getSkeletonNames().get(0));

        //clean up the importer as we are about to use it again.
        ColladaImporter.cleanUp();
       
        //load the animation file.
        ColladaImporter.load(animation, url, "anim");
        //this file might contain multiple animations, (in our case it's one)
        ArrayList<String> animations = ColladaImporter.getControllerNames();
        if(animations != null)
        {
           System.out.println("Number of animations: " + animations.size());
           for(int i = 0; i < animations.size(); i++)
           {
               System.out.println(animations.get(i));
           }
           //Obtain the animation from the file by name
           BoneAnimation anim1 = ColladaImporter.getAnimationController(animations.get(0));
          
           //set up a new animation controller with our BoneAnimation
           ac = new AnimationController();
           ac.addAnimation(anim1);
           ac.setRepeatType(Controller.RT_WRAP);
           ac.setActive(true);
           ac.setActiveAnimation(anim1);
          
           //assign the animation controller to our skeleton
           skel.addController(ac);
        }
       
        //attach the skeleton and the skin to the rootnode. Skeletons could possibly
        //be used to update multiple skins, so they are seperate objects.
        rootNode.attachChild(sn);
        rootNode.attachChild(skel);
       
        rootNode.updateGeometricState(0, true);
        //all done clean up.
        ColladaImporter.cleanUp();
       
        lightState.detachAll();
       
        PointLight pl = new PointLight();
        pl.setAmbient(new ColorRGBA(0.5f,0.5f,0.5f,1));
        pl.setDiffuse(new ColorRGBA(1,1,1,1));
        pl.setLocation(new Vector3f(10,-50,20));
        pl.setEnabled(true);
        lightState.attach(pl);
    }
}



And this is the error message :


java.lang.NullPointerException
   at TestAnimDAE.simpleInitGame(TestAnimDAE.java:100)
   at com.jme.app.BaseSimpleGame.initGame(Unknown Source)
   at com.jme.app.BaseGame.start(Unknown Source)
   at TestAnimDAE.main(TestAnimDAE.java:28)



The problem is on the line 100 skel.addController(ac);, the variable skel equals null ! The ColladaImporter don't load the skeleton but it load correctly the modele et the animations. Where is the problem ?

Examining file now (have a bit of time before my next meeting).

Ok, first results… I was able to load the model in our editor here at work, but it didn't look write (a bit of a mess of triangles). The bones looked good, however. I then went to add the animations and noticed that all animations are stored as individual tags, rather than under a common parent. That is, instead of <animation name="walk"> with every bone transformation under walk, all bone transformations are under animation library.



I modified the test to manually place these objects into a single bone animation, and fixed the NPE issues. This gave me a properly animated skeleton (the skin is still not applied correctly though). Not sure what the issue is there.



Here is the modified test that works, and I will check in the null checks in a couple minutes:



package test;

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import com.jme.animation.AnimationController;
import com.jme.animation.Bone;
import com.jme.animation.BoneAnimation;
import com.jme.animation.SkinNode;
import com.jme.app.AbstractGame;
import com.jme.app.SimpleGame;
import com.jme.input.FirstPersonHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.light.PointLight;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Controller;
import com.jme.util.BoneDebugger;
import com.jmex.model.collada.ColladaImporter;

public class TestAnimDAE extends SimpleGame {
   AnimationController ac;

   boolean boneOn = false;

   public static void main(String[] args) {
      TestAnimDAE app = new TestAnimDAE();
      app.setDialogBehaviour(AbstractGame.ALWAYS_SHOW_PROPS_DIALOG);
      app.start();
   }

   protected void simpleUpdate() {
      if (KeyBindingManager.getKeyBindingManager().isValidCommand("bones",
            false)) {
         boneOn = !boneOn;
      }
   }

   protected void simpleRender() {
      // If we want to display the skeleton use the BoneDebugger.
      if (boneOn) {
         BoneDebugger.drawBones(rootNode, display.getRenderer(), true);
      }
   }

   protected void simpleInitGame() {
      KeyBindingManager.getKeyBindingManager().set("bones",
            KeyInput.KEY_SPACE);

      // Our model is Z up so orient the camera properly.
      cam.setAxes(new Vector3f(-1, 0, 0), new Vector3f(0, 0, 1),
            new Vector3f(0, 1, 0));
      cam.setLocation(new Vector3f(0, -100, 20));
      input = new FirstPersonHandler(cam, 80, 1);

      // url to the location of the model's textures
      URL url = TestAnimDAE.class.getClassLoader().getResource("./collada/");
      // this stream points to the model itself.
      InputStream mobboss = TestAnimDAE.class.getClassLoader()
            .getResourceAsStream("./test.DAE");
      // this stream points to the animation file. Note: You don't necessarily
      // have to split animations out into seperate files, this just helps.
      InputStream animation = TestAnimDAE.class.getClassLoader()
            .getResourceAsStream("./test.DAE");
      if (mobboss == null) {
         System.out
               .println("Unable to find file, did you include jme-test.jar in classpath?");
         System.exit(0);
      }
      // tell the importer to load the mob boss
      ColladaImporter.load(mobboss, url, "model");
      // we can then retrieve the skin from the importer as well as the
      // skeleton
      SkinNode sn = ColladaImporter.getSkinNode(ColladaImporter
            .getSkinNodeNames().get(0));
      Bone skel = ColladaImporter.getSkeleton(ColladaImporter
            .getSkeletonNames().get(0));

      // clean up the importer as we are about to use it again.
      ColladaImporter.cleanUp();

      // load the animation file.
      ColladaImporter.load(animation, url, "anim");
      // this file might contain multiple animations, (in our case it's one)
      ArrayList<String> animations = ColladaImporter.getControllerNames();
      if (animations != null) {
         //set up a new animation controller with our BoneAnimation
         ac = new AnimationController();
         BoneAnimation anim = new BoneAnimation();
         System.out.println("Number of animations: " + animations.size());
         for (int i = 0; i < animations.size(); i++) {
            anim.addBoneAnimation(ColladaImporter.getAnimationController(animations.get(i)));
            System.out.println(animations.get(i));
         }
         ac.addAnimation(anim);
         ac.setRepeatType(Controller.RT_WRAP);
         ac.setActive(true);
         ac.setActiveAnimation(anim);

         // assign the animation controller to our skeleton
         skel.addController(ac);
      }

      // attach the skeleton and the skin to the rootnode. Skeletons could
      // possibly
      // be used to update multiple skins, so they are seperate objects.
      rootNode.attachChild(sn);
      rootNode.attachChild(skel);

      rootNode.updateGeometricState(0, true);
      // all done clean up.
      ColladaImporter.cleanUp();

      lightState.detachAll();

      PointLight pl = new PointLight();
      pl.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1));
      pl.setDiffuse(new ColorRGBA(1, 1, 1, 1));
      pl.setLocation(new Vector3f(10, -50, 20));
      pl.setEnabled(true);
      lightState.attach(pl);
   }
}

Ok, that's checked in… next will need to determine why the skin mesh has gone boom.

I have edited manually my Collada File to add the tag <animation id="walk"> that contain all animation tags and I use your class file but I have already the same problem, skel=null :cry:



Here is my collada file that I have changed, maybe I make a mistake : http://rorolepro01.free.fr/omega/test_modified.DAE



For the skin, it's logical if the mesh don't have a texture, I dont define any texture under 3DS Max