Reputation: 441
i'm trying to implement trackball by referring to this source https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball and https://www.khronos.org/opengl/wiki/Object_Mouse_Trackball
but after getting the rotation axis it doesn't seem to turn correctly
here's some snippet of my code
def compute_z(x, y):
# compute z from sphere model
# sphere size = 1
z = math.sqrt(abs(1 - math.pow(x,2) - math.pow(y,2)))
return z
def get_rotation_axis(vect_1, vect_2):
# determine rotation direction
axis = np.cross(vect_1, vect_2)
return axis
here's the main
while True:
mouse_pos = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.MOUSEMOTION:
if arcball_on:
cur_mx = mouse_pos[0]
cur_my = mouse_pos[1]
last_conv = convert_range(last_mx, last_my)
cur_conv = convert_range(cur_mx, cur_my)
a = (last_conv[0], last_conv[1], compute_z(last_conv[0], last_conv[1]))
b = (cur_conv[0], cur_conv[1], compute_z(cur_conv[0], cur_conv[1]))
angle = compute_angle(a, b)
axis = get_rotation_axis(a, b)
print(axis)
glRotatef(angle, axis[0], axis[1], -axis[2])
Upvotes: 3
Views: 168
Reputation: 3448
Generally your code works fine. Based on the references that you wrote, your compute_z
function might look like this:
def compute_z(x, y):
# compute z from sphere model
op_squared = x ** 2 + y ** 2
r_squared = SPHERE_R ** 2
if op_squared > r_squared / 2:
z = r_squared / 2 / math.sqrt (op_squared)
else:
z = math.sqrt(r_squared - op_squared)
return z
or even simpler:
def compute_z(x, y):
# compute z from sphere model
op_squared = x ** 2 + y ** 2
r_squared = SPHERE_R ** 2
if op_squared > r_squared:
return 0
else:
return math.sqrt(r_squared - op_squared)
where SPHERE_R
is the radius of a hypothetical sphere (default 1), because when the mouse clicks outside of the sphere, strange things might happen, while the first code approximates the shape to the hyperbolic sheet (following this reference) and second following this reference.
The angle
and axis
values are calculated correctly as I checked.
Furthermore the rotation vector should be normalized
:
def normalize(x):
x = np.asarray(x)
if np.linalg.norm(x):
return x / np.linalg.norm(x)
else:
return x
# .....
a = (last_conv[0], last_conv[1], compute_z(last_conv[0], last_conv[1]))
b = (cur_conv[0], cur_conv[1], compute_z(cur_conv[0], cur_conv[1]))
a = normalize(a)
b = normalize(b)
axis = get_rotation_axis(a, b)
axis = normalize(axis)
the glRotatef
function is working properly (you might also change the direction of the x axis, but it still works good.
When you perform a lot of rotations, you will see that the axis are not placed by the common sense, because of those rotations, but when you carefully and slowly move up/down of left/right using mouse - you will see that axis have rotated but the cube is still rotating as it should.
There is a trick that might change what you are rotating and it's used here. This way the rotation is made in object coordinates. I paste the description, but follow the details above.
An extra trick is converting the rotation axis from camera coordinates to object coordinates. It's useful when the camera and object are placed differently. For instance, if you rotate the object by 90° on the Y axis ("turn its head" to the right), then perform a vertical move with your mouse, you make a rotation on the camera X axis, but it should become a rotation on the Z axis (plane barrel roll) for the object. By converting the axis in object coordinates, the rotation will respect that the user work in camera coordinates (WYSIWYG). To transform from camera to object coordinates, we take the inverse of the MV matrix (from the MVP matrix triplet).
Upvotes: 1