Using AnimationFactory in my animation

Hey guys, I use cinematics for animation and PositionTrack and RotationTrack are now deprecated so it’s time to update.



I’ve been struggling today replacing PositionTrack with AnimationFactory and getting a nullpointerexception error.



I am reading a trace. My previous PositionTrack function was simple, it gets called every time a certain trigger is read from the trace. For example if COAST was read in the trace it call this function:



[java]

public void coastDrives(String id, float trigger_time, float x, float y,

float z, float duration) {



final Spatial spatialToMove = rootNode.getChild(id);



cinematic.addCinematicEvent(trigger_time, new PositionTrack(

spatialToMove, new Vector3f(x, y, z), duration,

LoopMode.DontLoop));



[/java]



This worked like a charm. Now with the new code I did the following change:



[java]

public void coastDrives(String id, float trigger_time, float x, float y,

float z, float duration) {



final Spatial spatialToMove = rootNode.getChild(id);



AnimationFactory factory = new AnimationFactory(duration, “coast”);



factory.addTimeTranslation(duration, new Vector3f(x, y, z));



AnimControl control = new AnimControl();

control.addAnim(factory.buildAnimation());

spatialToMove.addControl(control);



cinematic.addCinematicEvent(0, new AnimationTrack(spatialToMove, “coast”));



[/java]



and I get the following error:



[java]

Feb 17, 2012 1:48:56 PM com.jme3.app.Application handleError

SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

java.lang.NullPointerException

at com.jme3.cinematic.events.AnimationTrack.onPlay(AnimationTrack.java:129)

at com.jme3.cinematic.events.AbstractCinematicEvent.play(AbstractCinematicEvent.java:109)

at com.jme3.cinematic.KeyFrame.trigger(KeyFrame.java:59)

at com.jme3.cinematic.Cinematic.onUpdate(Cinematic.java:227)

at com.jme3.cinematic.events.AbstractCinematicEvent.internalUpdate(AbstractCinematicEvent.java:139)

at com.jme3.cinematic.Cinematic.update(Cinematic.java:196)

at com.jme3.app.state.AppStateManager.update(AppStateManager.java:249)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:255)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:149)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:182)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:223)

at java.lang.Thread.run(Thread.java:619)

Feb 17, 2012 1:48:56 PM com.jme3.renderer.lwjgl.LwjglRenderer cleanup

INFO: Deleting objects and invalidating state

Feb 17, 2012 1:48:56 PM com.jme3.input.lwjgl.LwjglMouseInput destroy

INFO: Mouse destroyed.

Feb 17, 2012 1:48:56 PM com.jme3.input.lwjgl.LwjglKeyInput destroy

INFO: Keyboard destroyed.

Feb 17, 2012 1:48:56 PM com.jme3.system.lwjgl.LwjglAbstractDisplay deinitInThread

INFO: Display destroyed.

[/java]

I guess your coastDrive is called several times, so you end up adding several AnimControl on your spatial which is bad because you’re not sure you get the good control for the good animation later.

Instead of creating the control each time, just fetch it in the spatial control list like this



[java]

factory.addTimeTranslation(duration, new Vector3f(x, y, z));



AnimControl control = spatialToMove.getControl(AnimControl.class);

if( control == null){

control = new AnimControl();

spatialToMove.addControl(control);

}



control.addAnim(factory.buildAnimation());

[/java]

It makes perfect sense what you’re saying however that didn’t solve it :S Is the problem that I am creating the animation factory over and over again? If so, I can’t see any other way to do it as the duration will always be changing.



so if the function becomes this:



[java] public void coastDrives(String id, float trigger_time, float x, float y,

float z, float duration) {



// current event’s drive called

final Spatial spatialToMove = rootNode.getChild(id);





AnimationFactory factory = new AnimationFactory(duration, “coast”);



factory.addTimeTranslation(duration, new Vector3f(x, y, z));



AnimControl control = spatialToMove.getControl(AnimControl.class);

if( control == null){

control = new AnimControl();

spatialToMove.addControl(control);

}



control.addAnim(factory.buildAnimation());



cinematic.addCinematicEvent(trigger_time, new AnimationTrack(spatialToMove, “coast”));

}[/java]



the error persists. In fact doing some further debugging, even if coast is called only once, you still get that null ptr error

could you update to latest nightly and retest please?

It may not work better but AnimationTrack changed since then and the line 129 is not the same.

So at least we’ll know what is null exactly. ( I assumed it was the anim in the first place but apparently not).

I did and getting the error at line 130.



[java]

Feb 21, 2012 3:28:06 PM com.jme3.app.Application handleError

SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

java.lang.NullPointerException

at com.jme3.cinematic.events.AnimationTrack.onPlay(AnimationTrack.java:130)

at com.jme3.cinematic.events.AbstractCinematicEvent.play(AbstractCinematicEvent.java:103)

at com.jme3.cinematic.KeyFrame.trigger(KeyFrame.java:59)

at com.jme3.cinematic.Cinematic.onUpdate(Cinematic.java:211)

at com.jme3.cinematic.events.AbstractCinematicEvent.internalUpdate(AbstractCinematicEvent.java:127)

at com.jme3.cinematic.Cinematic.update(Cinematic.java:194)

at com.jme3.app.state.AppStateManager.update(AppStateManager.java:249)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:233)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:149)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:182)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:223)

at java.lang.Thread.run(Thread.java:619)

Feb 21, 2012 3:28:06 PM com.jme3.renderer.lwjgl.LwjglRenderer cleanup

INFO: Deleting objects and invalidating state

Feb 21, 2012 3:28:07 PM com.jme3.scene.Node detachChildAt

INFO: Gui Node (Node): Child removed.

Feb 21, 2012 3:28:07 PM com.jme3.scene.Node detachChildAt

INFO: Gui Node (Node): Child removed.

Feb 21, 2012 3:28:07 PM com.jme3.input.lwjgl.LwjglMouseInput destroy

INFO: Mouse destroyed.

Feb 21, 2012 3:28:07 PM com.jme3.input.lwjgl.LwjglKeyInput destroy

INFO: Keyboard destroyed.

Feb 21, 2012 3:28:07 PM com.jme3.system.lwjgl.LwjglAbstractDisplay deinitInThread

INFO: Display destroyed.

[/java]

mhhh ok

Do you call the coastDrive method in the simpleInit? or do you call it in the update loop?



the channel may be null if you are calling it in the update loop.

No I call it from the simpleinit

I’m afraid i’m gonna have to play the Test Case card :stuck_out_tongue:

Or at least post your init code because i don’t get what’s happening here.

Let me post my init code as my code is a little bit complicated for a testCase:



[java]

@Override

public void simpleInitApp() {



// add/remove screen graphics stats

setDisplayFps(false);

setDisplayStatView(false);







// initialize Jme Cinematics

cinematic = new Cinematic(rootNode, simulationTime);

stateManager.attach(cinematic);



cinematic.setSpeed(animation_speed);

animationSpeed = (cinematic.getSpeed() + "x");





/**

  • Start reading the trace file (after everything is initialized) this
  • is the main animation thread where only a small portion of the file
  • is parsed and the rest is parsed on a separate thread (below) to
  • optimize loading time and performance. (THREAD 1)

    */

    Jme3Adapter reader = new Jme3Adapter(this, 0, 1.0f);



    /**
  • Separate thread that reads the rest of the file concurrently with the
  • execution

    /

    final LoaderAppState loader = new LoaderAppState(this);

    getStateManager().attach(loader);

    loader.setEnabled(true);



    // Filter effect related data

    fpp = new FilterPostProcessor(assetManager);

    fade = new FadeFilter(2); // Filter duration is set for 2 seconds

    fpp.addFilter(fade);

    viewPort.addProcessor(fpp);



    // set up camera related events - spatial to follow

    // >>> TODO connect it to Swing so user can choose which drive to follow

    spatialToFollow = rootNode.getChild(1);





    // Attach TimerAppState (animation timer)

    tp = new TimerAppState(this);

    tp.setEnabled(true);

    getStateManager().attach(tp);







    cinematic.addListener(new CinematicEventListener() {



    // when user presses ENTER, the animation plays

    public void onPlay(CinematicEvent cinematic) {



    System.out.println(“play/resume”);



    // enable/disable chaseCam

    // chaseCam.setEnabled(true); // chase drive camera (can’t see

    // difference till you have a floor)

    }

    // when user presses ENTER (for a 2nd time), the animation pauses

    public void onPause(CinematicEvent cinematic) {

    // chaseCam.setEnabled(false);

    System.out.println(“pause”);

    }



    public void onStop(CinematicEvent cinematic) {

    // chaseCam.setEnabled(false);

    inputManager.clearMappings();

    System.out.println(“stop”);

    }

    });



    // Initializes bitmap the animation timer on the animation screen

    animationFont = assetManager.loadFont(“Interface/Fonts/Default.fnt”);

    animationText = new BitmapText(animationFont, false);







    /
    * Animation Boolean Functions */

    // enable Fly cam

    enableFlyCam(true);



    // enable Chase cam (can override flycam) - also need to set chaseCam

    // from onPlay to off otherwise

    // you will get an error at time 0 (NullPointerException)

    enableChaseCam(false, cam, spatialToFollow);





    // Fade In Effect at the beginning

    fadeInEffect(true);



    // initialize keyboard

    initInputs();



    // prints animation speed

    printAnimationSpeed();



    turnOffMouse(mouseControl);





    }

    [/java]



    so while reading the trace file, the events get called for example whenever I reach COAST in that trace file below, the coast function is triggered:



    [java]time 345.674602305942

    coast car_34 0.0 15.972 0.0 8.941538461538462

    time 351.4166082989194

    changeColor car_140 0.0 1.0 0.0 1.0

    time 354.6161407674805

    accelerate car_1 0.0 17.272 0.0 2.0 1.3 -0.65 89.99999999999825[/java]

so I prepared a testCase and there is something I don’t understand, although I don’t get the null pointer error I am not getting the result anticipated when several COAST events are called see attached project under http://theforeigners.org/jme3/



Instrcutions: run the main select the tracefile available inthe folder and run, press escape. IT works fine with one animation event, when multiple, they get meshed or something so



(1) how do I solve that? what am I doing wrong?



(2) why it doesn’t work in Eclipse and I get the nullpointer error?



thanks



Update:



I tested the trace with only one coast event called and I no longer get the null pointer, so it’s really whenever multiple coast events are called. And the channel is what’s null on the onPlay() in AnimatrionTrack.java

answering this post http://hub.jmonkeyengine.org/groups/general-2/forum/topic/jumping-backwards-in-time/?topic_page=2&num=15#post-166238



I downloaded your test case and i don’t have any null pointer.

please post something that demonstrates the error.

1 Like

The testcase I sent you was not working until I reverted back to the stable version in the sdk. Also, I reverted back to the stable version in my Eclipse project to be sure that I am synchronized. This is weird as it is working perfectly like u said on the SDK but not well on my Eclipse.using the SAME EXACT code I sent you!! any idea why? again @nehon I can’t express how thankful I am for you taking the time to look into this.

I get that damn error:



[java]SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

java.lang.NullPointerException

at com.jme3.cinematic.events.AnimationTrack.onPlay(AnimationTrack.java:129)

at com.jme3.cinematic.events.AbstractCinematicEvent.play(AbstractCinematicEvent.java:109)

at com.jme3.cinematic.KeyFrame.trigger(KeyFrame.java:59)

at com.jme3.cinematic.Cinematic.onUpdate(Cinematic.java:227)

at com.jme3.cinematic.events.AbstractCinematicEvent.internalUpdate(AbstractCinematicEvent.java:138)

at com.jme3.cinematic.Cinematic.update(Cinematic.java:196)

at com.jme3.app.state.AppStateManager.update(AppStateManager.java:249)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:255)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:149)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:182)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:223)

at java.lang.Thread.run(Thread.java:619)

Mar 5, 2012 4:23:09 PM com.jme3.renderer.lwjgl.LwjglRenderer cleanup

INFO: Deleting objects and invalidating state

Mar 5, 2012 4:23:09 PM com.jme3.input.lwjgl.LwjglMouseInput destroy

INFO: Mouse destroyed.

Mar 5, 2012 4:23:09 PM com.jme3.input.lwjgl.LwjglKeyInput destroy

INFO: Keyboard destroyed.

Mar 5, 2012 4:23:09 PM com.jme3.system.lwjgl.LwjglAbstractDisplay deinitInThread

INFO: Display destroyed.[/java]

You mean you have this without changing anything from your testcase?

that’s indeed strange.



EDIT : mhh wait…the testcase you linked above is a netbeans project. How did you set it up if you use eclipse?

yeah I am definitely sure when I sent it at the time there was an error - all I did was revert back to the Stable version and it works like a charm but only on the SDK. Although I reverted to stable on my Eclipse I am still getting this error. However the error was not a null pointer in the SDK , it was simply the cars not behaving as instructed or sometimes not moving at all.


EDIT : mhh wait….the testcase you linked above is a netbeans project. How did you set it up if you use eclipse?


I copied and pasted the functions into a new project in Eclipse.

could it be a library issue?

ok so I know where the error is but not sure how to fix it. I use two threads to read my trace (and I was reading it from 1 trace from the SDK and thats why i didn’t see the error) so in simpleInit I have:



[java]Jme3Adapter reader = new Jme3Adapter(this, 0, 1.0f);

/**

  • Separate thread that reads the rest of the file concurrently with the
  • execution

    /

    final LoaderAppState loader = new LoaderAppState(this);

    getStateManager().attach(loader);

    loader.setEnabled(true);

    [/java]



    the second thread triggers the null pointer. The 2nd thread code is:



    [java]

    public class LoaderAppState extends AbstractAppState {



    float totalTime = 0;



    protected boolean active = true;



    // create an instance of the Simpleapplication inside Jme3Cinematics

    private final Jme3Cinematics jme3Cinematics;



    private final File traceFile;



    // Node that will hold the rootNode from Jme3Cinemtics (main)

    private Node rootNode = new Node("Root Node");





    // Multithreading: The future that is used to check the execution status

    Future future = null;



    // Multithreading: The adapter object that performs the long-running task

    Jme3Adapter adapter = null;



    // This constructor creates a new executor with a core pool size of 4.

    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(4);





    /
    * Constructor that initializes the animation speed /

    public LoaderAppState(Jme3Cinematics cinematics) {

    assert cinematics != null : "Jme3Cimenatics must be provided to LoaderAppState";

    this.jme3Cinematics = cinematics;

    jme3Cinematics.getAssetManager();

    this.traceFile = jme3Cinematics.getTraceFile();

    assert traceFile.exists() : "invalid trace file:" + traceFile.toString();

    }





    @Override

    public void initialize(AppStateManager stateManager, Application app) {



    super.initialize(stateManager, app);

    }



    /
    * A self-contained time-intensive task /

    Callable<Jme3Adapter> fileLoader = new Callable<Jme3Adapter>()

    {

    public Jme3Adapter call() throws Exception

    {



    System.out.println("LOADING THREAD RUNNING IN BACKGROUND");



    //determine the number of lines in the trace file

    LineNumberReader reader = new LineNumberReader(new FileReader(traceFile.getPath()));

    String lineRead = "";

    while ((lineRead = reader.readLine()) != null) {

    }

    int lineCount = reader.getLineNumber();



    // Now continue parsing from where we left of

    Jme3Adapter ad =new Jme3Adapter(jme3Cinematics,1.01f, lineCount);



    //Process the data and pass it back to Jme3Cinematics

    return ad;

    }

    };





    @Override

    public void update( float tpf )

    {



    System.out.println("Thread reading…");





    // if animation is off break loop

    if (!active) {

    return;

    }



    /
    * Separate thread that reads the rest of the file concurrently with the execution */

    try

    {

    //If we have not started a callable yet, do so!

    if(adapter == null && future == null)

    {

    //Thread starts!

    future = executor.submit(fileLoader); // must be a callable

    }

    //If we have started a callable already, we check the status

    else if(future != null)

    {

    //Get the thread data when its done

    if(future.isDone())

    {

    adapter = (Jme3Adapter) future.get();

    future = null;



    // make sure to kill the thread to avoid a memory leak

    executor.shutdown();

    active = false;

    System.out.println("Thread is completed!!");



    }

    else if(future.isCancelled())

    {

    //Set future to null. Maybe we succeed next time…

    future = null;

    }



    }

    }

    catch(Exception e){

    e.printStackTrace();

    }



    if(adapter == null){

    System.out.println("Adapter Null");

    }



    }





    // function that enables the timer animation

    public void setEnabled(boolean enabled) {

    this.active = enabled;

    }



    // function that checks if the timer animation is enabled

    public boolean isEnabled() {

    return active;

    }



    // function that gets the rootNode from the main (Jme3Cinematics)

    public Node getRootNode(){

    return rootNode;

    }





    }[/java]





    It worked perfectly fine with PositionTrack so not sure why is it causing an issue now