Right now I’m trying to work on a class that effectively works as a “gravity field” that calculates an up vector, and then rotates a player character or other game object to have that up vector.
Unfortunately, I’m running into a problem where calling nodepath.heads_up() is constantly causing any nodepath’s relative heading to one or two directions per point in the gravity field. Effectively, it snaps the camera back to a specific direction if you try to look to the left or right.
For the record, I’ve also tried getting fwd with creature.np.get_quat(creature.np.get_parent()).get_forward() But that caused the same issue with an additional issue of the camera repeatedly turning 180 degrees as the player object moved.
Is there some other way of rotating a game object to match an up vector or some way of using heads_up that’s more forgiving of relative heading?
P.S.:
If it helps, here’s how I’m calculating the up vector for a sphere:
You need a different approach, namely two calculus systems. You are now apparently using the principle of inheritance, in this case the transformation of the camera is imposed by the character. Which apparently doesn’t work as well as we would like, I suggest you synchronize the camera yourself in relation to the character’s body. To do this, you can use world coordinates, for example, you can easily match the position of the head point and the camera. It remains to calculate the direction and acceptable angles of rotation of the camera, relative to the position of the character’s body.
Hmm… Maybe, instead of using “heads_up”, it would work better to calculate a rotation to apply to your object.
In short, the smallest rotate between your old orientation and your new orientation should be the angle between the old up-vector and the new up-vector, while the axis around which rotate should be the cross-product of those same two vectors.
With this, you should be able to construct a rotation quaternion, which you could then apply to your object, I believe.
I like this idea! However, I don’t yet have any experience working with quaternions however. So far, my rotations seem arbitrary, and more prone to affecting gameobjects’ relative heading… Sorry, is there a mistake I’m making here?
Edit: Experimented on my own to try and get things right, now the player just rotates wildly out of control. I’ve been updating the code I posted above to match what I’ve been trying.
Should the new value of “gravUpVec” not be just “upVec”?
And there’s always the old standby of trying to negate values (e.g. the angle that you calculate).
Otherwise, if you can put together a short-and-simple test-program, I can try to debug it on my end!
[edit]
One more thought: Have you tested the code that generates up-vectors for your spheres?
It feels… complex, for what it seems to be doing, and without knowing the various details of it (e.g. what “player.np” is parented to) I find it hard to assess what it’s doing…
That was how I was doing it before, but I realized that if I don’t retrieve the actual up vector for the player’s np, small discrepancies can build up until I’m applying a rotation based on a starting vector totally different from the actual up-vector.
Well, it’s intention is to first get the position of the player (Which is the same kind of object as “creature”, my bad for not keeping the standard) in the space of the spherical gravity field. (This of course assumes that the origin of the spherical gravity field is in the center-of-mass.) Then, it normalizes that vector, and uses get_relative_vector() to translate it into the coordinate-space of player/creature’s parent, as that is also the easiest coordinate space to get the current up-vector in.
In practice, player/creature’s parent is almost always going to be equivalent to base.render, but my game does have ridable entities that creature classes can reparent to/from, so I keep the retrieval dynamic.
(As I’m writing this, I realize I should probably just get gravUpVec dynamically; as reparenting can invalidate it)
If I have time later, I might try to split this logic from my larger project for debugging. However, I have other things that take priority today.
This may or may not make sense to you, but when approaching a problem like this, I’d probably get rid of the inheritance-based vector relationships for a start. Then, I’d define a dedicated node for comparison that uses look_at() to get a referential basis for comparisons to arbitrary nodes in the scene graph.
For the case of ridable creatures, I’d suggest applying this logic to the creature, and then simply parenting the player to that creature, allowing the player’s orientation to then naturally follow that of the creature.
Quite how this would be handled internally might depend on the internal structures of your game.
It might be as simple as replacing the player’s model with that of the creature, attaching the player’s actual model to said creature-model, and then tweaking the player’s values to represent the creature.
Or it might involve temporarily replacing the logical player-object with a new one featuring the creature–keeping the player-object aside for reuse when the player dismounts/loses their ride–and again just reparenting the player-model to the model of that new “player”.
Or something else besides!
Which might lead to a simpler approach to the gravity-vector thing…
Would it not be simpler to just take the player’s new position (relative to “render”), subtract the sphere’s position (likewise relative), and then normalise the result?
That should lead to a vector in world-space that points from the centre of the sphere out to the player’s position on the surface of the sphere, which should match the surface normal at that point, I believe.
[edit]
Update: I found the problem rather interesting, so I decided to sit down and take a shot at it myself.
And I think that I’ve put together a working example program!
It’s rough and simple, but hopefully should demonstrate the principles of the thing.
([edit 2] In this example , the player-driven rotation is overridden by the sphere-surface rotation. Fixing that is left as an exercise for the reader. [/edit 2])
Of note:
I ended up negating the angle gained from “angleDeg”
I’m calculating the up-vector simply from the positions of the player and the sphere
The new quaternion is made by multiplying the player’s quaternion by the rotation quaternion–not the other way around!
The program uses Panda’s default models, so it should run just as-is, I believe!
Thanks to you checking for that specific case in your example program, I was able to discern that the problem with arbitrary rotations was due to the cross product between the new and old vectors being too small. Thank you so much for your help!