Rotating turret in JME that follows the camera

Hello,

I am attempting to get a turret that follows the rotation of the camera.

To do this, I have a separate spatial in my node called “Turret”, and I am then using this code to rotate it:

Spatial tur = plane.getChild("Turret"); Vector3f cDi = cam.getDirection(); Quaternion quat = new Quaternion(); quat.lookAt(cDi, new Vector3f(0, 1, 0)); Quaternion tWo = tur.getWorldRotation(); if (tWo.getY() < quat.getY()){ tur.rotate(0, 1 * tpf, 0); } if (tWo.getY() > quat.getY()){ tur.rotate(0, -1 * tpf, 0); }

This, technically speaking, works. The problem is that when the camera goes a full 360 degrees, the camera pops from 360 to 0 (or actually, I believe it is 180 to -180 in JME’s case), causing the turret to turn 360 degrees in the other direction before facing where the camera is again.

How can I solve this problem to make the turret rotate smoothly with the camera, such as in World Of Tanks, etc?

Thanks,
Joe

Well this is a bit complicated:

Do not use angles , you kinda wanto to use quatnernions , and then update it with an increment.

Kinda like this:

private float determineAngleDifference(final float targetYaw, final float currentYaw) {
	float yawDiff = targetYaw - currentYaw;
	if (yawDiff < -180) {
		yawDiff += 360;
	}
	if (yawDiff > 180) {
		yawDiff -= 360;
	}
	return yawDiff;
}

How would I use that method to do anything? I currently can’t see how knowing the difference in angle will solve the problem.

Edit: I see what you mean, but I just can’t manage this no matter how hard I try.

I can’t manage to do it, no matter how hard I try. I can’t find any method to simply set the turrets’ rotation either, and if I set the local rotation it just adds it into that of the vehicle…

So, any help appreciated. Basically, if I could just set the turrets’ rotation by yaw that should solve the issue.

Not sure and maybe I say nothing new to you, but why not use the existing chase camera?

1 Like

What you’d actually want is:
You want to set the turrets rotation to the rotation of the camera.
Something like:

Turret.setWorldRotation(Camera.getRotation());

If there is no setWorldRotation, you would set the localRotation but multiply the cam’s rotation with the inverse of turret’s parent’s worldrotation

That’d glue your turret into the camera direction. What you’d now want is a) modify the rotation so you are y-axis aligned. I can’t help you with that

b) you want it to turn smooth. This is possible with Interpolation or rather “slerp” (s? linear blah blah of quaternions :D)

So you’d have something like:

turret.getLocalRotation().interpolate/slerp(turret.parent.getWorldRotation().inverse().mult(camera.getLocalRotation()), x);

Note: This is pseudocode and especially the order of quaternion multiplications have to be tested empirically since I can never remember it :smiley:

The Problem here is x, I am currently looking for a solution as well. X actually describes how much the turret is already turned into the camera direction. The Problem is: When someone turns the camera before the turret has approached the correct position, you’d have to set x so it is the position it used to be before the camera turn, which is problematic or even impossible.

If you have a good idea, tell me, I’m looking for something comparable :smiley:
Try to simply use some float val = 0f; val += tpf; then you have 1 second turn time. Or you could use something like 2*tpf for 0.5s …

1 Like

I’m not sure what that should do to solve the issue.

Hmmm I did not see what follows your camera in that animation. It looked like a camera oribit around your vehicle. Like a chase camera. sorry just ignore it :slight_smile:

Accessing the values of a quaternion directly like this is kind of magic nonsense. When I see users do this it makes me wonder if they really know what the values of a quaternion are. At any rate, what you are trying to do won’t work by accessing the magic 4D values like that.

Presumably, you have a current rotation and some target rotation… then just use nlerp or slerp to interpolate.

Missed the rest of the thread before answering… if you have a world rotation and want your turret to have that in its local rotation then you need to do a change of basis.

Something like:
Quaternion world = …
Quaternion parent.getWorldRotation();
Quaternion local = parent.inverse().mult(world);

…or I might have the mult backwards…
world.mult(parent.inverse()).

Pre and post mult always screws me up.

Edit: which I guess is what DarkChaos had embedded in his answer without actually calling it out as to what it was. Even to the point of not remember pre or post mult. :slight_smile:

2 Likes

Huge thanks to both of you - With this code, the issue is now solved:

  Spatial turret = plane.getChild("Turret");
    
        
    Quaternion min2 = plane.getLocalRotation().inverse();
    Quaternion camD = cam.getRotation().clone();
    //Quaternion camDr = cam.getRotation().inverse();
    //camD.set(camDr.getX(), camD.getY(), camDr.getZ(), camD.getW());
    min2.multLocal(camD);
    min2.set(0, min2.getY(), 0, min2.getW());
    turret.setLocalRotation(min2);`

It isn’t perfect, but it will certainly be good enough for an early alpha, especially if I make the bullets follow the cameras’ pitch rather than the guns.

Still related to turret rotation, I am now trying to add guns to the turret. This works perfectly fine if the gun is located at the same location as the turret, but when I have tried five guns inside the turret, simply changing the rotation doesn’t solve the issue - I need to change the translation as well, to be where the new side of the turret is.

I currently can’t work it out. One could use a rotation matrix, but then what am I rotating? Or, I would prefer to be able to somehow set the guns to be a child of the turret, but I can’t work out how to do that either.

1 Like

You have your turret
You know like 10 cm left from center is one barrel
Worldposition of turret
WorldRotation of turret

now 10cm left is
left = new Vector3f(0.1,0,0)

turretWorldLeft = worldrotation.mult(left) (converts from turretCorrdinate system to worldCordinate System)
is now left as seen from the turret

inWorld10cmLeftOfTurret = worldPosition of turret + turretWorldLeft

But how would I use this information to set the guns to all be in their correct places? I just can’t work it out at the moment.

The guns are in the same j3o file as the vehicle and turret.

Hmm… Why not attach them to the same node as the turret so they automatically are rotated together?
Or is there a case where they both are supposed to be rotated differently?

How would I do this?

Obviously all objects there are the child of the node (so if I load the j3o file they are all children), but how in the j3o file do I make all the guns the child of the turret?

Any time you find yourself setting the values of a Quaternion directly like this then you are doing something wrong. That is creating an invalid rotation. Quaternion values are mostly magic and cannot be manipulated this way. They are not an “angle axis” or some other thing that you can manipulate the values. They are a vector on a 4D unit sphere. Unless you can easily think in four dimensions, it’s best to avoid trying to piece-meal one together like this.

It seems to work well enough for me. The turret turns perfectly, as you saw in the video I posted above.

What would you suggest as a better alternative?

Edit: it does as I intended too, as the intention was to stop the turret taking the pitch of the camera.

Just a matter of luck as that’s definitely not a valid rotation… at minimum, you’ve cleared two values and haven’t renormalized. Some combination of transforms may start making every vector3f turn at as NaN… you just don’t know.

Setting the values of a quaternion like this is never right. It is mostly an indicator that the person writing the code thinks that a Quaternion is something that it isn’t. My guess is that you think it might be an angle-axis.

If all you want is to stop the taking of pitch then you need to convert the quaternion into something where ‘pitch’ even makes sense, clear the pitch, and then set it back. (for example, using toAngles() and fromAngles())

But really, it might be better to find your rotation through lookAt() where the y component of the view direction has already been cleared. Everything else will be error prone in some odd edge case.

To do that, you’d probably want to use the camera’s direction in the turret’s local space. (worldToLocal()) Your best bet there is to do something like:
Vector3f worldDirPoint = turret.getWorldTranslation().add(cam.getDirection());
Vector3f localCamDir = turret.worldToLocal(worldDirPoint);

…that should give you a unit vector in turret space as to where the camera is looking. A quaternion made from that (with the y component set to 0) should look in the camera’s direction but without pitch. Note: some camera directions may still confuse it as there are some points of ambiguity that cannot be resolved… but your camera may never hit those angles. (For example, looking straight up or down relative to the y axis of the turret)