mirror of
https://github.com/3b1b/manim.git
synced 2025-11-14 03:47:44 +00:00
Changed camera frame from working in quaternions to working in Euler angles. For shame.
This commit is contained in:
parent
38cc0a7174
commit
a232c32756
1 changed files with 52 additions and 37 deletions
|
|
@ -14,9 +14,10 @@ from manimlib.utils.simple_functions import fdiv
|
||||||
from manimlib.utils.shaders import shader_info_to_id
|
from manimlib.utils.shaders import shader_info_to_id
|
||||||
from manimlib.utils.shaders import shader_id_to_info
|
from manimlib.utils.shaders import shader_id_to_info
|
||||||
from manimlib.utils.shaders import get_shader_code_from_file
|
from manimlib.utils.shaders import get_shader_code_from_file
|
||||||
from manimlib.utils.space_ops import cross
|
from manimlib.utils.simple_functions import clip
|
||||||
|
from manimlib.utils.space_ops import angle_of_vector
|
||||||
from manimlib.utils.space_ops import rotation_matrix_transpose_from_quaternion
|
from manimlib.utils.space_ops import rotation_matrix_transpose_from_quaternion
|
||||||
from manimlib.utils.space_ops import normalize
|
from manimlib.utils.space_ops import rotation_matrix_transpose
|
||||||
from manimlib.utils.space_ops import quaternion_from_angle_axis
|
from manimlib.utils.space_ops import quaternion_from_angle_axis
|
||||||
from manimlib.utils.space_ops import quaternion_mult
|
from manimlib.utils.space_ops import quaternion_mult
|
||||||
|
|
||||||
|
|
@ -26,65 +27,72 @@ class CameraFrame(Mobject):
|
||||||
"width": FRAME_WIDTH,
|
"width": FRAME_WIDTH,
|
||||||
"height": FRAME_HEIGHT,
|
"height": FRAME_HEIGHT,
|
||||||
"center_point": ORIGIN,
|
"center_point": ORIGIN,
|
||||||
# The quaternion describing how to rotate into
|
# Theta, phi, gamma
|
||||||
# the position of the camera.
|
"euler_angles": [0, 0, 0],
|
||||||
"rotation_quaternion": [1, 0, 0, 0],
|
|
||||||
"focal_distance": 5,
|
"focal_distance": 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
def init_points(self):
|
def init_points(self):
|
||||||
self.points = np.array([self.center_point])
|
self.points = np.array([self.center_point])
|
||||||
self.rotation_quaternion = quaternion_from_angle_axis(angle=0, axis=OUT)
|
self.euler_angles = np.array(self.euler_angles, dtype='float64')
|
||||||
|
|
||||||
def to_default_state(self):
|
def to_default_state(self):
|
||||||
self.center()
|
self.center()
|
||||||
self.set_height(FRAME_HEIGHT)
|
self.set_height(FRAME_HEIGHT)
|
||||||
self.set_width(FRAME_WIDTH)
|
self.set_width(FRAME_WIDTH)
|
||||||
self.set_rotation_quaternion([1, 0, 0, 0])
|
self.set_rotation(0, 0, 0)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_inverse_camera_position_matrix(self):
|
def get_inverse_camera_position_matrix(self):
|
||||||
# Map from real space into camera space
|
|
||||||
result = np.identity(4)
|
result = np.identity(4)
|
||||||
# First shift so that origin of real space coincides with camera origin
|
# First shift so that origin of real space coincides with camera origin
|
||||||
result[:3, 3] = -self.get_center().T
|
result[:3, 3] = -self.get_center().T
|
||||||
# Rotate based on camera orientation
|
# Rotate based on camera orientation
|
||||||
result[:3, :3] = np.dot(
|
result[:3, :3] = np.dot(self.get_inverse_camera_rotation_matrix(), result[:3, :3])
|
||||||
rotation_matrix_transpose_from_quaternion(self.rotation_quaternion),
|
|
||||||
result[:3, :3]
|
|
||||||
)
|
|
||||||
# Scale to have height 2 (matching the height of the box [-1, 1]^2)
|
# Scale to have height 2 (matching the height of the box [-1, 1]^2)
|
||||||
result *= 2 / self.height
|
result *= 2 / self.height
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def rotate(self, angle, axis=OUT, **kwargs):
|
def get_inverse_camera_rotation_matrix(self):
|
||||||
self.rotation_quaternion = normalize(quaternion_mult(
|
theta, phi, gamma = self.euler_angles
|
||||||
quaternion_from_angle_axis(angle, axis),
|
quat = quaternion_mult(
|
||||||
self.rotation_quaternion
|
|
||||||
))
|
|
||||||
return self
|
|
||||||
|
|
||||||
def set_rotation(self, phi=0, theta=0, gamma=0):
|
|
||||||
self.rotation_quaternion = normalize(quaternion_mult(
|
|
||||||
quaternion_from_angle_axis(theta, OUT),
|
quaternion_from_angle_axis(theta, OUT),
|
||||||
quaternion_from_angle_axis(phi, RIGHT),
|
quaternion_from_angle_axis(phi, RIGHT),
|
||||||
quaternion_from_angle_axis(gamma, OUT),
|
quaternion_from_angle_axis(gamma, OUT),
|
||||||
))
|
)
|
||||||
|
return rotation_matrix_transpose_from_quaternion(quat)
|
||||||
|
|
||||||
|
def rotate(self, angle, axis=OUT, **kwargs):
|
||||||
|
curr_rot_T = self.get_inverse_camera_rotation_matrix()
|
||||||
|
added_rot_T = rotation_matrix_transpose(angle, axis)
|
||||||
|
new_rot_T = np.dot(curr_rot_T, added_rot_T)
|
||||||
|
Fz = new_rot_T[2]
|
||||||
|
phi = np.arccos(Fz[2])
|
||||||
|
theta = angle_of_vector(Fz[:2]) + PI / 2
|
||||||
|
partial_rot_T = np.dot(
|
||||||
|
rotation_matrix_transpose(phi, RIGHT),
|
||||||
|
rotation_matrix_transpose(theta, OUT),
|
||||||
|
)
|
||||||
|
gamma = angle_of_vector(np.dot(partial_rot_T, new_rot_T.T)[:, 0])
|
||||||
|
# TODO, write a function that converts quaternions to euler angles
|
||||||
|
self.euler_angles[:] = theta, phi, gamma
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_rotation_quaternion(self, quat):
|
def set_rotation(self, theta=0, phi=0, gamma=0):
|
||||||
self.rotation_quaternion = quat
|
self.euler_angles[:] = theta, phi, gamma
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def increment_theta(self, dtheta):
|
def increment_theta(self, dtheta):
|
||||||
self.rotate(dtheta, OUT)
|
self.euler_angles[0] += dtheta
|
||||||
|
return self
|
||||||
|
|
||||||
def increment_phi(self, dphi):
|
def increment_phi(self, dphi):
|
||||||
# TODO, this seems clunky
|
self.euler_angles[1] = clip(self.euler_angles[1] + dphi, 0, PI)
|
||||||
camera_point = rotation_matrix_transpose_from_quaternion(self.rotation_quaternion)[2]
|
return self
|
||||||
axis = cross(OUT, camera_point)
|
|
||||||
axis = normalize(axis, fall_back=RIGHT)
|
def increment_gamma(self, dgamma):
|
||||||
self.rotate(dphi, axis)
|
self.euler_angles[2] += dgamma
|
||||||
|
return self
|
||||||
|
|
||||||
def scale(self, scale_factor, **kwargs):
|
def scale(self, scale_factor, **kwargs):
|
||||||
# TODO, handle about_point and about_edge?
|
# TODO, handle about_point and about_edge?
|
||||||
|
|
@ -133,7 +141,8 @@ class Camera(object):
|
||||||
"image_mode": "RGBA",
|
"image_mode": "RGBA",
|
||||||
"n_channels": 4,
|
"n_channels": 4,
|
||||||
"pixel_array_dtype": 'uint8',
|
"pixel_array_dtype": 'uint8',
|
||||||
"light_source_position": [-10, 10, 10],
|
"light_source_position": [-10, 10, 10], # TODO, add multiple light sources
|
||||||
|
"apply_depth_test": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, ctx=None, **kwargs):
|
def __init__(self, ctx=None, **kwargs):
|
||||||
|
|
@ -143,7 +152,7 @@ class Camera(object):
|
||||||
self.init_context(ctx)
|
self.init_context(ctx)
|
||||||
self.init_shaders()
|
self.init_shaders()
|
||||||
self.init_textures()
|
self.init_textures()
|
||||||
self.light_source = Point(self.light_source_position)
|
self.init_light_source()
|
||||||
|
|
||||||
def init_frame(self):
|
def init_frame(self):
|
||||||
self.frame = CameraFrame(**self.frame_config)
|
self.frame = CameraFrame(**self.frame_config)
|
||||||
|
|
@ -157,13 +166,19 @@ class Camera(object):
|
||||||
self.fbo = self.get_fbo()
|
self.fbo = self.get_fbo()
|
||||||
self.fbo.use()
|
self.fbo.use()
|
||||||
|
|
||||||
self.ctx.enable(moderngl.BLEND)
|
if self.apply_depth_test:
|
||||||
self.ctx.blend_func = (
|
self.ctx.enable(moderngl.DEPTH_TEST | moderngl.BLEND)
|
||||||
moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA,
|
else:
|
||||||
moderngl.ONE, moderngl.ONE
|
self.ctx.enable(moderngl.BLEND)
|
||||||
)
|
self.ctx.blend_func = (
|
||||||
|
moderngl.SRC_ALPHA, moderngl.ONE_MINUS_SRC_ALPHA,
|
||||||
|
moderngl.ONE, moderngl.ONE
|
||||||
|
)
|
||||||
self.background_fbo = None
|
self.background_fbo = None
|
||||||
|
|
||||||
|
def init_light_source(self):
|
||||||
|
self.light_source = Point(self.light_source_position)
|
||||||
|
|
||||||
# Methods associated with the frame buffer
|
# Methods associated with the frame buffer
|
||||||
def get_fbo(self):
|
def get_fbo(self):
|
||||||
return self.ctx.simple_framebuffer(
|
return self.ctx.simple_framebuffer(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue