Can't rotate nodes for no aparent reason

I am trying to create a level editor and for that load premade .j3o modes, translate and rotate them in the scene. To store the data i simply save the asset name, location and rotation of them. But when loading the scene in the editor somehow the nodes that are initialy not rotated somehwo can’t be rotated any more.

I am creating the scene like this:
[java]
for (SSIEnviroment enviroment : currentLevel.getEnviroment().values()) {
Node envNode = (Node) assetManager.loadModel(enviroment.getModel());
envNode.setUserData(SSIStatics.NODETYPE, SSIStatics.NODETYPE_ENVIROMENT);
envNode.setUserData(SSIStatics.NODETYPE_ENVIROMENT, enviroment.getUID());
envNode.move(enviroment.getPosition());
envNode.rotate(enviroment.getRotation());
enviromentNode.attachChild(envNode);

        uidCounter++;
    }

[/java]

To test the whole editor i just added a few objects like this:
[java]
currentLevel = new SSILevel(“new”);
currentLevel.getEnviroment().put(
“1”, new SSIEnviroment(“1”, “Models/SSI/Scenery/BlueHalls/Wall1.j3o”,
Vector3f.ZERO.clone(), Quaternion.ZERO.clone()));
currentLevel.getEnviroment().put(
“2”, new SSIEnviroment(“2”, “Models/SSI/Scenery/BlueHalls/Wall2.j3o”,
Vector3f.ZERO.clone().setZ(8), Quaternion.ZERO.clone()));
currentLevel.getEnviroment().put(
“3”, new SSIEnviroment(“3”, “Models/SSI/Scenery/BlueHalls/Wall4.j3o”,
Vector3f.ZERO.clone().setZ(8).setY(4), PITCH090.clone()));
[/java]

Moving the nodes works perfectly, but rotating does only work for the node with if “3”. All the other nodes simply wont rotate in any direction.
I rotate the nodes like this:
[java]
public static final Quaternion PITCH045 = new Quaternion().fromAngleAxis(FastMath.PI / 4, new Vector3f(1, 0, 0));
public static final Quaternion PITCH090 = new Quaternion().fromAngleAxis(FastMath.PI / 2, new Vector3f(1, 0, 0));

switch (direction) {
case ArroundX: {
switch (turnAngle) {
case Turn45: {
selectedNode.rotate(PITCH045);
break;
}
case Turn90: {
selectedNode.rotate(PITCH090);
break;
}
}
break;
}
}
[/java]

And getting the selected node like this:
[java]
CollisionResults results = new CollisionResults();
Vector2f click2d = inputManager.getCursorPosition();
Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();
Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
Ray ray = new Ray(click3d, dir);
rootNode.collideWith(ray, results);
if (results.size() > 0) {
Node picked = results.getClosestCollision().getGeometry().getParent();
selectedNode = picked;

            Geometry g = results.getClosestCollision().getGeometry().clone();
            g.setName("selectionshape");
            g.setMaterial(selectedMat);
            g.scale(1.01f);
            setCoordinateAxes(selectedNode.getWorldTranslation());
            while (selectedNode.getUserData(SSIStatics.NODETYPE) == null) {
                selectedNode = selectedNode.getParent();
            }
            selectedNode.attachChild(g);
        }

[/java]

Anyone got an idea where the problem is ?
The last node with id 3 is the only one that accepts rotation. It is also the only one that gets created with an initial rotation, but that shouldn’t be a problem i think ?

Replace:
Quaternion.ZERO.clone())
with:
new Quaternion().

Quaternion.ZERO is an invalid rotation and I suspect it screws up all subsequence rotations since rotate() is relatative to the last rotation.

BTW, just an aside… it’s “environment”… with an ‘n’. The n is almost silent but not quite so reading the code was twisting my brain in knots. :slight_smile:

3 Likes

Yes, figured it out by now too. It seems that rotating by the ZERO quaternion breaks the nodes rotation. Replaced it by Quaternion.IDENTITY wich as far as i understand is a zero rotation.
Maybe could build in a check into the .rotate() method if the rotation quaternion is valid to prevent something like this.

Haha thanks for the info, i don’t have a spellchecker in the IDE and seldom give spelling much attention anyways :wink:

@ryukajiya said: Yes, figured it out by now too. It seems that rotating by the ZERO quaternion breaks the nodes rotation. Replaced it by Quaternion.IDENTITY wich as far as i understand is a zero rotation. Maybe could build in a check into the .rotate() method if the rotation quaternion is valid to prevent something like this.

Haha thanks for the info, i don’t have a spellchecker in the IDE and seldom give spelling much attention anyways :wink:

I’m not sure a potentially expensive check to see if you’ve done something crazy with your rotations is a good idea every time rotate() is called. We tend to assume that users have at least a basic grasp of what they are doing and will otherwise quickly learn from their mistakes.

P.S.: There is no reason to use IDENTITY.clone()… you might as well just call new Quaternion() which is essentially the same thing.

If you know where the mistake is you can lean from it, yes :wink:
Was more or less luck that i found this myself looking at bjects while debugging and wondering why quaternions looked strange.

Was using it to test the editor anyways, creating a new quaternion on my own doesn’t happen normaly.

But thanks for the reply.

What is the point in that Quaternion.ZERO existing? Is it actually useful for anything?

<cite>@zarch said:</cite> What is the point in that Quaternion.ZERO existing? Is it actually useful for anything?

I think it is used in some rare transformations… i think to collaps an object to zero size/to a single point or something like that if i remember correctly.

@zarch said: What is the point in that Quaternion.ZERO existing? Is it actually useful for anything?

Even though we use Quaternions for rotation a Quaternion != rotation. I think it just tries to be mathematically complete.

Note: this is why it’s Quaternion.mult() instead of Quaternion.transform() or some other domain specific term.

<cite>@pspeed said:</cite> I'm not sure a potentially expensive check to see if you've done something crazy with your rotations is a good idea every time rotate() is called. We tend to assume that users have at least a basic grasp of what they are doing and will otherwise quickly learn from their mistakes.

P.S.: There is no reason to use IDENTITY.clone()… you might as well just call new Quaternion() which is essentially the same thing.

Would probably be a case for the good old assert statement. ^^

@Empire Phoenix said: Would probably be a case for the good old assert statement. ^^

But where do you draw the line? The 0 quaternion is one of nearly an infinite number of invalid rotations. Should we test for all of them? Just the ones near 0? Some more obvious subset?

No matter how much hand-holding there will still be really simple ways to screw things up. That’s why we don’t just pick up our mouse and say “Computer… make me a Minecraft MMO.”

I agree with Paul, Quternions are combine by multiplication, to me it’s pretty obvious that if you multiply quat.ZERO with anything, you’ll end up with quat.ZERO.
We can’t check for every single bad argument, users have to know what they are doing.
The OP did find the problem in the end, so IMO that’s not an issue.

No not that much of an issue, but checking if a quaternion is sane is quite simple, is its length ? ~1 if not it cannoe be properly projected down into 3 dimensions.

At least a javadoc comment on quat.zero saying Are you sure you want to use this? See quat.identity instead as most people will want this.

I fell into this trap myself last year and I remember scratching my head for a good 15 minutes before I found the line that was messing up all subsequent transforms.

Think I may have been caught out by with this as well, when I was still learning about them (was a while ago), although I was getting caught out by a lot of stuff then xD. What happens if u try and multiply by an inverse of a 0 Quaternion?

If we are trying to protect users from themselves then another option is to remove the constants altogether. You’d have to specifically set a 0 quaternion, then since the default is identity.

And the reason I mention that is because it’s a way way way way way more common problem that people use the constants without cloning them. So if we are going to take any steps to prevent the much less likely error of dumb-math then maybe we should go all the way and remove the constants completely. Or document to never use them?

I’m only partially being facetious. There are a 1000 ways you can screw up math if you don’t understand what you are doing. We will never catch them all. I’m a firm believer that a) you should know a little bit about the math you are using before diving in, OR b) expect to get a few bumps and bruises as you learn things.

…but if we are going to hold hands then we should go all the way.

I think the problem here (from my point of view at least) is/was that if you have not that extensive knowledge of quaternion math and only use them for rotations in 3D you probably have worked a lot with vectors allready, and with vectors if you want a “neutral” one you take the ZERO and work from it. With quaternion rotaion this messes up everything you do afterwards with it.

Guess adding a javadoc comment on the ZERO quaternion that is is not fit to be used in rotation would be enough so that even if you make the mistake you can find it pretty easy.

@ryukajiya said: I think the problem here (from my point of view at least) is/was that if you have not that extensive knowledge of quaternion math and only use them for rotations in 3D you probably have worked a lot with vectors allready, and with vectors if you want a "neutral" one you take the ZERO and work from it. With quaternion rotaion this messes up everything you do afterwards with it.

Guess adding a javadoc comment on the ZERO quaternion that is is not fit to be used in rotation would be enough so that even if you make the mistake you can find it pretty easy.

Though I guess if you were using a ZERO vector and multiplying things by it later then you wouldn’t expect non-zero results…

I guess a Javadoc saying that the ZERO quaternion acts like zero and not like one is ok. It may be redundant but it’s useful for people who don’t know much about how rotations are accumulated and want to get fancy using the constants.

<cite>@pspeed said:</cite> Though I guess if you were using a ZERO vector and multiplying things by it later then you wouldn't expect non-zero results....

It was more along the lines:
In 2D starting with no translation you use Vector2f.Zero
In 3D starting with no translation you use Vector3f.Zero
From there assumption:
If you want to start with no rotation use Quaternion.Zero, which was the wrong assumption :wink:

@ryukajiya said: It was more along the lines: In 2D starting with no translation you use Vector2f.Zero In 3D starting with no translation you use Vector3f.Zero From there assumption: If you want to start with no rotation use Quaternion.Zero, which was the wrong assumption ;)

Well, while we’re on the subject because I think this is probably important now: Zero scale is not the starting scale. If you set your scale to zero then your model will disappear.

Haha that is obvious :wink: