def up(self, x, y, z): """Set the camera up vector. Args: args (:mod:`taichi.types.primitive_types`): 3D coordinates. Example:: >>> camera.up(0, 1, 0) """ self.curr_up = Vector([x, y, z]) self.ptr.up(x, y, z)
def _gauss_elimination_3x3(Ab, dt): for i in static(range(3)): max_row = i max_v = ops.abs(Ab[i, i]) for j in static(range(i + 1, 3)): if ops.abs(Ab[j, i]) > max_v: max_row = j max_v = ops.abs(Ab[j, i]) assert max_v != 0.0, "Matrix is singular in linear solve." if i != max_row: if max_row == 1: for col in static(range(4)): Ab[i, col], Ab[1, col] = Ab[1, col], Ab[i, col] else: for col in static(range(4)): Ab[i, col], Ab[2, col] = Ab[2, col], Ab[i, col] assert Ab[i, i] != 0.0, "Matrix is singular in linear solve." for j in static(range(i + 1, 3)): scale = Ab[j, i] / Ab[i, i] Ab[j, i] = 0.0 for k in static(range(i + 1, 4)): Ab[j, k] -= Ab[i, k] * scale # Back substitution x = Vector.zero(dt, 3) for i in static(range(2, -1, -1)): x[i] = Ab[i, 3] for k in static(range(i + 1, 3)): x[i] -= Ab[i, k] * x[k] x[i] = x[i] / Ab[i, i] return x
def to_u8_rgba(image): if not hasattr(image, 'n') or image.m != 1: raise Exception( 'the input image needs to be a Vector field (matrix with 1 column)' ) if len(image.shape) != 2: raise Exception( "the shape of the image must be of the form (width,height)") if image.dtype == u8 and image.n == 4: # already in the desired format return image if image not in image_field_cache: staging_img = Vector.field(4, u8, image.shape) image_field_cache[image] = staging_img else: staging_img = image_field_cache[image] if image.dtype == u8: copy_image_u8_to_u8(image, staging_img, image.n) elif image.dtype == f32: copy_image_f32_to_u8(image, staging_img, image.n) else: raise Exception("dtype of input image must either be u8 or f32") return staging_img
def get_normals_field(vertices): if vertices not in normals_field_cache: N = vertices.shape[0] normals = Vector.field(3, f32, shape=(N, )) normal_weights = field(f32, shape=(N, )) normals_field_cache[vertices] = (normals, normal_weights) return (normals, normal_weights) return normals_field_cache[vertices]
def sym_eig2x2(A, dt): """Compute the eigenvalues and right eigenvectors (Av=lambda v) of a 2x2 real symmetric matrix. Mathematical concept refers to https://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix. Args: A (ti.Matrix(2, 2)): input 2x2 symmetric matrix `A`. dt (DataType): date type of elements in matrix `A`, typically accepts ti.f32 or ti.f64. Returns: eigenvalues (ti.Vector(2)): The eigenvalues. Each entry store one eigen value. eigenvectors (ti.Matrix(2, 2)): The eigenvectors. Each column stores one eigenvector. """ tr = A.trace() det = A.determinant() gap = tr**2 - 4 * det lambda1 = (tr + ops.sqrt(gap)) * 0.5 lambda2 = (tr - ops.sqrt(gap)) * 0.5 eigenvalues = Vector([lambda1, lambda2], dt=dt) A1 = A - lambda1 * Matrix.identity(dt, 2) A2 = A - lambda2 * Matrix.identity(dt, 2) v1 = Vector.zero(dt, 2) v2 = Vector.zero(dt, 2) if all(A1 == Matrix.zero(dt, 2, 2)) and all(A1 == Matrix.zero(dt, 2, 2)): v1 = Vector([0.0, 1.0]).cast(dt) v2 = Vector([1.0, 0.0]).cast(dt) else: v1 = Vector([A2[0, 0], A2[1, 0]], dt=dt).normalized() v2 = Vector([A1[0, 0], A1[1, 0]], dt=dt).normalized() eigenvectors = Matrix.cols([v1, v2]) return eigenvalues, eigenvectors
def track_user_inputs(self, window, movement_speed=1.0, yaw_speed=2, pitch_speed=2, hold_key=None): front = (self.curr_lookat - self.curr_position).normalized() position_change = Vector([0.0, 0.0, 0.0]) left = self.curr_up.cross(front) up = self.curr_up if window.is_pressed('w'): position_change = front * movement_speed if window.is_pressed('s'): position_change = -front * movement_speed if window.is_pressed('a'): position_change = left * movement_speed if window.is_pressed('d'): position_change = -left * movement_speed if window.is_pressed('e'): position_change = up * movement_speed if window.is_pressed('q'): position_change = -up * movement_speed self.position(*(self.curr_position + position_change)) self.lookat(*(self.curr_lookat + position_change)) if hold_key is not None: if not window.is_pressed(hold_key): self.last_mouse_x = None self.last_mouse_y = None return curr_mouse_x, curr_mouse_y = window.get_cursor_pos() if self.last_mouse_x is None or self.last_mouse_y is None: self.last_mouse_x, self.last_mouse_y = curr_mouse_x, curr_mouse_y return dx = curr_mouse_x - self.last_mouse_x dy = curr_mouse_y - self.last_mouse_y yaw, pitch = vec_to_euler(front) yaw_speed = 2 pitch_speed = 2 yaw -= dx * yaw_speed pitch += dy * pitch_speed pitch_limit = pi / 2 * 0.99 if pitch > pitch_limit: pitch = pitch_limit elif pitch < -pitch_limit: pitch = -pitch_limit front = euler_to_vec(yaw, pitch) self.lookat(*(self.curr_position + front)) self.up(0, 1, 0) self.last_mouse_x, self.last_mouse_y = curr_mouse_x, curr_mouse_y
def get_vbo_field(vertices): if vertices not in vbo_field_cache: N = vertices.shape[0] pos = 3 normal = 3 tex_coord = 2 color = 4 vertex_stride = pos + normal + tex_coord + color vbo = Vector.field(vertex_stride, f32, shape=(N, )) vbo_field_cache[vertices] = vbo return vbo return vbo_field_cache[vertices]
def lookat(self, x, y, z): """Set the camera lookat. Args: args (:mod:`taichi.types.primitive_types`): 3D coordinates. Example:: >>> camera.lookat(0, 0, 0) """ self.curr_lookat = Vector([x, y, z]) self.ptr.lookat(x, y, z)
def position(self, x, y, z): """Set the camera position. Args: args (:mod:`taichi.types.primitive_types`): 3D coordinates. Example:: >>> camera.position(1, 1, 1) """ self.curr_position = Vector([x, y, z]) self.ptr.position(x, y, z)
def _gauss_elimination_2x2(Ab, dt): if ops.abs(Ab[0, 0]) < ops.abs(Ab[1, 0]): Ab[0, 0], Ab[1, 0] = Ab[1, 0], Ab[0, 0] Ab[0, 1], Ab[1, 1] = Ab[1, 1], Ab[0, 1] Ab[0, 2], Ab[1, 2] = Ab[1, 2], Ab[0, 2] assert Ab[0, 0] != 0.0, "Matrix is singular in linear solve." scale = Ab[1, 0] / Ab[0, 0] Ab[1, 0] = 0.0 for k in static(range(1, 3)): Ab[1, k] -= Ab[0, k] * scale x = Vector.zero(dt, 2) # Back substitution x[1] = Ab[1, 2] / Ab[1, 1] x[0] = (Ab[0, 2] - Ab[0, 1] * x[1]) / Ab[0, 0] return x
def gen_normals_kernel_indexed(vertices: template(), indices: template(), normals: template(), weights: template()): num_triangles = indices.shape[0] / 3 num_vertices = vertices.shape[0] for i in range(num_vertices): normals[i] = Vector([0.0, 0.0, 0.0]) weights[i] = 0.0 for i in range(num_triangles): i_a = indices[i * 3] i_b = indices[i * 3 + 1] i_c = indices[i * 3 + 2] a = vertices[i_a] b = vertices[i_b] c = vertices[i_c] n = (a - b).cross(a - c).normalized() atomic_add(normals[i_a], n) atomic_add(normals[i_b], n) atomic_add(normals[i_c], n) atomic_add(weights[i_a], 1.0) atomic_add(weights[i_b], 1.0) atomic_add(weights[i_c], 1.0) for i in range(num_vertices): if weights[i] > 0.0: normals[i] = normals[i] / weights[i]
def sym_eig3x3(A, dt): """Compute the eigenvalues and right eigenvectors (Av=lambda v) of a 3x3 real symmetric matrix using Cardano's method. Mathematical concept refers to https://www.mpi-hd.mpg.de/personalhomes/globes/3x3/. Args: A (ti.Matrix(3, 3)): input 3x3 symmetric matrix `A`. dt (DataType): date type of elements in matrix `A`, typically accepts ti.f32 or ti.f64. Returns: eigenvalues (ti.Vector(3)): The eigenvalues. Each entry store one eigen value. eigenvectors (ti.Matrix(3, 3)): The eigenvectors. Each column stores one eigenvector. """ M_SQRT3 = 1.73205080756887729352744634151 m = A.trace() dd = A[0, 1] * A[0, 1] ee = A[1, 2] * A[1, 2] ff = A[0, 2] * A[0, 2] c1 = A[0, 0] * A[1, 1] + A[0, 0] * A[2, 2] + A[1, 1] * A[2, 2] - (dd + ee + ff) c0 = A[2, 2] * dd + A[0, 0] * ee + A[1, 1] * ff - A[0, 0] * A[1, 1] * A[ 2, 2] - 2.0 * A[0, 2] * A[0, 1] * A[1, 2] p = m * m - 3.0 * c1 q = m * (p - 1.5 * c1) - 13.5 * c0 sqrt_p = ops.sqrt(ops.abs(p)) phi = 27.0 * (0.25 * c1 * c1 * (p - c1) + c0 * (q + 6.75 * c0)) phi = (1.0 / 3.0) * ops.atan2(ops.sqrt(ops.abs(phi)), q) c = sqrt_p * ops.cos(phi) s = (1.0 / M_SQRT3) * sqrt_p * ops.sin(phi) eigenvalues = Vector([0.0, 0.0, 0.0], dt=dt) eigenvalues[2] = (1.0 / 3.0) * (m - c) eigenvalues[1] = eigenvalues[2] + s eigenvalues[0] = eigenvalues[2] + c eigenvalues[2] = eigenvalues[2] - s t = ops.abs(eigenvalues[0]) u = ops.abs(eigenvalues[1]) if u > t: t = u u = ops.abs(eigenvalues[2]) if u > t: t = u if t < 1.0: u = t else: u = t * t Q = Matrix.zero(dt, 3, 3) Q[0, 1] = A[0, 1] * A[1, 2] - A[0, 2] * A[1, 1] Q[1, 1] = A[0, 2] * A[0, 1] - A[1, 2] * A[0, 0] Q[2, 1] = A[0, 1] * A[0, 1] Q[0, 0] = Q[0, 1] + A[0, 2] * eigenvalues[0] Q[1, 0] = Q[1, 1] + A[1, 2] * eigenvalues[0] Q[2, 0] = (A[0, 0] - eigenvalues[0]) * (A[1, 1] - eigenvalues[0]) - Q[2, 1] norm = Q[0, 0] * Q[0, 0] + Q[1, 0] * Q[1, 0] + Q[2, 0] * Q[2, 0] norm = ops.sqrt(1.0 / norm) Q[0, 0] *= norm Q[1, 0] *= norm Q[2, 0] *= norm Q[0, 1] = Q[0, 1] + A[0, 2] * eigenvalues[1] Q[1, 1] = Q[1, 1] + A[1, 2] * eigenvalues[1] Q[2, 1] = (A[0, 0] - eigenvalues[1]) * (A[1, 1] - eigenvalues[1]) - Q[2, 1] norm = Q[0, 1] * Q[0, 1] + Q[1, 1] * Q[1, 1] + Q[2, 1] * Q[2, 1] norm = ops.sqrt(1.0 / norm) Q[0, 1] *= norm Q[1, 1] *= norm Q[2, 1] *= norm Q[0, 2] = Q[1, 0] * Q[2, 1] - Q[2, 0] * Q[1, 1] Q[1, 2] = Q[2, 0] * Q[0, 1] - Q[0, 0] * Q[2, 1] Q[2, 2] = Q[0, 0] * Q[1, 1] - Q[1, 0] * Q[0, 1] return eigenvalues, Q
def eig2x2(A, dt): """Compute the eigenvalues and right eigenvectors (Av=lambda v) of a 2x2 real matrix. Mathematical concept refers to https://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix. Args: A (ti.Matrix(2, 2)): input 2x2 matrix `A`. dt (DataType): date type of elements in matrix `A`, typically accepts ti.f32 or ti.f64. Returns: eigenvalues (ti.Matrix(2, 2)): The eigenvalues in complex form. Each row stores one eigenvalue. The first number of the eigenvalue represents the real part and the second number represents the imaginary part. eigenvectors: (ti.Matrix(4, 2)): The eigenvectors in complex form. Each column stores one eigenvector. Each eigenvector consists of 2 entries, each of which is represented by two numbers for its real part and imaginary part. """ tr = A.trace() det = A.determinant() gap = tr**2 - 4 * det lambda1 = Vector.zero(dt, 2) lambda2 = Vector.zero(dt, 2) v1 = Vector.zero(dt, 4) v2 = Vector.zero(dt, 4) if gap > 0: lambda1 = Vector([tr + ops.sqrt(gap), 0.0], dt=dt) * 0.5 lambda2 = Vector([tr - ops.sqrt(gap), 0.0], dt=dt) * 0.5 A1 = A - lambda1[0] * Matrix.identity(dt, 2) A2 = A - lambda2[0] * Matrix.identity(dt, 2) if all(A1 == Matrix.zero(dt, 2, 2)) and all( A1 == Matrix.zero(dt, 2, 2)): v1 = Vector([0.0, 0.0, 1.0, 0.0]).cast(dt) v2 = Vector([1.0, 0.0, 0.0, 0.0]).cast(dt) else: v1 = Vector([A2[0, 0], 0.0, A2[1, 0], 0.0], dt=dt).normalized() v2 = Vector([A1[0, 0], 0.0, A1[1, 0], 0.0], dt=dt).normalized() else: lambda1 = Vector([tr, ops.sqrt(-gap)], dt=dt) * 0.5 lambda2 = Vector([tr, -ops.sqrt(-gap)], dt=dt) * 0.5 A1r = A - lambda1[0] * Matrix.identity(dt, 2) A1i = -lambda1[1] * Matrix.identity(dt, 2) A2r = A - lambda2[0] * Matrix.identity(dt, 2) A2i = -lambda2[1] * Matrix.identity(dt, 2) v1 = Vector([A2r[0, 0], A2i[0, 0], A2r[1, 0], A2i[1, 0]], dt=dt).normalized() v2 = Vector([A1r[0, 0], A1i[0, 0], A1r[1, 0], A1i[1, 0]], dt=dt).normalized() eigenvalues = Matrix.rows([lambda1, lambda2]) eigenvectors = Matrix.cols([v1, v2]) return eigenvalues, eigenvectors
def euler_to_vec(yaw, pitch): v = Vector([0.0, 0.0, 0.0]) v[0] = -sin(yaw) * cos(pitch) v[1] = sin(pitch) v[2] = -cos(yaw) * cos(pitch) return v
def up(self, x, y, z): self.curr_up = Vector([x, y, z]) self.ptr.up(x, y, z)
def position(self, x, y, z): self.curr_position = Vector([x, y, z]) self.ptr.position(x, y, z)
def track_user_inputs(self, window, movement_speed=1.0, yaw_speed=2, pitch_speed=2, hold_key=None): """Move the camera according to user inputs. Press `w`, `s`, `a`, `d`, `e`, `q` to move the camera `formard`, `back`, `left`, `right`, `head up`, `head down`, accordingly. Args: window (:class:`~taichi.ui.Window`): a windown instance. movement_speed (:mod:`~taichi.types.primitive_types`): camera movement speed. yaw_speed (:mod:`~taichi.types.primitive_types`): speed of changes in yaw angle. pitch_speed (:mod:`~taichi.types.primitive_types`): speed of changes in pitch angle. hold_key (:mod:`~taichi.ui`): User defined key for holding the camera movement. """ front = (self.curr_lookat - self.curr_position).normalized() position_change = Vector([0.0, 0.0, 0.0]) left = self.curr_up.cross(front) up = self.curr_up if window.is_pressed('w'): position_change = front * movement_speed if window.is_pressed('s'): position_change = -front * movement_speed if window.is_pressed('a'): position_change = left * movement_speed if window.is_pressed('d'): position_change = -left * movement_speed if window.is_pressed('e'): position_change = up * movement_speed if window.is_pressed('q'): position_change = -up * movement_speed self.position(*(self.curr_position + position_change)) self.lookat(*(self.curr_lookat + position_change)) if hold_key is not None: if not window.is_pressed(hold_key): self.last_mouse_x = None self.last_mouse_y = None return curr_mouse_x, curr_mouse_y = window.get_cursor_pos() if self.last_mouse_x is None or self.last_mouse_y is None: self.last_mouse_x, self.last_mouse_y = curr_mouse_x, curr_mouse_y return dx = curr_mouse_x - self.last_mouse_x dy = curr_mouse_y - self.last_mouse_y yaw, pitch = vec_to_euler(front) yaw_speed = 2 pitch_speed = 2 yaw -= dx * yaw_speed pitch += dy * pitch_speed pitch_limit = pi / 2 * 0.99 if pitch > pitch_limit: pitch = pitch_limit elif pitch < -pitch_limit: pitch = -pitch_limit front = euler_to_vec(yaw, pitch) self.lookat(*(self.curr_position + front)) self.up(0, 1, 0) self.last_mouse_x, self.last_mouse_y = curr_mouse_x, curr_mouse_y
class Camera: def __init__(self, ptr): self.ptr = ptr self.position(0.0, 0.0, 0.0) self.lookat(0.0, 0.0, 1.0) self.up(0.0, 1.0, 0.0) # used for tracking user inputs self.last_mouse_x = None self.last_mouse_y = None def position(self, x, y, z): self.curr_position = Vector([x, y, z]) self.ptr.position(x, y, z) def lookat(self, x, y, z): self.curr_lookat = Vector([x, y, z]) self.ptr.lookat(x, y, z) def up(self, x, y, z): self.curr_up = Vector([x, y, z]) self.ptr.up(x, y, z) def projection_mode(self, mode): self.ptr.projection_mode(mode) def fov(self, fov): self.ptr.fov(fov) def left(self, left): self.ptr.left(left) def right(self, right): self.ptr.right(right) def top(self, top): self.ptr.top(top) def bottom(self, bottom): self.ptr.bottom(bottom) def z_near(self, z_near): self.ptr.z_near(z_near) def z_far(self, z_far): self.ptr.z_far(z_far) # move the camera according to user inputs, FPS game style. def track_user_inputs(self, window, movement_speed=1.0, yaw_speed=2, pitch_speed=2, hold_key=None): front = (self.curr_lookat - self.curr_position).normalized() position_change = Vector([0.0, 0.0, 0.0]) left = self.curr_up.cross(front) if window.is_pressed('w'): position_change = front * movement_speed if window.is_pressed('s'): position_change = -front * movement_speed if window.is_pressed('a'): position_change = left * movement_speed if window.is_pressed('d'): position_change = -left * movement_speed self.position(*(self.curr_position + position_change)) self.lookat(*(self.curr_lookat + position_change)) if hold_key is not None: if not window.is_pressed(hold_key): self.last_mouse_x = None self.last_mouse_y = None return curr_mouse_x, curr_mouse_y = window.get_cursor_pos() if self.last_mouse_x is None or self.last_mouse_y is None: self.last_mouse_x, self.last_mouse_y = curr_mouse_x, curr_mouse_y return dx = curr_mouse_x - self.last_mouse_x dy = curr_mouse_y - self.last_mouse_y yaw, pitch = vec_to_euler(front) yaw_speed = 2 pitch_speed = 2 yaw -= dx * yaw_speed pitch += dy * pitch_speed pitch_limit = pi / 2 * 0.99 if pitch > pitch_limit: pitch = pitch_limit elif pitch < -pitch_limit: pitch = -pitch_limit front = euler_to_vec(yaw, pitch) self.lookat(*(self.curr_position + front)) self.up(0, 1, 0) self.last_mouse_x, self.last_mouse_y = curr_mouse_x, curr_mouse_y
class Camera: """The Camera class. You should not instantiate this class by calling the `__init__` method directly, please use the helper function :func:`~taichi.ui.make_camera()` instead. Args: ptr (:class:`~taichi._ti_core.PyCamera`): An instance of the `PyCamera` \ class from the C++ level. Example:: >>> scene = ti.ui.Scene() # assume you have a scene >>> >>> camera = ti.ui.make_camera() >>> camera.position(1, 1, 1) # set camera position >>> camera.lookat(0, 0, 0) # set camera lookat >>> camera.up(0, 1, 0) # set camera up vector >>> scene.set_camera(camera) >>> >>> # you can also control camera movement in a window >>> window = ti.ui.Window("GGUI Camera", res=(640, 480), vsync=True) >>> camera.track_user_inputs(window, movement_speed=0.03, hold_key=ti.ui.RMB) """ def __init__(self, ptr): self.ptr = ptr self.position(0.0, 0.0, 0.0) self.lookat(0.0, 0.0, 1.0) self.up(0.0, 1.0, 0.0) # used for tracking user inputs self.last_mouse_x = None self.last_mouse_y = None def position(self, x, y, z): """Set the camera position. Args: args (:mod:`taichi.types.primitive_types`): 3D coordinates. Example:: >>> camera.position(1, 1, 1) """ self.curr_position = Vector([x, y, z]) self.ptr.position(x, y, z) def lookat(self, x, y, z): """Set the camera lookat. Args: args (:mod:`taichi.types.primitive_types`): 3D coordinates. Example:: >>> camera.lookat(0, 0, 0) """ self.curr_lookat = Vector([x, y, z]) self.ptr.lookat(x, y, z) def up(self, x, y, z): """Set the camera up vector. Args: args (:mod:`taichi.types.primitive_types`): 3D coordinates. Example:: >>> camera.up(0, 1, 0) """ self.curr_up = Vector([x, y, z]) self.ptr.up(x, y, z) def projection_mode(self, mode): """Camera projection mode, 0 for perspective and 1 for orthogonal. """ self.ptr.projection_mode(mode) def fov(self, fov): """Set the camera fov angle (field of view) in degrees. Args: fov (:mod:`taichi.types.primitive_types`): Angle in range (0, 180). Example:: >>> camera.fov(45) """ self.ptr.fov(fov) def left(self, left): """Set the offset of the left clipping plane in camera frustum. Args: left (:mod:`taichi.types.primitive_types`): \ offset of the left clipping plane. Example:: >>> camera.left(-1.0) """ self.ptr.left(left) def right(self, right): """Set the offset of the right clipping plane in camera frustum. Args: right (:mod:`taichi.types.primitive_types`): \ offset of the right clipping plane. Example:: >>> camera.right(1.0) """ self.ptr.right(right) def top(self, top): """Set the offset of the top clipping plane in camera frustum. Args: top (:mod:`taichi.types.primitive_types`): \ offset of the top clipping plane. Example:: >>> camera.top(-1.0) """ self.ptr.top(top) def bottom(self, bottom): """Set the offset of the bottom clipping plane in camera frustum. Args: bottom (:mod:`taichi.types.primitive_types`): \ offset of the bottom clipping plane. Example:: >>> camera.bottom(1.0) """ self.ptr.bottom(bottom) def z_near(self, z_near): """Set the offset of the near clipping plane in camera frustum. Args: near (:mod:`taichi.types.primitive_types`): \ offset of the near clipping plane. Example:: >>> camera.near(0.1) """ self.ptr.z_near(z_near) def z_far(self, z_far): """Set the offset of the far clipping plane in camera frustum. Args: far (:mod:`taichi.types.primitive_types`): \ offset of the far clipping plane. Example:: >>> camera.left(1000.0) """ self.ptr.z_far(z_far) def get_view_matrix(self): """Get the view matrix(in row major) of the camera. Example:: >>> camera.get_view_matrix() """ return self.ptr.get_view_matrix() def get_projection_matrix(self, aspect): """Get the projection matrix(in row major) of the camera. Args: aspect (:mod:`taichi.types.primitive_types`): \ aspect ratio of the camera Example:: >>> camera.get_projection_matrix(1080/720) """ return self.ptr.get_projection_matrix(aspect) def track_user_inputs(self, window, movement_speed=1.0, yaw_speed=2, pitch_speed=2, hold_key=None): """Move the camera according to user inputs. Press `w`, `s`, `a`, `d`, `e`, `q` to move the camera `formard`, `back`, `left`, `right`, `head up`, `head down`, accordingly. Args: window (:class:`~taichi.ui.Window`): a windown instance. movement_speed (:mod:`~taichi.types.primitive_types`): camera movement speed. yaw_speed (:mod:`~taichi.types.primitive_types`): speed of changes in yaw angle. pitch_speed (:mod:`~taichi.types.primitive_types`): speed of changes in pitch angle. hold_key (:mod:`~taichi.ui`): User defined key for holding the camera movement. """ front = (self.curr_lookat - self.curr_position).normalized() position_change = Vector([0.0, 0.0, 0.0]) left = self.curr_up.cross(front) up = self.curr_up if window.is_pressed('w'): position_change = front * movement_speed if window.is_pressed('s'): position_change = -front * movement_speed if window.is_pressed('a'): position_change = left * movement_speed if window.is_pressed('d'): position_change = -left * movement_speed if window.is_pressed('e'): position_change = up * movement_speed if window.is_pressed('q'): position_change = -up * movement_speed self.position(*(self.curr_position + position_change)) self.lookat(*(self.curr_lookat + position_change)) if hold_key is not None: if not window.is_pressed(hold_key): self.last_mouse_x = None self.last_mouse_y = None return curr_mouse_x, curr_mouse_y = window.get_cursor_pos() if self.last_mouse_x is None or self.last_mouse_y is None: self.last_mouse_x, self.last_mouse_y = curr_mouse_x, curr_mouse_y return dx = curr_mouse_x - self.last_mouse_x dy = curr_mouse_y - self.last_mouse_y yaw, pitch = vec_to_euler(front) yaw_speed = 2 pitch_speed = 2 yaw -= dx * yaw_speed pitch += dy * pitch_speed pitch_limit = pi / 2 * 0.99 if pitch > pitch_limit: pitch = pitch_limit elif pitch < -pitch_limit: pitch = -pitch_limit front = euler_to_vec(yaw, pitch) self.lookat(*(self.curr_position + front)) self.up(0, 1, 0) self.last_mouse_x, self.last_mouse_y = curr_mouse_x, curr_mouse_y
def lookat(self, x, y, z): self.curr_lookat = Vector([x, y, z]) self.ptr.lookat(x, y, z)