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
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.
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