Sphere rolling relative to camera heading instead of its own

Hi all,

I have been trying to get a sphere to roll in the direction of the camera, similar to how BB8 from Star Wars or a Sphero ball.
self.character.setHpr(self.character,0,self.z_velocity *-5 *deltaTime * (math.cos(math.radians(self.playerBase.getH()))),(self.z_velocity * deltaTime *5* (-math.sin(math.radians(self.playerBase.getH())))))

One thing to note is that they work perfectly individually, so If roll is set to 0, the ball would rotate perfectly in the pitch axis whereas if pitch is set to 0 in the command above and roll remains unchanged, it would roll perfectly, if I combine the two movements it wouldn’t work and the rotation is a bit wonky.

The full code is available here, specifically player.py (code above is at line 250), and once again, if any parts of the code needs explaining, I am happy to clarify them :slight_smile:

In that case, why not set its HPR relative to the camera, rather than relative to the character?

If you want to prevent skipping when the camera is rotated without the ball being updated, you might be able to get the character’s relative HPR, modify that, and then set the result relatively. I’m not sure that this will work, but it seems like it might.

Otherwise, some quaternion-based rotation should work, using the camera’s coordinate-vectors as axes.

2 Likes

I agree with @Thaumaturge , though make your code easier to read.

will have a go at implementing this, thanks! :slight_smile:

1 Like

Still no luck :frowning:

    self.playerHolder.setY(self.playerBase, (self.z_velocity * deltaTime))
    self.quat = self.playerBase.getQuat()
    self.forwardQuat = self.quat.getRight()[0]
    self.rightQuat = self.quat.getForward()[0]
    # ,self.z_velocity*-10*deltaTime * self.forwardQuat
    self.character.setHpr(self.character,0,self.z_velocity*-10*deltaTime * self.forwardQuat,self.z_velocity*10*deltaTime * self.rightQuat)

GitHub also updated.

[EDIT]
The code above is essentially the same as the original one, so I am in doubt whether the way I’ve coded it matches what you had in mind.

Ah, not quite. I was thinking that you could construct a rotation-quaternion based on the camera’s vectors (via “setFromAxisAngle” method of the “Quat” class), and then apply that to the sphere’s quaternion in order that the rotation take effect.

(I think that this is done by multiplying the quaternions, but I forget exactly how this works offhand. Quaternions were never something that I really got into! ^^; )

1 Like

Oh that makes sense, thanks a lot!

I’ve been looking at the documentation and old forum posts, it seems like there is a minimal amount explanation to what goes into the two parameters (especially in my case). So do you mind clarifying what ‘angle’ and ‘axis’ I should feed into setFromAxisAngle?

As I understand it, a quaternion can be thought of as a rotation around an axis-vector–like turning something around a central rod.

So, in “setFromAxisAngle”, the “axis” is this axis-vector–the vector around which the thing will be rotated–and the angle is the amount by which to rotate it.

1 Like

Ah, gotcha

Thank you :slight_smile:

1 Like

I’ve got this:

        axis = camera.getHpr()
        axis.normalize()
        angle = (self.z_velocity*deltaTime*-5)
        quat = Quat()
        quat.setFromAxisAngle(angle,axis)
        newVec = quat.xform(self.character.getHpr())
        self.character.setHpr(newVec)

and I’m getting

line 252, in playerUpdate
    quat.setFromAxisAngle(angle,axis)
AssertionError: IS_THRESHOLD_EQUAL(axis.length(), 1.0f, 0.001f) at line 302 of built1.10/include/lquaternion_src.I

HPR values don’t form an axis–they’re a set of angles. (And if all said angles are zero, using them as a vector produces a zero-length vector, which may be the source of your problem.)

I’d suggest instead getting the camera’s “right”-vector, like so:

vec = camera.getQuat().getRight()

That is to say, getting the quaternion representation of the camera’s orientation, and then requesting the “right” vector from that representation.

Similarly, I don’t think that using the character’s HPR values will work–you’d likely want to get the character’s quaternion, apply your quaternion to that one, and then set the character’s orientation to be the result of that operation.

1 Like

Okay, worked on this and I got this so far, wanted to make sure I got this right before implementing it onto the actual character

        axis = camera.getQuat().getRight()
        axis.normalize()
        angle = (self.z_velocity*deltaTime*-5)
        quat = Quat()
        quat.setFromAxisAngle(angle,axis)
        newVec = quat*self.playerBase.getQuat()

Any changes needed? self.playerBase is the parent (or grandparent) of all the nodes in the player scene graph that consists of the model and the camera, so I felt it would be more necessary to take the quad of that rather than self.character which is the 3D model node.

Honestly, this is the point at which I’d likely be experimenting myself to find a working form. ^^;

That said, I’d likely work with “self.character” rather than the parent-node–that way one can just update the new rotation from the current. (Presuming that it’s “self.character” that you intend to rotate.)

Indeed, I think that at the moment it won’t produce quite the right result, as it would presumably always be starting from the same base value, and thus not continue from its orientation as of the previous frame.

As I’m not all that familiar with quaternions myself, I don’t know whether that last line has the two quaternions the right way around, offhand. Swapping their order is something that I might have in mind if the result doesn’t look right.

(By the way, I’m not sure that you need to normalize the right-vector–I think that it’s provided already unit-length. But as long as it’s not a performance issue, it’s likely pretty much harmless.)

1 Like

I have this so far (and I have tried another approach)

        axis = camera.getQuat().getRight()
        axis.normalize()
        angle = (self.z_velocity*deltaTime*-5)
        quat = Quat()
        quat.setFromAxisAngle(angle,axis)
        newVec = self.character.getQuat() * quat
        print(newVec.getHpr())
        if self.z_velocity > 0:
            self.character.setHpr(newVec.getHpr())

It moves correctly but only in one direction, plus I wasn’t sure where I can set the pitch rotation.

I am going experiment/play around with the code above more to see if I finally get a satisfactory result. If you see something above that needs fixing please let me know.

Hmm… I’m not sure of what’s going wrong, but I can suggest a few experiments to try:

  • When you create “newVec”, perhaps try swapping “quat” and “self.character.getQuat()”.
    • I honestly don’t remember which way around quaternion composition goes, and thus whether what you have is correct or not.
  • Since “self.character” isn’t attached directly to the root of the scene-graph, perhaps try setting its orientation relative to that root.
    • In this case, when constructing your final rotation you would presumably want to get “self.character”'s current rotation-quaternion relative to the root of the scene-graph, too.

By the way, you don’t need to convert to HPR in order to apply your quaternion–you should be able to just directly set it. Like so:

self.character.setQuat(newQuat)

Or to set it relative to another node (just as with “setHpr”, “setScale”, “setPos”, etc):

self.character.setQuat(otherNode, newQuat)
1 Like

The day has finally come where I think I’ve (or let’s say we’ve) solved it!

        axis = self.playerBase.getQuat().getRight()
        angle = (self.z_velocity*deltaTime*-5)
        quat = Quat()
        quat.setFromAxisAngle(angle, axis)
        newVec = self.character.getQuat()*quat
        #print(newVec.getHpr())
        if self.z_velocity > 0:
            self.character.setQuat(newVec)

Thanks a lot for your help @Thaumaturge :smiley:

1 Like

If welcomed, I could actually take a short amount of time during the next 2 weeks creating a sample program based on this.

1 Like

Ah, congratulations and well done on getting it working! I’m glad for you! :slight_smile:

1 Like