Is it possible to Get HPR of "getSurfaceNormal"? (Solved)

Hello, I have a question, is there a fancy way to obtain the HPR (or at least the pitch) of the surface normal in panda3d? I heard that there are vector calculators in panda3d, maybe something like this for example…

entry.getSurfaceNormal.getH()
entry.getSurfaceNormal.getP()
entry.getSurfaceNormal.getR()
entry.getSurfaceNormal.getHpr()
or using blender 3d’s terms, the face, like…
entry.getSurfaceNormal.face().getH()
entry.getSurfaceNormal.face().getP()
entry.getSurfaceNormal.face().getR()
entry.getSurfaceNormal.face().getHpr()

if not, is there code to calculate this?, Let me explain my situation to make things clear, I,m using the “self.actor.headsup” code to allow my character to move on walls, and it is working great, but I have a problem, I want to implement “if” statements that can read the information of the “headsup” code before it actually takes effect.

So I can better control the situation of my game, I thank anyone who is willing to help.

If I understand this correctly, you want the rotation you need to rotate a given vector (eg. the forward vector) to match the surface normal? Since this would not include information about how much to roll about the vector axis, do you want an arbitrary roll angle?

I didn’t read your message carefully enough, sorry. To calculate the heads_up rotation given any vector without actually moving a node, you can directly use the global heads_up function (or headsUp if your codebase is still using camelCase) like this:

from panda3d.core import Quat, headsUp

quat = Quat()
headsUp(quat, surfaceNormal)

print(quat.getHpr())

heads_up takes an optional up vector as third argument if you want to change the up vector (default (0, 0, 1)). There is also a global look_at function corresponding to NodePath.look_at.

okay, thank you, that was fast, I,ll give it a try now.

EDIT: well I tried it, and well, this is embarrassing to say, but how do these numbers relate to the character’s pitch and roll? (especially the pitch) I tired to reduce 90 by the middle number as it seemed that the pitch was decided by the remainder to 90, but the numbers do not align too well.

A not elegant at all, but working code could look like this:

def normal_to_hpr(normal):
    temp=render.attach_new_node('temp')
    temp1=temp.attach_new_node('temp1')
    temp1.set_pos(normal)
    temp.look_at(temp1)
    hpr=temp.get_hpr()
    temp1.remove_node()
    temp.remove_node()
    return hpr

@jnpickee: the results from quat.getHpr() are what they would be on the character if you had called character.headsUp().

What exactly are you trying to do? we might be able to give more specific advice.

@wezu: it seems easier to just use the global look_at function, which doesn’t require the construction of temporary nodes.

Well, I,m trying to calculate the rotation of the character before it actually happens, here goes another example, lets say I was making a skate boarding game where the character has the ability to move with and without the skateboard (for some reason).

In skateboarding games like Tony Hawk ProSkater, you have the ability to “air” which is to move off a ramp into the air, in this process the character rotates to the curvature of the ramp until he/she flys up in a 90 degree position into the air, now this would possible with a skateboard because of it’s wheels thus the hi-speed.

But what about off the skateboard, yeah you can somewhat move to the curvature of the ramp with your feet, but you succumb to gravity rather quickly, I,m want to implement something similar to this, now this would be easy with a simple.

“if character on this polygon/face would turn 65.0 + don’t turn him/her above that” if statement, and there lies my problem, I need to the degrees information before the headsup can act.


wezu: I,ll look into that later, sadly I already have too many nodes going on right now, so much in fact I need to keep my game at 30fps, and I would have to do this nine more times as I have 10 physics based nodes in memory at any given time.


Edit: to make things more clearer, I basically want to check if a face has the ability to rotate the contacting node a certain way, before actually applying rotation.

If I understand correctly, the node in question would be rotated to align with the surface-normal (or to align its “up” vector with the surface normal), correct?

If so, then it seems to me that what you’re looking for is the vertical angle of the surface normal–which can be found via either a bit of trigonometry, or via the “signedAngle[Deg/Rad]” methods from Panda’s Vector-class.

If I’m not much mistaken–and I haven’t checked this thoroughly, so I may be–the two methods would look something like this:

  • Trigonometry:
    The vertical angle is asin(z-coordinate of the surface-normal)
  • signedAngle[Deg/Rad]:
    Where x, y, and z are the x-, y- and z- coordinates of the surface-normal:
    The vertical angle is surface-normal.signedAngleDeg(x, y, 0)
    (The “signedAngleDeg” method may be safer–my maths may be off.)

Yes correct, I,m trying to obtain the alignment in the form of the HPR that would be node’s new position before it happens, sorry, trigonometry is a bit beyond my mathematical capability, but are the signedAngle[Deg/Rad] part of panda’s calculators? how would you call them?

They’re methods in the Panda’s “Vector” classes.

There is a bit of complexity here, if I have it correctly:

If you want to use these methods with 3D vectors (e.g. Vec3 objects), they require an additional “reference vector”. This defines which way to consider to be a “positive” angle, given that you could look at the two vectors from either side in 3D. (See the API linked-to below for more detail on that.) In addition, your vectors should be normalised.

These aren’t the case with 2D vectors–you don’t need a “reference vector”, and you can get away without normalising your vectors.

You can turn your current 3D situation into a 2D one for these purposes, I believe, but that does call for some extra work.

Let me give you an example with 3D vectors:

someVector = Vec3(0.5, 7, 0)
anotherVector = Vec3(9.2, 0.2, 5.0)
referenceVector = Vec3(0, 0, 1)

someVector.normalize()
anotherVector.normalize()

angle = someVector.signedAngleDeg(anotherVector, referenceVector)

Here are the API entries for Vec3 and Vec2.

well, I tried your idea a couple of times a and got type errors, I,m guessing because I don’t know how to implement it correctly. here is the exact code I,m using as my headsup.

self.actor.headsup(self.actor, Point3(0, 1, 0), entry.getSurfaceNormal(self.render))

Do you know how to obtain (calculate) the pitch and roll from this code without rotating the node itself? sorry if I,m asking, but I obtained this line of code from somewhere on this board (and it works without me really understanding it), and I do not really understand it, only that the last argument is obviously the surface normal, and from what you told me, I,m guessing the middle the “reference vector”?

But what about the first argument? all it says, is the node path, what does that mean? Some point of reference?

okay, rdb, I swapped your syntax around and did this.

headsUp(quat, Point3(0, 1, 0), entry.getSurfaceNormal(self.render))

And it now seems similar to the pitch (appears to be working), but how does it calculate the roll also, do I do a separate quat for it?

OK, I see; picturing the skateboard, doing what you just did would always make the skateboard point in the forward direction while ensuring that the up axis conforms to the surface normal.

If I understand correctly, though, you don’t want to affect the forward direction, but keep the skateboard faced in the same direction; so what you could try is passing the current forward direction as the second parameter:

fwd = obj.getQuat().getForward()
quat = Quat()
headsUp(quat, fwd, entry.getSurfaceNormal(self.render))

You might also try replacing headsUp with lookAt to see how their effects differ; I think the main difference is that one will first rotate to match the up vector and the other will first rotate to match the forward vector.

The previous response to you has already solved my issue, I,m only now confirming it, because I had to take a day to rewrite a lot of my code to implement such a change, yes, your getHpr from a quat works with the missing reference vector mentioned by “Thaumaturge” included into it.

So thank you all, first to you “rdb” for the code, then “Thaumaturge” for teaching me about the reference vector to complete the code, and “wezu”, for the backup option, and with that being said, this issue has been solved.

1 Like