diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index b319f812..f5d44e9d 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -211,26 +211,16 @@ class Camera(object): # Rendering def capture(self, *mobjects, **kwargs): - shader_infos = it.chain(*[ - mob.get_shader_info_list() - for mob in mobjects - ]) - # TODO, batching works well when the mobjects are already organized, - # but can we somehow use z-buffering to better effect here? - batches = batch_by_property(shader_infos, shader_info_to_id) - - for info_group, sid in batches: - shader = self.get_shader(sid) - data = np.hstack([info["data"] for info in info_group]) - render_primative = int(info_group[0]["render_primative"]) - self.render_from_shader(shader, data, render_primative) + for mobject in mobjects: + mobject.render(camera=self) # Shaders def init_shaders(self): # Initialize with the null id going to None self.id_to_shader = {"": None} - def get_shader(self, sid): + def get_shader(self, shader_info): + sid = shader_info_to_id(shader_info) if sid not in self.id_to_shader: info = shader_id_to_info(sid) shader = self.ctx.program( @@ -288,10 +278,3 @@ class Camera(object): texture.use(location=tid) self.path_to_texture_id[path] = tid return self.path_to_texture_id[path] - - def render_from_shader(self, shader, data, render_primative): - if data is None or shader is None or len(data) == 0: - return - vbo = self.ctx.buffer(data.tobytes()) - vao = self.ctx.simple_vertex_array(shader, vbo, *data.dtype.names) - vao.render(render_primative) diff --git a/manimlib/mobject/mobject.py b/manimlib/mobject/mobject.py index fa698a07..0479d28b 100644 --- a/manimlib/mobject/mobject.py +++ b/manimlib/mobject/mobject.py @@ -60,6 +60,8 @@ class Mobject(Container): self.name = self.__class__.__name__ self.updaters = [] self.updating_suspended = False + self.vbo = None + self.shader_data_is_locked = False self.reset_points() self.init_points() @@ -1152,6 +1154,14 @@ class Mobject(Container): return new_arr return arr + def lock_shader_data(self): + self.shader_data_is_locked = False + self.saved_shader_info_list = self.get_shader_info_list() + self.shader_data_is_locked = True + + def unlock_shader_data(self): + self.shader_data_is_locked = False + def get_shader_info_list(self): shader_infos = it.chain( [self.get_shader_info()], @@ -1185,6 +1195,32 @@ class Mobject(Container): # Must return a structured numpy array return self.shader_data + def get_vbo(self, ctx, data): + d_bytes = data.tobytes() + if self.vbo is None or self.vbo.size != len(d_bytes): + self.vbo = ctx.buffer(d_bytes) + else: + self.vbo.write(d_bytes) + return self.vbo + + def render(self, camera): + if self.shader_data_is_locked: + info_list = self.saved_shader_info_list + else: + info_list = self.get_shader_info_list() + + for shader_info in info_list: + data = shader_info["data"] + if data is None or len(data) == 0: + continue + shader = camera.get_shader(shader_info) + if shader is None: + continue + render_primative = int(shader_info["render_primative"]) + vbo = self.get_vbo(camera.ctx, data) + vao = camera.ctx.simple_vertex_array(shader, vbo, *data.dtype.names) + vao.render(render_primative) + # Errors def throw_error_if_no_points(self): if self.has_no_points(): diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index c91e35d7..e5f7f43d 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -2,6 +2,7 @@ import inspect import random import warnings import platform +import itertools as it from tqdm import tqdm as ProgressDisplay import numpy as np @@ -389,6 +390,18 @@ class Scene(Container): self.num_plays += 1 return wrapper + def lock_static_mobjects(self, animations): + movers = list(it.chain(*[ + anim.mobject.get_family() + for anim in animations + ])) + for mobject in self.mobjects: + if mobject in movers: + continue + if mobject.get_family_updaters(): + continue + mobject.lock_shader_data() + def begin_animations(self, animations): curr_mobjects = self.get_mobject_family_members() for animation in animations: @@ -420,6 +433,8 @@ class Scene(Container): self.mobjects_from_last_animation = [ anim.mobject for anim in animations ] + for mobject in self.mobjects: + mobject.unlock_shader_data() if self.skip_animations: # TODO, run this call in for each animation? self.update_mobjects(self.get_run_time(animations)) @@ -432,6 +447,7 @@ class Scene(Container): warnings.warn("Called Scene.play with no animations") return animations = self.anims_from_play_args(*args, **kwargs) + self.lock_static_mobjects(animations) self.begin_animations(animations) self.progress_through_animations(animations) self.finish_animations(animations)