Problem calling and assigning rotation values more than pi radians

So I know how to get local orientations with http://shuvit.org/python_api/bge.types.KX_GameObject.html#KX_GameObject.localOrientation

Is there any way to assign “delta” rotation values.

In my case, I am trying to control a tachometer using an rpm property.

import math, mathutils
from math import pi
from bge import logic

cont = logic.getCurrentController()
own = cont.owner

orient = own.localOrientation.to_euler
own["rot"] = orient[1]     # getting y rotation
baseAngle = (own["rpm"]*(pi/6000))        # sets amount of rotation per rpm

own["rot"] += (baseAngle - own["rot"])*0.25        # smooth output to get a nice transition

newOrient = orient
newOrient[0] = 0
newOrient[1] = own["rot"]
newOrient[2] = 0

own.localOrientation = newOrient.to_matrix()

Video (showing rpm values are ok)

The rpm needle goes ballistic if rpm > 6000.

Any ideas?

This was one simplified way I attempted (using some logic bricks for inputs and python for handling the tach) (UP Arrow for increasing RPMS)- tach_test_img.blend (632.3 KB)

First I chose a graphic to represent the tach background. Then built a needle to match it. Centered it and then rotated it to 0 RPM(also applied the rotation but that probably wasn’t necessary). Then parented it to an empty that I placed at the rotation center of the needle. I then grabbed the empty and rotated it to the point that I considered ‘pegged’(i chose to use the last mark or 9000 RPM). Made a note of the degrees of rotation, which in this case was 270 degrees = pi and a half = 4.71 radians. I then rotated the empty(needle’s parent) back to 0.

NOTE - The logic I set up for the test is using 100 RPM steps to make the needle climb and drop faster for quick results. And the min and max_rpm properties are unused(they were just for reference while building).

Now I decided to create a rotation matrix based on the smallest step I would use to move the needle. Since I’m using RPM steps of 100 and the max RPM will be 9000, we need 90 rotation steps with 4.71 radians. That comes to 0.0523 radians per step (made it negative to rotate clockwise).

With that value(multiplied by our current RPM over stepsize), we create the 3x3 rotation matrix on the axis of rotation(I used Z axis).

rot_step = mathutils.Matrix.Rotation(-0.0523 * (own['cur_rpm']/100), 3, 'Z')

and then we just apply the rotation

own.localOrientation = rot_step

…it’s an idea.

Well it definitely shows that using localOrientation works for values more than pi. I’m wondering if the euler conversion is the problem.

The euler conversion is fine. What’s causing the problem in your code is own[‘rot’]. Specifically when you alter it on line 12. Are you using that solely to smooth out the rotation? If you use baseAngle for newOrient.y and use slow parent you will get a smooth rotation(that is if it’s parented to something).

Here’s one using euler to matrix conversion with slow parent to smooth it - tach_test_img_euler.blend (639.6 KB)

Thanks. Yes, this is causing the issue:

own["rot"] += (baseAngle - own["rot"])*0.25

This works but there is no smoothing. The problem with slow parent is that it affects location and so the needle would drift off the dash at 100kph. I think I need to redo my damping formula.

import math, mathutils
from math import pi
from bge import logic

cont = logic.getCurrentController()
own = cont.owner

orient = own.localOrientation.to_euler()
own["rot"] = orient[1]

baseAngle = (own["rpm"]*(pi/6000))

# own["rot"] += (baseAngle - own["rot"])*0.25

newOrient = mathutils.Euler((0, baseAngle, 0))

own.localOrientation = newOrient.to_matrix()

what is your base angle, degrees or radians?
to_euller() outputs radians, so if you using degrees then you should convert it with

radians = math.radians(degrees)
degrees = math.degrees(radians)

Wait up!

@sodsm_live

I tested the smoothing formula with this script and the values are fine:

import math
from math import pi

rpm = 0
orient = 0
drpm = 10

while rpm < 8500:
    rpm += drpm
    print(rpm, end=" | ")
    baseAngle = (rpm*(pi/6000))
    print("%.6f" %baseAngle, end=" | ")
    orient += (baseAngle - orient)*0.25
    print("%.6f" %orient)

Output sample @ rpm > 6000:

7000 | 3.665191 | 3.649483
7010 | 3.670427 | 3.654719
7020 | 3.675663 | 3.659955
7030 | 3.680899 | 3.665191
7040 | 3.686135 | 3.670427
7050 | 3.691371 | 3.675663
7060 | 3.696607 | 3.680899
7070 | 3.701843 | 3.686135
7080 | 3.707079 | 3.691371
7090 | 3.712315 | 3.696607
7100 | 3.717551 | 3.701843

So next suspect was orient[1]:

import math, mathutils
from math import pi
from bge import logic

cont = logic.getCurrentController()
own = cont.owner

orient = own.localOrientation.to_euler()
own["rot"] = orient[1]

print("%.6f" %orient[1])

baseAngle = (own["rpm"]*(pi/6000))

own["rot"] += (baseAngle - own["rot"])*0.25

newOrient = mathutils.Euler((0, own["rot"], 0))

own.localOrientation = newOrient.to_matrix()

Output @ rpm ~ 6000 to 7000

2.997561
3.045972
3.094402
-3.140311
-1.521661
-0.295519
0.634154
1.343621
1.887920
2.308345
2.635841
2.893750
3.099432
-3.017539
-1.310131

The values go negative for some reason. Values of more than pi rads are not being assigned???

Values are all in radians.

Orientation values range from -3.14 to 3.14 representing 360 degrees. They’re going negative because you’re getting your orientation from the object which only knows -3.14 to 3.14. It goes crazy as it passes 180 degrees and your formula gets unexpected negative numbers.

So we are back at the initial suspected problem. I need to find a way to call and apply “cumulative” rotation like the delta rotation option in object settings. But I have no idea if this is included in the API.

What about if orient[1] is less than 0, add 2PI to it?

# Just get the float value instead of the whole Euler object
orient = own.localOrientation.to_euler()[1] 
if orient < 0:
    orient += 3.14*2 # Always stay positive

baseAngle = own['cur_rpm'] * 3.14/6000
orient += (baseAngle - orient)*0.25

newOrient = mathutils.Euler((0.0, orient, 0.0), 'XYZ')
    
own.localOrientation = newOrient.to_matrix()

Consolidating code here. You could use own[‘rot’] instead of orient but doesn’t seem to be needed as persistent.

EDIT - For cumulative rotation you could use own.applyRotation([0,rot_speed,0], True)
where rot_speed is your preset/dynamic needle speed. Then you would only use orientation to check to see if it is where you want it.

Yes that works for now. Thanks a million!

Used own[“rot”] for debugging

Final script:

import math, mathutils
from math import pi
from bge import logic

cont = logic.getCurrentController()
own = cont.owner

baseAngle = (own["rpm"]*(pi/6000))

orient = own.localOrientation.to_euler()[1]

# Convert negative radian values to postive

if orient < 0:
    orient = 2*pi+orient

baseAngle = (own["rpm"]*(pi/6000))

orient += (baseAngle - orient)*0.25

newOrient = mathutils.Euler((0, orient, 0))

own.localOrientation = newOrient.to_matrix()
1 Like