[SOLVED] Semantics of getControl()

final AnimComposer animComposer=new AnimComposer();
spatial.addControl(animComposer);
animComposer.addAnimClip(new AnimClip("anim"));
animComposer.setCurrentAction("anim");

produces:

java.lang.NullPointerException
	at com.jme3.anim.tween.action.ClipAction.doInterpolate(ClipAction.java:25)
	at com.jme3.anim.tween.action.BlendableAction.interpolate(BlendableAction.java:52)
	at com.jme3.anim.AnimComposer.controlUpdate(AnimComposer.java:277)
	at com.jme3.scene.control.AbstractControl.update(AbstractControl.java:111)
	at com.jme3.scene.Spatial.runControlUpdate(Spatial.java:737)
	at com.jme3.scene.Spatial.updateLogicalState(Spatial.java:880)
	at com.jme3.scene.Node.updateLogicalState(Node.java:242)
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:261)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:153)
	at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:193)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:234)
	at java.base/java.lang.Thread.run(Thread.java:832)

but reacquiring the control somehow fixes it?

spatial.getControl(AnimComposer.class).setCurrentAction("anim");

I would expect to only need to use the accessor if I wasn’t in scope of the control’s reference

Your expectations are true. The getControl method should return the same object that you already have in the code you posted. I suspect the code you posted isn’t the whole story, though.

When do you call the getControl method? Is it some time later (in the next frame or more)?

Can you post both the code that works and the code that doesn’t without removing code you think is in-necessary? Just a simple “this works if you run it, and this doesn’t” example. It will help determine the difference in why calling the same method on the same object works, and why it doesn’t.

1 Like

The only way this would make any different at all is:

  1. you are calling it in a way different place and that’s actually different enough to matter
  2. you have an AnimComposer already on the spatial and getControl() is returning that one instead of the one you added.

The second one seems to fit the symptoms with the information provided… since we can’t see where you’ve called getControl().

2 Likes

Here’s a working example (with the alternative failing line commented out):

package core;
import com.jme3.anim.AnimClip;
import com.jme3.anim.AnimComposer;
import com.jme3.app.SimpleApplication;
import com.jme3.light.DirectionalLight;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
public class UnitTest extends SimpleApplication{
	public static void main(final String[] args){
		new UnitTest().start();
	}
	@Override
	public void simpleInitApp(){
		final var sun=new DirectionalLight();
		sun.setDirection(new Vector3f(-0.1f,-0.7f,-1.0f));
		rootNode.addLight(sun);
		final var scene=(Node)assetManager.loadModel("testCell.gltf.j3o");
		scene.getChildren().stream().filter(spatial->spatial.getUserDataKeys().stream().anyMatch(dataKey->dataKey.equals("animObj"))).forEach(spatial->{
			final AnimComposer animComposer=new AnimComposer();
			spatial.addControl(animComposer);
			animComposer.addAnimClip(new AnimClip("anim"));
//			animComposer.setCurrentAction("anim");
			spatial.getControl(AnimComposer.class).setCurrentAction("anim");
		});
		rootNode.attachChild(scene);
	}
}

http://www.mediafire.com/file/csakxahs17yo1pw/file

1 Like

testCell.gltf.j3o must already have an AnimComposer then.

getControl() is not doing any magic. It’s going through the list of added controls and returning the first one that matches the class.

You could prove that hypothesis by adding a

System.out.println("test:" + spatial.getControl(AnimComposer.class)) ;

at the top of your filter closure.

3 Likes

Right you are!
test:com.jme3.anim.AnimComposer@3f28c50

It never occurred to me that such an object would already be in the file itself. I thought my brain was going to melt. Thank you both!

3 Likes