Getting a Quaternion to represent rotation from a Vector3f to another Vector3f

Basically, I am looking into getting the same result as given by this method. Here is the example of application for the method. I am after exactly the same behavior of my game entity… Could anyone bring up an insight on how do I do this with what we have in our maths?

You can consider a Vector3f as a Quaternion with an angle-value set to zero. Just transform your Vector in a Quaternion and multiply it with the rotationquaternion.

What, no, a quaternion is definitely not the same as a angle-axis, a quaternion has no angle-value.
Here is one way to do it:

1 Like

I’m sorry, I didn’t read the title carefully enough. I thought you want to rotate your vector by a given quaternion.

Also i ment the real-coordinate (w) of the Quaternion. Angain, I’m sorry, it was a long day^^

Hmmm… alright… I also have to get the up vector for the spatial… how do I do that? I see there’s a bunch of methods in a Spatial that take in an up vector, but none returning one… Do I just multiply the Quaternion with a Vector3f.UNIT_Y ?

Here is the code that I’ve got with that, but it does not work the expected way… visually the entity just keeps rotating around Y at a very fast speed:

	OnPlanetUpright upright = e.get(OnPlanetUpright.class);
	Entity planet = upright.getPlanet();
	
	Vector3f planetPosition = ed.getComponent(planet.getId(), Position3d.class).getLocation();
	
	Vector3f entityPosition = ed.getComponent(e.getId(), Position3d.class).getLocation();
	Vector3f relative = entityPosition.subtract(planetPosition).normalize();

	Quaternion facing = ed.getComponent(e.getId(), Position3d.class).getFacing();

	Vector3f up = facing.mult(Vector3f.UNIT_Y);
	Vector3f a = relative.mult(up);//up.mult(relative);
	float w = FastMath.sqrt( up.lengthSquared() * relative.lengthSquared()) + up.dot(relative);
	
	Quaternion diff = new Quaternion(a.x, a.y, a.z, w).normalizeLocal();
	
	ed.getComponent(e.getId(), Position3d.class).getFacing().set(diff.mult(facing));

Is there a reason you aren’t using the approach in the linked material?

I’m not sure why you need the local up vector in this case if what you really want is to rotate one reference frame to another… but maybe you want to maintain local up or something?

Anyway, it would help to know why you aren’t using the one from stackoverflow and that might help explain the other questions.

And actually, can you explain what you are trying to do at a higher level to make sure we aren’t falling victim to the x/y problem?

Strange for me, since I was sure I implemented the algorithm described in the Stack Overflow accepted answer! Look, I am multiplying the two vectors to get the x, y and z and then I get the w element as described in the post and then normalize… isn’t it so? (maybe I am misunderstanding something here). Then I adjust the current facing of the entity by the resulting rotation quaternion…

My original task is the same as in the linked video - I have a not so big spherical planet world, just about the size as it is in the video, and I want my character to walk upright on its surface. So the characters feet must always look toward the center of the planet and its head - toward the space. So I was thinking about that problem and looked for info and found the video, which implements this very thing in Unity3D.

Yet I was unable to properly implement the concept, however, starting from the link provided by jmaasing, I am looking into more matrials, and currently the best result is acheived by duplicating what is in there by that link. But still I’m not getting it quite right…

Just checking… you know that mult() is waaaaay different than cross(), right?

As to “what you are trying to do”… I get that you want feet on a planet and stuff. But why do you need to get the rotation from one vector to another vector? I could do a lot of planet walking without ever needing to do that.

I guess if you are interested in finding how to rotate the character you could use an axis-angle. You know where the center of the planet is and you know where the feet should be so there you have an axis, the ‘forward’ of the character is the angle around this axis. You can convert an axis-angle to a quaternion or a rotation matrix.
But as pspeed says. Let us know what you are trying to accomplish because there might be alternative ways to do it.

Oh, wow, how could I misinterpret mult for cross… maybe the reason is that in my native language cross bears the same short name as multiplication (the full name, although, makes the distinction)… okay, I fixed that. But this still gives strange results.

pspeed, jmaasing:
I am trying to get the rotation difference of one vector from the other because I want to know how do I rotate my entity so that it becomes aligned like I said before. I know that maybe there could be some simplification, but to me this data is valuable because I am going to go further and implement planetary vehicles. An aircraft would certainly require such data for proper navigation in planetary atmosphere. When you’re saying about other ways,I know there could be simplifications, but see, I’d really like to have the most complete navigational data. For example, to implement something like that device:

http://www.nashvillecfi.com/pics/ils2-172.png

I am starting to get the math, recalling things I’ve learned in school! So, here is what I have got:

	OnPlanetUpright upright = e.get(OnPlanetUpright.class);
	Entity planet = upright.getPlanet();

	Vector3f planetPosition = ed.getComponent(planet.getId(), Position3d.class).getLocation();

	Vector3f entityPosition = ed.getComponent(e.getId(), Position3d.class).getLocation();
	Vector3f relative = entityPosition.subtract(planetPosition).normalize();

	Quaternion facing = ed.getComponent(e.getId(), Position3d.class).getFacing();

	Vector3f up = facing.mult(Vector3f.UNIT_Y.clone());

	relative.normalizeLocal();
	up.normalizeLocal();
	
	float dot = up.dot(relative);
	Vector3f cross = up.cross(relative);
	
	cross.normalizeLocal();
	
	Quaternion res = null;
	
	if (dot > 0.99999999f) {
		System.out.println("case A");
		res = Quaternion.IDENTITY.clone();
	} else if (dot < -0.99999999f) {
		System.out.println("case B");
		Vector3f axis = up.cross(Vector3f.UNIT_X.clone());
		if(axis.lengthSquared() == 0) {
			axis = up.cross(Vector3f.UNIT_Y.clone());
		}
		axis.normalize();
		res = new Quaternion().fromAngleAxis(FastMath.PI, axis);
	} else {
		System.out.println("case C");
		// --- way 1, from SO
                    //  float w = FastMath.sqrt(up.lengthSquared() * relative.lengthSquared()) + dot;
                    //  res = new Quaternion(cross.x, cross.y, cross.z, w);
		// --- way 2, from Ogre source
		float s = FastMath.sqrt((1f + dot) * 2f);
		float inv = 1f / s;
		res = new Quaternion(cross.x * inv, cross.y * inv, cross.z * inv, s * 0.5f);
	}
	res = res.normalizeLocal();
	
	// - debug output
	Vector3f angles = new Vector3f();
	res.toAngleAxis(angles);
	System.out.println("q = " + res + "   angles = " + angles);
	
	ed.getComponent(e.getId(), Position3d.class).getFacing().set(facing.mult(res));

This algorithm sometimes works ok, it works just fine in case A and B and sometimes in case C. However, sometimes in case C it keeps bouncing between two angles. Looks like I have all maths ok, also it is the same as within the Orge code which is bound to be correct. Here is some output that I am getting from the “debug output” part:

case C
q = (0.45777223, 0.06726092, 0.07316848, 0.88349694)   angles = (0.9772336, 0.14358589, 0.1561971)
case C
q = (-0.45777225, -0.0672609, -0.07316846, 0.883497)   angles = (-0.97723365, -0.14358583, -0.15619706)
case C
q = (0.45777228, 0.0672609, 0.073168434, 0.883497)   angles = (0.9772337, 0.14358583, 0.15619701)
case C
q = (-0.4577722, -0.06726089, -0.07316841, 0.883497)   angles = (-0.9772337, -0.14358586, -0.156197)
case C
q = (0.45777225, 0.067260884, 0.0731684, 0.883497)   angles = (0.97723365, 0.1435858, 0.15619692)
case C
q = (-0.45777225, -0.06726088, -0.07316838, 0.883497)   angles = (-0.97723365, -0.14358579, -0.15619689)
case C
q = (0.45777225, 0.06726087, 0.07316837, 0.883497)   angles = (0.97723365, 0.14358577, 0.15619686)
case C
q = (-0.45777225, -0.06726087, -0.073168375, 0.883497)   angles = (-0.97723365, -0.14358577, -0.15619688)
case C
q = (0.45777225, 0.06726086, 0.07316836, 0.883497)   angles = (0.97723365, 0.14358576, 0.15619685)
case C
q = (-0.45777225, -0.06726086, -0.07316836, 0.883497)   angles = (-0.97723365, -0.14358576, -0.15619685)
case C
q = (0.45777225, 0.06726085, 0.07316835, 0.883497)   angles = (0.97723365, 0.14358573, 0.15619683)
case C
q = (-0.45777225, -0.06726085, -0.073168375, 0.883497)   angles = (-0.97723365, -0.14358573, -0.15619688)
case C
q = (0.45777225, 0.06726088, 0.07316838, 0.883497)   angles = (0.97723365, 0.14358579, 0.15619689)
case C
q = (-0.45777217, -0.06726084, -0.07316838, 0.883497)   angles = (-0.97723365, -0.14358574, -0.15619692)
case C
q = (0.45777225, 0.06726082, 0.07316834, 0.883497)   angles = (0.97723377, 0.14358568, 0.15619682)
case C
q = (-0.45777217, -0.067260794, -0.07316833, 0.883497)   angles = (-0.97723377, -0.14358567, -0.15619683)
case C
q = (0.4577723, 0.06726076, 0.07316831, 0.88349694)   angles = (0.97723377, 0.14358553, 0.15619673)
case C
q = (-0.45777217, -0.06726079, -0.07316837, 0.883497)   angles = (-0.97723365, -0.14358564, -0.15619689)
case C
q = (0.45777225, 0.067260824, 0.07316843, 0.883497)   angles = (0.97723365, 0.14358568, 0.15619698)

see, it’s bouncing between “-” and “+”… probably I am incoherent with getting my Vector3f up ?

But why rotate it versus just setting the proper rotation? See, the first way is difficult to calculate but the second way is drop-dead simple… so is there a reason you must rotate from the current orientation to another or can you just set the proper orientation directly?

Hmm, well… if later the aircraft be controlled by an AI, it must know how to navigate it, thus I presume it must know how to rotate it correctly… so this is why I am struggling with this, also trying to understand the maths within this… The rotation is also to be done gradually in such a case…

What about the way of setting the required orientation immediately? What is this way?

This kind of math-intensive games is new to me, so I’m like, making the first steps. I hope getting on with time, like, taking a good grip of this area of maths…

Well, see… even that is different because you’ll probably have a target rotation and a source rotation and interpolate. Or you are already working in some spherical space (from the AI’s perspective) where none of that matters. The AI would only know altitude and heading, etc…

Given:
Vector3f feet = …
Vector3f planetCenter = …
Vector3f currentLookDir = …

Presuming you want the object to be “looking” in the same direction as before…
Rotation:
Vector3f up = feet.subtract(planetCenter).normalizeLocal(); // surface normal of the planet
Vector3f left = currentLookDir.cross(up);
Vector3f newUp = left.cross(currentLookDir);
Quaternion rotation = new Quaternion().fromAxes(left, newUp, currentLookDir);

So with that, if you were looking towards some distant object, as you moved around you’d keep looking at that same object. ie: not with relative pitch from the surface (you’d look up and down as you moved closer and farther respectively).

To truly simulate walking/flying over the surface you will probably want to keep track of position in spherical coordinates (like: lat, lon location with direction as pitch and heading).

You can do the “maintain pitch” thing using the basis rotation I provided above but it’s more complicated and really if you are trying to simulate flight/movement on a planet then it is better to use a planetary reference system.

Edit: note that it’s possible I got my cross products backwards… it’s important. Some debugging would tell you if so.

1 Like

It really looks like I couldn’t’ve chosen an easier task for starters :smiley:

Thank you for posting the algorithm. I think that I will have two separate systems: one for walking characters, which must walk the ground and keep it upright, standing. This algorithm will suit for that… the currentLookDir can be a vector, controlled by the mouse, I guess… And another one coordinate system for surface wheeled and aircraft vehicles which are better tracked in spherical coords system and converted to cartesian only for absolute in-world placement and rotation.

Well, note that “current look dir” is in “world space” and not “planet surface space”.

Even a walking character will want to have a concept of spherical coordinates and then the mouse controls yaw and pitch (just like normal 3D environments).

Now I have spent so much time reading about the issue and trying different ways to implement it… I do all in accordance as it is written in the advices I have found, I’ve done it in several different math approaches, and it should work as I see it in my mind, the way I understand it. But it works paritally. The entity moves almost okay in some equator around the sphere, but at certain hemispheres it goes crazy with rotation… I cannot explain that. This is the closest explanation to waht I get - just crazy swinging rotations… but they have solved it… I have also verified that in no other places in code is any rotation applied to the object. Maybe someone with good understanding of math can shed some light… and why does it work everywhere else - in Unity3D, in Ogre and other places where I have seen the answer did work for the asking person? Just look at the video from the original post, it is so effortless there.

Is not there a way to get a quaternion to rotate the object with axis xyz into x’y’z’ where y’ is spheres normal?:

If there is no way, how does it work in Unity3D? Just look at the video in my original post, they do it in a very simple operation… In my mind that operation, as I imagine it, should give the correct result despite anything… I can’t stop thinking of that problem, it does not give any rest to me… it’s turning principal… what is it so special about it?

here is my code as of now…

		Vector3f planetPosition = ed.getComponent(planet.getId(), Position3d.class).getLocation();
		Vector3f entityPosition = ed.getComponent(e.getId(), Position3d.class).getLocation();
//			Vector3f relative = entityPosition.subtract(planetPosition).normalize();
		Vector3f relative = planetPosition.subtract(entityPosition).normalize();

		Quaternion facing = ed.getComponent(e.getId(), Position3d.class).getFacing();
		Vector3f up = facing.mult(Vector3f.UNIT_Y.clone());
		
		relative.normalizeLocal();
		up.normalizeLocal();
		
		float dot = up.dot(relative);
		Vector3f cross = up.cross(relative);
		
		up.normalizeLocal();
     		cross.normalizeLocal();
		Quaternion res = null;
		
		if (dot > 0.99999999f) {
			System.out.println("case A");
			res = Quaternion.IDENTITY.clone();
		} else if (dot < -0.99999999f) {
			System.out.println("case B");
			Vector3f axis = up.cross(Vector3f.UNIT_X.clone());
			if(axis.lengthSquared() == 0) {
				axis = up.cross(Vector3f.UNIT_Y.clone());
			}
			axis.normalize();
			res = new Quaternion().fromAngleAxis(FastMath.PI, axis);
		} else {
			// --- way 1, from SO
			float ww = FastMath.sqrt(up.lengthSquared() * relative.lengthSquared()) + dot;
			//res = new Quaternion(ww, cross.x, cross.y, cross.z);
			res = new Quaternion().fromAngleAxis(ww, cross);
			// --- way 2, from Ogre source
                            // float s = FastMath.sqrt((1f + dot) * 2f);
                            // float inv = 1f / s;
                            // res = new Quaternion(cross.x * inv, cross.y * inv, cross.z * inv, s * 0.5f);
                        // --- way 3 from some other forum....
                            // float f2 = FastMath.acos(dot);
                            // res = new Quaternion().fromAngleAxis(FastMath.cos(f2 / 2), cross.mult(FastMath.sin(f2 / 2)));
                            // res.lookAt(facing.mult(Vector3f.UNIT_Z.clone()), relative);
		}
		res = res.normalizeLocal();
		
		ed.getComponent(e.getId(), Position3d.class).getFacing().set(facing.mult(res));

PS: I know, I think that I better reside to the local pitch/yaw/roll system, and I am going for it now, but this problem does not leave my attention… I feel like I misunderstand something important and like this misunderstanding can spring up again in some other place, so I really want to resolve it completely…