I’m playing around with SpatialTransformer, and I was trying to rotate a sphere around an already-rotating sphere and ran into trouble.
The problem is:
the sphere is attached to a node rot1 with a local translation of 10 up.
Then that node is attached to another node "root"with a local translation of 10 up. So in the end the sphere is supposed to be 20 units up from the center of node “root”.
However as soon as I add a SpatialTransformer to the rot1 node, then the node rot1 is moved to the world center. I tested it like this: I made a sphere in the center of each node (rot1 and root). Without the spatialtransformer, you can tell rot1 is very much above root. However as soon as I add the transformer, rot1 and root overlap.
Here’s the relevant code:
Node root = new Node(“root”);
Node rot1 = new Node(“rot1”);
Sphere s1 = new Sphere(“s1”, 10 , 10, 1);
s1.setLocalTranslation(new Vector3f(0, 5, 0));
rot1.attachChild(s1);
rot1.setLocalTranslation(new Vector3f(0,5,0));
SpatialTransformer st1 = new SpatialTransformer(2);
st1.setObject(rot1, 0, -1);
…bla bla …
rot1.addController(st1);
rootNode.attachChild(root);
By the way: has anyone here ever used Xith3D? I’m thinking about switching over because of the bugs I ran into lately with jMe and the lack of documentation.
I really like jMe and its featureset is the selling point that drove me to use it in the first place, but if the rest of the package is as buggy, then I really don’t think I can learn much.
I would be glad if anybody offered their opinions, or a few words of guidance.
Well, it’s not necessarily a bug. However, I can see how it doesn’t do what you are expecting it to.
When you set the position sets for the spatial transformer this will override the local translation of the object it is acting on. That is, you are defining where the center of the node will travel during it’s animation. However, I can see where you are coming from, that it should take into account local translation.
While I hate to see anyone leave, if you don’t feel jME is right for you, by all means try out the other options. However, if the amount of bugs in jME turned you off you probably won’t be happy with Xith3D. Like us they are a young early alpha project. If you need something stable with little to no bugs you would be best working with Java3D.
However, the only way projects like ours get bug free is people to stick it out with us and post the bugs they find.
Thank you mojo for your reply.
I was hoping for a more stable package, but what you said makes a lot of sense. Xith does look a lot more awkward than jMe and I really hoped that Xith is deeper than it looked…Anyway, maybe i will learn from java3D first. As I’m completely new to this, being neither fluent in OGL or 3D, I think I should start my learning with a more stable package, and then once i got the basics and concepts, I can come back to jMe for the performance leap. Hopefully, by then I might be fluent enough to make my own contributions to the project.
Your tolerance amazes me mojo
Cuppo.
Mojo is right about SpatialTransformer. If you look at the javadoc, you’ll see the line:
interpolateMissing
public void interpolateMissing()
This must be called one time, once all translations/rotations/scales have been set. It will interpolate unset values to make the animation look correct. Tail and head values are assumed to be the identity.
The head and tail translations values are set to the identity if you don't specify any for spatial transformer. If you want it to stay at y=10, then just add a begining time and ending time translation of y=10. If you don't want to do it with spatial transformer, then use a Node with y=10 translation and attach the spatial transforming nodes to it. The reason head and tail values are set to the identity is because that's how most model loaders work.
For what it's worth, you'll find the documentation of jME better than xith (that's opinion of course).
The workaround you suggested would work only if the node was stationary.
What I really wanted to do was have a rotating sphere around another rotating sphere, like the moon revolving around the earth which is in turn revolving around the sun.
Considering jMe already handles local transformations, I don’t see why SpatialTransformer can’t easily be modified to just use the coordinate system of the parent. There must be a line somewhere in spatialtransformer that sets the new location of the object. That line can be changed to setLocalTranslation(…), and setLocalRotation(…), instead can’t it? This way it won’t need the parentIndex in the setObject(…) method.
SpatialTransformer was made to work like the Node/Child relationship so that if you rotate the parent, the child rotates as well. I allowed Spatials to have other Spatial’s as parents only for my own efficiency when loading models. You can use -1 for all indexes and just use Node/TriMesh spatials if you want. It does work on the parent’s coordinate system, it’s just that the parent’s coordinate system changes when it rotates. To do what you’re suggesting in a scenegraph enviroment it would look something like the following. The key is to realize that the moon doens’t rotate around the earth. The earth rotates around a point at the center of the earth that doesn’t rotate. The moon rotates around that same point that happens to be the center of the earth.
Node centerOfEarth;
TriMesh earth;
TriMesh moon;
centerOfEarth.attachChild(earth);
centerOfEarth.attachChild(moon);
SpatialTransformer st=new SpatialTransformer(2);
st.setObject(earth,0,-1);
st.setObject(moon,1,-1);
// move the moon out
st.setTranslation(1,0,new Vector3f(10,0,0));
st.setTranslation(1,5,new Vector3f(10,0,0));
// rotate the moon.
st.setRotatoin(1,0,identityrotation);
st.setRotation(1,3,rotate 180 degrees around y axis);
st.setRotation(1,6,rotate 360 degrees around y axis);
// rotate the earth.
st.setRotatoin(0,0,identityrotation);
st.setRotation(0,1,rotate 120 degrees around y axis);
st.setRotation(0,2,rotate 240 degrees around y axis);
st.setRotation(0,3,rotate 360 degrees around y axis);
st.setRotation(0,4,rotate 120 degrees around y axis);
st.setRotation(0,5,rotate 240 degrees around y axis);
st.setRotation(0,6,rotate 360 degrees around y axis);
centerofEarth.setLocaltranslation(earth’s real world position);
Okay, I don’t understand the above code. I tried it out as best as I could but I kept on getting nullpointerexception. Sorry. So I included my own moon earth spinning code here: I made it as readable as I could.
Quaternion a0 = new Quaternion();
a0.fromAngleAxis(0, new Vector3f(0,0,1));
Quaternion a180 = new Quaternion();
a180.fromAngleAxis(FastMath.DEG_TO_RAD180, new Vector3(0,0,1));
Quaternion a360 = new Quaternion();
a360.fromAngleAxis(FastMath.DEG_TO_RAD360, new Vector3(0,0,1));
Node earthLeg = new Node(“Earth Leg”);
earthLeg.attachChild(new Sphere(“earth leg origin”, 5, 5, 0.3f));
Sphere earth = new Sphere(“Earth”, 5, 5, 1);
earth.setLocalTranslation(new Vector3f(0,5,0)); //move earth out.
earthLeg.attachChild(earth);
Node moonLeg = new Node(“Moon Leg”);
moonLeg.attachChild(new Sphere(“moon leg origin”, 5, 5, 0.3f));
Sphere moon = new Sphere(“Moon”, 5, 5, 0.5f);
moon.setLocalTranslation(new Vector3f(0,5,0)); //move moon out.
moonLeg.attachChild(moon);
moonLeg.setLocalTranslation(new Vector3f(0,5,0)); //move moonlegout
earthLeg.attachChild(moonLeg);
//OKAY START ANIMATING
SpatialTransformer moonSpin = new SpatialTransformer(1);
moonSpin.setObject(moonLeg, 0, -1);
moonSpin.setRotation(0, 0, a0);
moonSpin.setRotation(0, 2, a180);
moonSpin.setRotation(0, 4, a360);
moonSpin.interpolateMissing();
moonLeg.addController(moonSpin);
SpatialTransformer earthSpin = new SpatialTransformer(1);
earthSpin.setObject(earthLeg, 0, -1);
earthSpin.setRotation(0, 0, a0);
earthSpin.setRotation(0, 2, a180);
earthSpin.setRotation(0, 4, a360);
earthSpin.interpolateMissing();
earthLeg.addController(earthSpin);
rootNode.attachChild(earthLeg);
Can you please run this? Notice how it looks with the animation code commented out.
Gimmie 15 minutes. This seems like a pretty neat example. I’m gonna try to code it up using SpatialTransformer.
Cool thx a lot ceps
import com.jme.app.SimpleGame;
import com.jme.scene.Node;
import com.jme.scene.state.MaterialState;
import com.jme.scene.state.TextureState;
import com.jme.scene.shape.Sphere;
import com.jme.renderer.ColorRGBA;
import com.jme.util.TextureManager;
import com.jme.image.Texture;
import com.jme.animation.SpatialTransformer;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.math.FastMath;
import java.net.URL;
/**
* Started Date: Sep 7, 2004<br><br>
*
* @author Jack Lindamood
*/
public class HelloSolar extends SimpleGame {
public static void main(String[] args) {
HelloSolar app = new HelloSolar();
app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}
URL MONKEY=HelloSolar.class.getClassLoader().getResource("jmetest/data/images/Monkey.png");
URL GRASS=HelloSolar.class.getClassLoader().getResource("jmetest/data/texture/grass.jpg");
Sphere earth;
protected void simpleInitGame() {
// FastMath.USE_FAST_TRIG=true;
Node sunCen=new Node("Sun's center of gravity");
Node earthCen=new Node("Earth's center of gravity");
sunCen.attachChild(earthCen);
Node moonCen=new Node("Moon's center of gravity");
earthCen.attachChild(moonCen);
Sphere sun=new Sphere("sun",35,35,5);
sunCen.attachChild(sun);
MaterialState yellow=display.getRenderer().createMaterialState();
yellow.setEmissive(new ColorRGBA(0,1,1,1));
sun.setRenderState(yellow);
earth=new Sphere("earth",35,35,2);
earthCen.attachChild(earth);
TextureState grass=display.getRenderer().createTextureState();
grass.setTexture(TextureManager.loadTexture(GRASS,Texture.MM_LINEAR,Texture.FM_LINEAR,true));
earth.setRenderState(grass);
Sphere moon=new Sphere("moon",10,10,.5f);
moonCen.attachChild(moon);
TextureState monkey=display.getRenderer().createTextureState();
monkey.setTexture(TextureManager.loadTexture(MONKEY,Texture.MM_LINEAR,Texture.FM_LINEAR,true));
moon.setRenderState(monkey);
SpatialTransformer st=new SpatialTransformer(4);
st.setObject(sunCen,0,-1);
st.setObject(earthCen,1,-1);
st.setObject(earth,2,-1);
st.setObject(moon,3,-1);
{ // sun's center rotates to make the earth's center go around the sun.
Quaternion q;
q=new Quaternion();
q.fromAngleNormalAxis(0,new Vector3f(0,1,0));
st.setRotation(0,0,q);
q=new Quaternion();
q.fromAngleNormalAxis(FastMath.HALF_PI,new Vector3f(0,1,0));
st.setRotation(0,89,q);
q=new Quaternion();
q.fromAngleNormalAxis(FastMath.PI,new Vector3f(0,1,0));
st.setRotation(0,178,q);
q=new Quaternion();
q.fromAngleNormalAxis(FastMath.PI+FastMath.HALF_PI,new Vector3f(0,1,0));
st.setRotation(0,267,q);
}
{ // Earth's center rotates to make the moon's center move around it.
float dayIncrease=FastMath.TWO_PI*(2.0f/3.0f);
float begin=0;
for (int i=0;i<356;i++){
Quaternion q;
q=new Quaternion();
q.fromAngleNormalAxis(begin,new Vector3f(0,1,0));
st.setRotation(1,i+0,q);
begin+=dayIncrease;
}
st.setPosition(1,0,new Vector3f(15,0,0));
st.setPosition(1,355,new Vector3f(15,0,0));
}
{ // Earth rotates around its center
float dayIncrease=FastMath.TWO_PI*(1.0f/4.0f);
float begin=0;
for (int i=0;i<356;i++){
Quaternion q;
q=new Quaternion();
q.fromAngleNormalAxis(begin,new Vector3f(0,1,0));
st.setRotation(2,i+0,q);
begin+=dayIncrease;
}
}
{ // Moon rotates around its center
float dayIncrease=FastMath.TWO_PI*(1.0f/3.0f);
float begin=0;
for (int i=0;i<356;i++){
Quaternion q;
q=new Quaternion();
q.fromAngleNormalAxis(begin,new Vector3f(0,1,0));
st.setRotation(3,i+0,q);
begin+=dayIncrease;
}
}
// Finally move the moon's center 5 away from the earth's center
moonCen.setLocalTranslation(new Vector3f(3,0,0));
st.interpolateMissing();
sunCen.addController(st);
st.setSpeed(10); // so we don't have to wait forever to finish
rootNode.attachChild(sunCen);
}
}
Some explanation about the code. Let me know if a line of the explanation is unclear. The key is to realize that the earth is not the sun’s child. It is actually the child of the sun’s center of gravity.
The center of the sun is the center of the solar system.
The center of the solar system has two children: The sun itself and the Earth’s center.
The center of the solar system rotates to allow the earth’s center to rotate around the sun.
The earth’s center has two children: The earth itself and the moon.
The earth’s center rotates to make the moon rotate around it.
The earth itself rotates around the earth’s center to make night/day.
The moon rotates itself to simulate how satilites rotate.
Can’t offer anything on the rotating spheres… However, regarding Java3D/Xith3D…
I started with Java3D. I even got a tiny, but decent understanding of how some of the basics worked. Then I realized how little support Java3D was getting and how much work it was going to be to create a game engine from scratch. (I wasn’t worried about the performance, figuring by the time I actually finished something, the hardware would be fast enough.)
From the Java3D forums… I saw some mumblings about Xith3D and went to check it out. Xith3D is very similar to Java3D. However (and maybe this has changed in recent months), there wasn’t a lot of activity going on over there. (And I wasn’t fond of the website… which, justified or not, made me concerned about the developers.)
I saw a post (I think by our very own Mojo) on the Xith3D boards about jME, so I went to check it out.
I was immediately impressed with the engine, as well as the active development community. (And the website was much nicer, IMHO. grin) I was a bit skeptical at first. jME is different than Java3D/Xith3D, so there’s a learning curve. But now…I can’t imagine being anywhere else.
Although I don’t post too much, and I haven’t really had the chance to work on my own project… I read the message boards here every morning just to keep up. There’s always activity. When you post, you always get a response, and there are people who will go out of their way to help you. (Take Cep helping with the sphere thing, for example – that’s the norm, not the exception.) Also… practically as soon as a bug is found, it is taken care of by somebody. So if you find a problem you don’t know how to fix, you can rest easy knowing that somebody here will help.
Yes… it would be nice to have more documentation, but that’s actually improving (largely thanks to Cep, again. grin). I think it’s a normal thing for an open source project to have these issues. The real difference comes in if there are still people interested in the project past its infancy. jME has that, and seems to be attracting more people every day.
The engine and the people are really great here. Sure, there are going to be a few hiccups – it’s a pre-1.0 open source project… but things are remarkably stable, and are continually getting more robust and feature-rich. It’s worth sticking around.
By all means, check out your other options. Personally, I’d put jME head-to-head with them any day. I have a lot of respect for those guys (I really do), but jME is just a better game engine.
Just my .02.
–K
Thx for all your replies.
I think I’ll probably switch to Java3D for learning while keeping a close eye on jMe. Once I have the basics of 3d down then I’ll switch back to jMe to do any real work. By the way: what do you feel about programming directly with OGL? I know the winners of Sun’s game programming contest all directly used JLWGL or JOGL.
And thx cep for the reply. So the problem is in the interpolateMissing() method. Can you explain what that method means in more detail?
What does “missing” exactly mean?
And how come you only did sunCen.addController(st), and you didn’t add it to anything else?
interpolateMissing() must be called in order to setup the controller before it’s attached. Controllers only need to be attached to one object in order to execute. It’s all inside the user guide and the javadoc. Where are you seeing “missed”?
Edit: Oh I see, “missing” in the javadoc. Well, you specify rotations for time T=0, T=1, T=5, T=9. Then you specify translations for time T=0,T=4,T=9. Right now, you’re “missing” rotations for time T=4 and you’re missing translations for time T=1,T=5. That function fills in the rotations/translations for all the times in between.
Sorry, i’m still having trouble understanding it.
in your example code cep you specified the rotation of the sun center in large increments. But you specified the rotation of the earth center in a total of 365 INCREMENTS! I’m guessing that’s the technique that you used to overcome the local translation problem.
So does interpolateMissing() fill in the positions for all integer times?
So LWJGL and JOGL can be used to do 2d easily. Can scenegraphs be used to do 2D? I don’t care about the performance drop as long as its easier.
overcome? Just trying to do what the real solar system does.
My controller has a time frame from 0 to 365 and i’m going to model my entire solar system with the one controller.
The earth rotates around the sun’s center once (That’s 0 to 2_PI) in 365 days.
My earth rotates around its center once every day. That’s really fast though, so I give it 2/3 of a rotation every day.
interpolateMissing() fills in unspecified values for you. I can explain the details if you want (it’s somewhat long), but a user needs to know is it is required to be called before the controller is used, and that head/tail rotation/translation/scale are set to the identity.
FWIW: This isn’t really a jME thing. Any scenegraph setup (java3d or xith3d) works the same way, it just would use different names for things.
If I wanted to be exactly correct like the real solar system, I would have to use a seperate controller for each object because they aren’t related on the same cycle time frame. I would use one controller that does one rotation of the sun’s center from time 0 to 355.459… then I’ld use another controller that does one rotation of the earth’s center from 0 to 1 then I’ld do another controller that does one rotation of the moon around the earth every from 0 to 29, then I’ld do one more controller for one rotation of the moon itself for whatever lunar cycle the moon rotates on (I think it would be 0 to 29 as well)
I think I finally get it now.
I have 1 small issue that needs clearing up, and then I’ll be out of your hair.
for rotating the sunCen you put:
Quaternion q;
q=new Quaternion();
q.fromAngleNormalAxis(0,new Vector3f(0,1,0));
st.setRotation(0,0,q);
q=new Quaternion();
q.fromAngleNormalAxis(FastMath.HALF_PI,new Vector3f(0,1,0));
st.setRotation(0,89,q);
q=new Quaternion();
q.fromAngleNormalAxis(FastMath.PI,new Vector3f(0,1,0));
st.setRotation(0,178,q);
q=new Quaternion();
q.fromAngleNormalAxis(FastMath.PI+FastMath.HALF_PI,new Vector3f(0,1,0));
st.setRotation(0,267,q);
So you set the rotation for times 0,89,178,267. and I’m guessing you let interpolateMissing() fill in the rest.
BUT for rotating the earthCen you put:
float dayIncrease=FastMath.TWO_PI*(2.0f/3.0f);
float begin=0;
for (int i=0;i<356;i++){
Quaternion q;
q=new Quaternion();
q.fromAngleNormalAxis(begin,new Vector3f(0,1,0));
st.setRotation(1,i+0,q);
begin+=dayIncrease;
}
st.setPosition(1,0,new Vector3f(15,0,0));
st.setPosition(1,355,new Vector3f(15,0,0));
For this case: you set the positions for all times from 0 to 355. How come you didn’t do what you did for rotating the sunCen, and just let interpolateMissing() fill in the rest?
I’m being a pain in the ass. I’m sorry. These forums are lucky to have u.
So you set the rotation for times 0,89,178,267. and I'm guessing you let interpolateMissing() fill in the rest.
Exactly. I can do that because I'm setting times for object "earth' all the way up to 355. So time 267 - 365 is a rotation from quat(267) to quat(identity)
For this case: you set the positions for all times from 0 to 355. How come you didn't do what you did for rotating the sunCen, and just let interpolateMissing() fill in the rest?
Because then the earth would take 365 days just to rotate once! I need the earth to rotate, then rotate again, and again, and again.... I can't just specify the rotation for one day, because what happens when time = 2?? I could either A) use a seperate controller for the earth, have it rotate once every day and repeat OR B) Use the same controller for the earth I use for the sun and have it rotate 356 times during the controller's lifetime.
These forums are lucky to have u.You must not know me well yet :)
interpolateMissing() takes the last rotation I have assigned and sets up a rotation that goes from that last rotation to the end, filling in an identity rotation at the end.
Ahhhh i get it.
Thanks for helping me cep.
These forums are indeed lucky to have you. Never knew anyone else with so much patience.