BlendShapes

Hello Guys,

I’ve got a question concerning BlendShape Animation. For ExamIple I want to create a smiling face. To create this face I’ve got 2 Meshes at hand, one neutral face with no expression and one smiling face with an open mouth. Say I want the weighted sum of them, something like
0.5 * neutral face + 0.5 * smiling face = something between these two Meshes.

Is something like this somehow possible with jme3? I know that it doesn’t support ShapeKey Animation right now and that it only supports BoneAnimation. Is this possible with something different than BoneAnimation?

Thank you!

There is no built in way to do this.
pose animation is on the todo since a long time.
There is an embryo of solution in the engine with a Pose class and PoseTrack (that is now deprecated, and I’m not sure why).
It’s not working at all, but If you feel like looking into it, that’d be greatly appreciated.

1 Like

Could we maybe brainstorm a little bit?

I found out that I can create an animation using JME. So I could use my two meshes and set keyframes. The first keyframe would be at the neutral face and the second one at the smiling face. JME would be able to interpolate between these two, right?

Or did I missunderstood something?

In its current stage, JME can only animate one mesh. It cannot interpolate it WRT another one.

@Kidow said: Could we maybe brainstorm a little bit?

I found out that I can create an animation using JME. So I could use my two meshes and set keyframes. The first keyframe would be at the neutral face and the second one at the smiling face. JME would be able to interpolate between these two, right?

Or did I missunderstood something?


Well we have to find the correct balance between computation and memory usage. For example, Bone animation is entirely data driven (the animation knwos the transformation of a bone for each frame of the animation.) because computing complex movements of bones often implies Inverse Kinematic constrains implementation and computing them in real time is not optimal.
For pose animation, it’s more a matter of linear interpolation (maybe a bit more complex, with some ease in or ease out system) between keyframed mesh.
But having 1 complete mesh per key frames looks very expensive.
we need an approach to store the animation data in a different way (storing only the transformed vertices).

The Pose class in JME is exactly that. It’s basically a keyframe, that knows the indices of the transformed vertices, and the position in model space of those vertices.
A pose animation would just be a list of Pose and the PoseTrack would interpolate vertices’ position and apply it to the real mesh between those poses.

For now it seems that poses only support translation though, no rotation or scale. I guess that’s good enough, but can be constraining in some cases. Also there is a chance that this was greatly inspired by how pose animation is implemented in Ogre, because our bone animation system is basically the same as ogre.
It could be interesting to peek into ogre’s code.

1 Like

Thanks for the information!
Okay so I read into it and I decided to work with only one mesh. Right now I’m working with blender and I try to use ShapeKeys with JME3. I use the OgreExporter.
I started to extend the MeshLoader.java so that JME3 is able to interpret the pose tag and poseOffset tag. There I got confused, because I don’t really know how and where I register my poses. I know about the Pose and the Posetrack.

I collected all data to create a pose and I made an array out of them, just like PoseTrack expects it.

And right now I don’t know what to do. I think I need to link the poses to the mesh. But how do I do this?

Thanks!

EDIT:

@nehon said:

The Pose class in JME is exactly that. It’s basically a keyframe, that knows the indices of the transformed vertices, and the position in model space of those vertices.
A pose animation would just be a list of Pose and the PoseTrack would interpolate vertices’ position and apply it to the real mesh between those poses.
.


I always misread those lines. Right now I know what to do… at least I think I do :amused:

1 Like

Hello,

it’s me again. I got a question concerning PoseTrack. I don’t understand what’s the use of the inner class PoseFrame?

Thanks!

@Kidow Sorry for the delay, I’ve been quite busy at work lately.

To me it works like this :
An animation has key framed poses : the Pose class that describes the affected vertices (the indice array) and the offset to apply to their original position (the offsett array).

The naive approach would be to have a Pose for each frame but the amount of data held in memory would be sub optimal

I guess PoseFrame is there to optimize the amount of data held in memory.
For each frame, you have a PoseFrame that holds a reference to the start and end key framed Poses it’s between (in the poses array) ,and associate a weight to each of these frames (in thr weights array).
The weight is baked I guess and is the result of an interpolation (linear or whatever) between the 2 key frames.

Example :
you have an animation over 100 frames
Let’s say you have 3 Poses in your animation pose1, pose2, pose3.
pose1 is at frame 0
pose2 is at frame 50
pose3 is at frame 100

The PoseFrame for frame 10 would have poses[1] reference pose1 and poses[2] reference pose2.
then (given you have a linear interpolation) weight[1] would be 0.2f (10/50) and weight[2] would be 0.8 ((50-10)/50).

so this way you just have to weight each poses and apply them to the original vertex position on each frame. and you just hold 3 poses in memory and only 2 floats an 2 reference for each frames of the animation.

That’s kind of powerful because the implementation is independent from the type of interpolation you use, the storage is minimal and the computation cost on each frame is very low.

Hey @nehon thanks for all the information. That helped alot. But I’m still not a 100% sure if I’m using everything right. This is just some kind of pseudocode, with questions. Could you take a look, please?

[java]
// All Poses,every Pose needs to be in an own Animation(25fps)
Pose[] poseArray = null;
// posetrack to store the data for one Animation
PoseTrack t = null;
// Mesh
Mesh mesh = null;
for (int i = 0; i< poseArray.length; i++){
AnimationFactory fac = new AnimationFactory(25f, poseArray[i].getName());
// do I need to specify Keyframes?
// fac.addKeyFrameTranslation(0, translation);
// fac.addKeyFrameTranslation(25f, translation);
for (int j = 0; j< 25; j++){
float[] weights = calculate_weights();
Pose[] poses = {poseArray[i], poseArray[i+1]};
t.frames[j] = new PoseFrame(poses,weights);
}
for (int x = 0; x< 25; x++){
// Is this the weight for the individual pose?
float weight = 0;
t.applyFrame(mesh, x, weight);
}
// Somehow build the Animation
}
[/java]

I need every pose in an own animationtrack, because I'd like to still be able to blend between between animation.
Thank you for all the help! And Happy Easter!

1 Like

Not exactly.
first here is some brief (maybe a bit too much) doc on ogre wiki about how they implement pose anim OGRE Manual v1.8 (’Byatis’): 8.3.2 Pose Animation
I hope the interpolation is done when exporting from blender, and that you don’t have to do it yourself. ( blender have thousandths ways to interpolate, that would be a shame that we have to recompute everything)

then applyFrame is private and should stay private, the animation should be driven by a AnimControl that will call the setTime of the PoseTrack (currently commented out).

here is how things should be done when you load an ogre anim :

  1. extract poses (there shouldn’t be one per frame, but one per keyframe)
  2. for each frame create a PoseFrame with the appropriate poses and the appropriate weights (hopefully loaded from the ogre file)
  3. from those poses and array of PoseFrame create a PoseTrack
  4. create an animation and invoke setTracks(arrayOfTrack); arrayOfTrack contains your PoseTrack.
  5. create an AnimControl and add the animation to it.
  6. then add the control to the model

This way you’ll be able to invoke pose animation the exact same way you invoke bone animation. Moreover you’ll be able to combine pose and bone animations.

@Kidow said: Thank you for all the help!
hehe well, actually that's you helping me ;)! Thanks

Second try!

I took a closer look at the OGREXML.

At first there are all Poses listed with their offset to the original mesh.
Link

If you are doing a poseAnimation with blender(with the NLA Editor).
You see 2 poserefs because my model had two ShapeKeys.
Link

So I tried to extend the MeshLoader and it looks like this.

[java]
public class MeshLoader(){

private Vector3f[] poseVec;
private int[] indicies;
private ArrayList poses;
private String poseName = “”;
private ArrayList animation;
private int keyIndex = 0;
private float[] times;
private int poseRefIndex = 0;
private Pose[] poseRef;
private float[] weights;
private PoseFrame[] frame;
private String animname;
private float animlength;
private int trackIndex = 0;

startElement(String uri, String name, String qName){
if(){

}else if (qName.equals(“poses”)) {
poses = new ArrayList();
} else if (qName.equals(“pose”)) {
poseName = attribs.getValue(“name”);
poseVec = new Vector3f[vertCount];
indicies = new int[vertCount];
} else if (qName.equals(“poseoffset”)) {
int index = Integer.parseInt(attribs.getValue(“index”));
float x = Float.parseFloat(attribs.getValue(“x”));
float y = Float.parseFloat(attribs.getValue(“y”));
float z = Float.parseFloat(attribs.getValue(“z”));
if (x > 0f && y > 0f && z > 0f){
poseVec[index] = new Vector3f(x,y,z);
indicies[index] = index;
}
}else if (qName.equals(“animations”)){
animation = new ArrayList();
}else if(qName.equals(“animation”)){
animlength = Float.parseFloat(attribs.getValue(“length”));
animname = String.valueOf(attribs.getValue(“name”));
int frameCount = (int) Math.round((animlength / 0.041666666667f)+1f);
times = new float[frameCount];
keyIndex = 0;
}else if(qName.equals(“tracks”)){
//ok
}else if(qName.equals(“track”)){
//ok
}else if(qName.equals(“keyframes”)){
//ok
}else if(qName.equals(“keyframe”)){
frame = new PoseFrame[times.length];
times[keyIndex] = Float.parseFloat(attribs.getValue(“time”));
poseRefIndex = 0;
weights = new float[poses.size()];
poseRef = new Pose[poses.size()];
}else if(qName.equals(“poseref”)){
int poseInd = Integer.parseInt(attribs.getValue(“poseindex”));
Pose actPose = poses.get(poseInd);
poseRef[poseRefIndex] = actPose;
weights[poseRefIndex] = Float.parseFloat(attribs.getValue(“influence”));
poseRefIndex++;
}else {
logger.log(Level.WARNING, “Unknown tag: {0}. Ignoring.”, qName);
ignoreUntilEnd = qName;
}
}
[/java]
The endElement-methode in the MeshLoader class
[java]
public void endElement(String uri, String name, String qName) {
if(){

}
else if(qName.equals(“animations”)){
setUpControl();
endPoseAnim();
}else if(qName.equals(“track”)){
trackIndex++;
}else if(qName.equals(“keyframe”)){
setUpPoseAnim();
}else if (qName.equals(“pose”)){
Pose pose = new Pose(poseName, meshIndex, poseVec, indicies);
poses.add(pose);
}

}
[/java]

the other stuff
[java]
// Sets up the PoseFrame
private void setUpPoseAnim() {
frame[keyIndex] = new PoseFrame(poseRef,weights);
keyIndex++;
}
// Sets up the PoseTrack
private void endPoseAnim() {
PoseTrack[] track = new PoseTrack[trackIndex];
for (int i = 0; i<track.length; i++){
track[i] = new PoseTrack(meshIndex, times, frame);
Animation a = new Animation(animname, animlength);
a.setTracks(track);
animation.add(a);
}
}
// Sets up the Control
private void setUpControl() {
AnimControl ctrl = new AnimControl();
for (int i = 0; i < animation.size(); i++){
ctrl.addAnim(animation.get(i));
}
//add it to the model?
}
}
[/java]

Better than the first try? :slight_smile:

And thanks again, for looking at it, and giving such good advice

1 Like

It looks good!
Does it work?
I’ll look into it closer as soon as I can.

Hi there,
maybe looking at the jme2-sources can help here as well.We had Pose-Animations already running. Afaik that
was more or less one of the last improvements we made at these times (god bless mighty jme2)
Not sure how much of the importer is sitill useable, but nothings goes over a working example :smiley:

Have a look here:

Importer: http://code.google.com/p/jmonkeyengine/source/browse/branches/jme2.1/src/com/jmex/model/ogrexml/
Test: http://code.google.com/p/jmonkeyengine/source/browse/branches/jme2.1/src/jmetest/ogrexml/TestFacialAnimation.java
Test-Data(Human): http://code.google.com/p/jmonkeyengine/source/browse/branches/jme2.1/src/#src%2Fjmetest%2Fdata%2Fmodel%2Fogrexml
http://code.google.com/p/jmonkeyengine/source/browse/branches/jme2.1/src/#src%2Fcom%2Fjmex%2Fmodel%2Fogrexml%2Fanim

Hope that helps

1 Like

Third Try! Thanks for the helpful links.

I had some errors in the previous Code. I corrected them, but it’s not working!
Here the corrected version, everything is still in the MeshLoader class.

[java]
public class MeshLoader extends DefaultHandler implements AssetLoader {

private Vector3f[] poseVec;
private int[] indicies;
private ArrayList poses;
private String poseName = “”;
private ArrayList animation;
private int keyIndex = 0;
private float[] times;
private int poseRefIndex = 0;
private Pose[] poseRef;
private float[] weights;
private PoseFrame[] frame;
private String animname;
private float animlength;
private int trackIndex = 0;
private boolean poseFlag = false;

[/java]

The startElment Method

[java]
public void startElement(String uri, String localName, String qName,
Attributes attribs) throws SAXException {


} else if (qName.equals(“poses”)) {
poses = new ArrayList();
} else if (qName.equals(“pose”)) {
poseName = attribs.getValue(“name”);
poseVec = new Vector3f[vertCount];
indicies = new int[vertCount];
} else if (qName.equals(“poseoffset”)) {
int index = Integer.parseInt(attribs.getValue(“index”));
float x = Float.parseFloat(attribs.getValue(“x”));
float y = Float.parseFloat(attribs.getValue(“y”));
float z = Float.parseFloat(attribs.getValue(“z”));
if (x > 0f && y > 0f && z > 0f){
poseVec[index] = new Vector3f(x,y,z);
indicies[index] = index;
}
}else if (qName.equals(“animations”)){
animation = new ArrayList();
}else if(qName.equals(“animation”)){
animlength = Float.parseFloat(attribs.getValue(“length”));
animname = String.valueOf(attribs.getValue(“name”));
int frameCount = (int) Math.round((animlength / 0.041666666667f)+1f);
times = new float[frameCount];
keyIndex = 0;
}else if(qName.equals(“tracks”)){
trackIndex = 0;
}else if(qName.equals(“track”)){
//ok
}else if(qName.equals(“keyframes”)){
//ok
}else if(qName.equals(“keyframe”)){
frame = new PoseFrame[times.length];
times[keyIndex] = Float.parseFloat(attribs.getValue(“time”));
poseRefIndex = 0;
weights = new float[poses.size()];
poseRef = new Pose[poses.size()];
}else if(qName.equals(“poseref”)){
int poseInd = Integer.parseInt(attribs.getValue(“poseindex”));
Pose actPose = poses.get(poseInd);
poseRef[poseRefIndex] = actPose;
weights[poseRefIndex] = Float.parseFloat(attribs.getValue(“influence”));
poseRefIndex++;
}

}
[/java]

The endElement Method.

[java]
public void endElement(String uri, String name, String qName) {

else if (qName.equals(“pose”)){
Pose pose = new Pose(poseName, meshIndex, poseVec, indicies);
poses.add(pose);
}else if(qName.equals(“keyframe”)){
setUpPoseAnim();
}else if(qName.equals(“track”)){
trackIndex++;
}else if(qName.equals(“tracks”)){
endPoseAnim();
}else if(qName.equals(“animation”)){
poseFlag = true;
}

}
[/java]

And the other stuff
[java]
// Sets up the PoseFrame
private void setUpPoseAnim() {
frame[keyIndex] = new PoseFrame(poseRef,weights);
keyIndex++;
}
// Sets up the PoseTrack
private void endPoseAnim() {
PoseTrack[] track = new PoseTrack[trackIndex];
for (int i = 0; i < track.length; i++){
track[i] = new PoseTrack(meshIndex, times, frame);
Animation a = new Animation(animname, animlength);
a.setTracks(track);
animation.add(a);
}
}
// Sets up the Control
private Node setUpPoseControl() {
Node node = new Node(meshName + "-ogremesh");
AnimControl control = new AnimControl();
HashMap anims = new HashMap();
ArrayList animList = animation;
for (int i = 0; i < animList.size(); i++) {
Animation anim = animList.get(i);
anims.put(anim.getName(), anim);
}
control.setAnimations(anims);
node.addControl(control);
return node;
}

[/java]

And I changed a little thing in the load-method
[java]
// Added by larynx 25.06.2011
// Android needs the namespace aware flag set to true
// Kirill 30.06.2011
// Now, hack is applied for both desktop and android to avoid
// checking with JmeSystem.
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);

		XMLReader xr = factory.newSAXParser().getXMLReader();
		xr.setContentHandler(this);
		xr.setErrorHandler(this);

		InputStreamReader r = null;
		try {
			r = new InputStreamReader(info.openStream());
			xr.parse(new InputSource(r));
		} finally {
			if (r != null) {
				r.close();
			}
		}
		//For PoseAnim
		if (poseFlag == true){
			return setUpPoseControl();
		}
		return compileModel();
	} catch (SAXException ex) {
		IOException ioEx = new IOException(
				&quot;Error while parsing Ogre3D mesh.xml&quot;);
		ioEx.initCause(ex);
		throw ioEx;
	} catch (ParserConfigurationException ex) {
		IOException ioEx = new IOException(
				&quot;Error while parsing Ogre3D mesh.xml&quot;);
		ioEx.initCause(ex);
		throw ioEx;
	}

[/java]

Right now I'm able to set an animation by using a animationChannel without getting a nullpointerException. But unfortunately I cannot see it. What went wrong?

1 Like

Okay stupid post is stupid, forget about the changes in the load method. I just did a few adjustments in the compile Model()-Method.
Now it looks like this. Now I can see the model in jme but the animation is still not working.

[java]
private Node compileModel() {
Node model = new Node(meshName + “-ogremesh”);

	for (int i = 0; i &lt; geoms.size(); i++) {
		Geometry g = geoms.get(i);
		Mesh m = g.getMesh();

		// New code for buffer extract
		if (sharedMesh != null &amp;&amp; usesSharedMesh.get(i)) {
			m.extractVertexData(sharedMesh);
		}

		// Old code for buffer sharer
		// if (sharedMesh != null &amp;&amp; isUsingSharedVerts(g)) {
		// m.setBound(sharedMesh.getBound().clone());
		// }
		model.attachChild(geoms.get(i));
	}

	// Do not attach shared geometry to the node!

	// if (animData != null) {
	// This model uses animation

	// Old code for buffer sharer
	// generate bind pose for mesh
	// ONLY if not using shared geometry
	// This includes the shared geoemtry itself actually
	// if (sharedMesh != null) {
	// sharedMesh.generateBindPose(!HARDWARE_SKINNING);
	// }

	for (int i = 0; i &lt; geoms.size(); i++) {
		Geometry g = geoms.get(i);
		Mesh m = geoms.get(i).getMesh();

		m.generateBindPose(!HARDWARE_SKINNING);

		// Old code for buffer sharer
		// boolean useShared = isUsingSharedVerts(g);
		// if (!useShared) {
		// create bind pose
		// m.generateBindPose(!HARDWARE_SKINNING);
		// }
	}

	// Put the animations in the AnimControl
	HashMap anims = new HashMap();
	ArrayList animList = new ArrayList();
	if (poseFlag == true) {
		animList = animation;
	} else {
		animList = animData.anims;
	}
	for (int i = 0; i &lt; animList.size(); i++) {
		Animation anim = animList.get(i);
		anims.put(anim.getName(), anim);
	}
	if (poseFlag == true) {
		AnimControl ctrl = new AnimControl();
		ctrl.setAnimations(anims);
		model.addControl(ctrl);
	} else {
		AnimControl ctrl = new AnimControl(animData.skeleton);
		ctrl.setAnimations(anims);
		model.addControl(ctrl);
		// Put the skeleton in the skeleton control
		SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton);

		// This will acquire the targets from the node
		model.addControl(skeletonControl);
	}
	return model;
}

[/java]

@Kidow said: Right now I'm able to set an animation by using a animationChannel without getting a nullpointerException. But unfortunately I cannot see it. What went wrong?
Did you implement the setTime() code in poseTrack? It's commented out in the core, it may be what you're missing. This method has to apply the transformations to the vertices of the mesh for the given time.

Hey there,

totally forgot about that. I did it, but right now I get an NullPointerException because the poses-Array per PoseFrame seem to be null.

Heres what I did.
The setTime-method.I took the findGeom()-method from here http://hub.jmonkeyengine.org/forum/topic/object-must-be-a-geometry/

[java]
public void setTime(float time, float weight, AnimControl control,
AnimChannel channel, TempVars vars) {
// TODO: When MeshControl is created, it will gather targets
// list automatically which is then retrieved here.
Spatial spat = control.getSpatial();
Geometry geom = findGeom(spat);
Mesh target = geom.getMesh();

	// Mesh target = targets[targetMeshIndex];
	if (time  times[times.length - 1]) {
		applyFrame(target, times.length - 1, weight);
	} else {
		int startFrame = 0;
		for (int i = 0; i &lt; times.length; i++) {
			if (times[i] &lt; time) {
				startFrame = i;
			}
		}

		int endFrame = startFrame + 1;
		float blend = (time - times[startFrame])
				/ (times[endFrame] - times[startFrame]);
		applyFrame(target, startFrame, blend * weight);
		applyFrame(target, endFrame, (1f - blend) * weight);
	}
}

[/java]

[java]
public Geometry findGeom(Spatial spatial) {
if (spatial instanceof Node) {
Node findingnode = (Node) spatial;
for (int i = 0; i < findingnode.getQuantity(); i++) {
Spatial child = findingnode.getChild(i);
Geometry result = findGeom(child);
if (result != null) {
return result;
}
}
} else if (spatial instanceof Geometry) {
return (Geometry) spatial;
}
return null;
}
[/java]

The Exception right now

09.04.2013 14:53:50 com.jme3.app.Application handleError
SCHWERWIEGEND: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
java.lang.NullPointerException
at com.jme3.animation.PoseTrack.applyFrame(PoseTrack.java:126)
at com.jme3.animation.PoseTrack.setTime(PoseTrack.java:179)
at com.jme3.animation.Animation.setTime(Animation.java:110)
at com.jme3.animation.AnimChannel.update(AnimChannel.java:391)
at com.jme3.animation.AnimControl.controlUpdate(AnimControl.java:332)
at com.jme3.scene.control.AbstractControl.update(AbstractControl.java:112)
at com.jme3.scene.Spatial.runControlUpdate(Spatial.java:570)
at com.jme3.scene.Spatial.updateLogicalState(Spatial.java:688)
at com.jme3.scene.Node.updateLogicalState(Node.java:145)
at com.jme3.scene.Node.updateLogicalState(Node.java:152)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:244)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Unknown Source)

Any ideas? Am I doing something terribly wrong with the import?

1 Like

If you didn’t change the line numbers in poseTrack it looks like the failing line is :
[java]
float poseWeight = frame.weights[i] * weight;
[/java]
make sure frame.weights is not null when you load the file

Good work so far you’re almost done :wink:

1 Like

Sorry I actually changed the lines a bit. It says the problem is this line in applyFrame()

[java]
for (int i = 0; i < frame.poses.length; i++)
[/java]

Does it have something to do with the inner class PoseFrame? Could it be that the poses-array is not instatiated right?

Thanks Thanks Thanks!

Edited because:
[java]
PoseFrame frame = frames[frameIndex];
[/java]

frames[frameIndex] is already null, so I understand why the poses array in poseFrame is null as well. But I'm still clueless why the frames array returns null.

EDIT: Found it! Currently working on another mistake. I will post a little later, when I hopefully corrected everything!

1 Like

It’s me again! I actually got the animation running. But it’s not working correctly. All the changes that I did.

The MeshLoader class
[java]
public class MeshLoader extends DefaultHandler implements AssetLoader {

private ArrayList poses;
private String poseName = "";
private ArrayList animation;
private int keyIndex = 0;
private float[] times;
private int poseRefIndex = 0;
private Pose[] poseRef;
private float[] weights;
private PoseTrack.PoseFrame[] frame;
private String animname;
private float animlength;
private int trackIndex = 0;
private boolean poseFlag = false;
ArrayList poseVecHelp;
ArrayList indiciesHelp;

public void startElement(String uri, String localName, String qName,
Attributes attribs) throws SAXException {
if(){

	} else if (qName.equals("poses")) {
		poses = new ArrayList();
	} else if (qName.equals("pose")) {
		poseName = attribs.getValue("name");
		poseVecHelp = new ArrayList();
		indiciesHelp = new ArrayList();
	} else if (qName.equals("poseoffset")) {
		int index = Integer.parseInt(attribs.getValue("index"));
		float x = Float.parseFloat(attribs.getValue("x"));
		float y = Float.parseFloat(attribs.getValue("y"));
		float z = Float.parseFloat(attribs.getValue("z"));
		if (x == 0f &amp;&amp; y == 0f &amp;&amp; z == 0f) {
		} else {
			poseVecHelp.add(new Vector3f(x,y,z));
			indiciesHelp.add(index);
		}
	} else if (qName.equals("animations")) {
		animation = new ArrayList();
	} else if (qName.equals("animation")) {
		animlength = Float.parseFloat(attribs.getValue("length"));
		animname = String.valueOf(attribs.getValue("name"));
		int frameCount = (int) Math
				.round((animlength / 0.041666666667f) + 1f);
		times = new float[frameCount];
		keyIndex = 0;
	} else if (qName.equals("tracks")) {
		trackIndex = 0;
	} else if (qName.equals("track")) {
		// ok
	} else if (qName.equals("keyframes")) {
		// ok
		frame = new PoseTrack.PoseFrame[times.length];
	} else if (qName.equals("keyframe")) {
		times[keyIndex] = Float.parseFloat(attribs.getValue("time"));
		poseRefIndex = 0;
		weights = new float[poses.size()];
		poseRef = new Pose[poses.size()];
	} else if (qName.equals("poseref")) {
		int poseInd = Integer.parseInt(attribs.getValue("poseindex"));
		Pose actPose = poses.get(poseInd);
		poseRef[poseRefIndex] = actPose;
		weights[poseRefIndex] = Float.parseFloat(attribs
				.getValue("influence"));
		poseRefIndex++;
	} else{
		....
	}

}

[/java]

[java]

public void endElement(String uri, String name, String qName) {
if(){

}else if (qName.equals(“pose”)) {
Vector3f[] poseVec = new Vector3f[poseVecHelp.size()];
int[] indicies = new int[indiciesHelp.size()];
for (int o = 0; o < poseVecHelp.size(); o++){
poseVec[o] = poseVecHelp.get(o);
indicies[o] = indiciesHelp.get(o);
}
Pose pose = new Pose(poseName, meshIndex, poseVec, indicies);
poses.add(pose);
} else if (qName.equals(“keyframe”)) {
setUpPoseAnim();
} else if (qName.equals(“track”)) {
trackIndex++;
} else if (qName.equals(“tracks”)) {

		endPoseAnim();
	} else if (qName.equals(&quot;animation&quot;)) {
		poseFlag = true;
	}
}

}

// Sets up the PoseFrame
private void setUpPoseAnim() {
	PoseFrame helpFrame = new PoseFrame(poseRef, weights);
	frame[keyIndex] = helpFrame;
	keyIndex++;
}

// Sets up the PoseTrack
private void endPoseAnim() {
	PoseTrack[] track = new PoseTrack[trackIndex];
	for (int i = 0; i &lt; track.length; i++) {
		track[i] = new PoseTrack(meshIndex, times, frame);
		Animation a = new Animation(animname, animlength);
		a.setTracks(track);
		animation.add(a);
	}
}

[/java]

[java]
private Node compileModel() {
Node model = new Node(meshName + "-ogremesh");

	for (int i = 0; i &lt; geoms.size(); i++) {
		Geometry g = geoms.get(i);
		Mesh m = g.getMesh();

		// New code for buffer extract
		if (sharedMesh != null &amp;&amp; usesSharedMesh.get(i)) {
			m.extractVertexData(sharedMesh);
		}

		// Old code for buffer sharer
		// if (sharedMesh != null &amp;&amp; isUsingSharedVerts(g)) {
		// m.setBound(sharedMesh.getBound().clone());
		// }
		model.attachChild(geoms.get(i));
	}

	// Do not attach shared geometry to the node!

	// if (animData != null) {
	// This model uses animation

	// Old code for buffer sharer
	// generate bind pose for mesh
	// ONLY if not using shared geometry
	// This includes the shared geoemtry itself actually
	// if (sharedMesh != null) {
	// sharedMesh.generateBindPose(!HARDWARE_SKINNING);
	// }

	for (int i = 0; i &lt; geoms.size(); i++) {
		Geometry g = geoms.get(i);
		Mesh m = geoms.get(i).getMesh();

		m.generateBindPose(!HARDWARE_SKINNING);

		// Old code for buffer sharer
		// boolean useShared = isUsingSharedVerts(g);
		// if (!useShared) {
		// create bind pose
		// m.generateBindPose(!HARDWARE_SKINNING);
		// }
	}

	// Put the animations in the AnimControl
	HashMap anims = new HashMap();
	ArrayList animList = new ArrayList();
	if (poseFlag == true) {
		animList = animation;
	} else {
		animList = animData.anims;
	}
	for (int i = 0; i &lt; animList.size(); i++) {
		Animation anim = animList.get(i);
		anims.put(anim.getName(), anim);
	}
	if (poseFlag == true) {
		AnimControl ctrl = new AnimControl();
		ctrl.setAnimations(anims);
		model.addControl(ctrl);
	} else {
		AnimControl ctrl = new AnimControl(animData.skeleton);
		ctrl.setAnimations(anims);
		model.addControl(ctrl);
		// Put the skeleton in the skeleton control
		SkeletonControl skeletonControl = new SkeletonControl(
				animData.skeleton);

		// This will acquire the targets from the node
		model.addControl(skeletonControl);
	}
	return model;
}

}
[/java]

In PoseTrack I only changed this part
[java]
target = targets[targetMeshIndex];
[/java]

into this
[java]
Spatial spat = control.getSpatial();
Geometry geom = findGeom(spat);
Mesh target = geom.getMesh();
[/java]

the findGeom()
[java]
public Geometry findGeom(Spatial spatial) {
if (spatial instanceof Node) {
Node findingnode = (Node) spatial;
for (int i = 0; i < findingnode.getQuantity(); i++) {
Spatial child = findingnode.getChild(i);
Geometry result = findGeom(child);
if (result != null) {
return result;
}
}
} else if (spatial instanceof Geometry) {
return (Geometry) spatial;
}
return null;
}
[/java]

It kind of works…But the animation is different from the animation in Blender. It looks like it is scaled bigger. For example, if a point should move 2 blenderUnits along the x axis in the animation it looks like 20(or even more) blenderUnits. Any ideas where this could happen?

1 Like