mirror of
https://github.com/3b1b/manim.git
synced 2025-07-28 20:43:56 +08:00
Refactor away from treating shader_info as a dictionary, and make it a proper type as ShaderWrapper. This also includes some cleanup in hos Camera renders
This commit is contained in:
@ -12,9 +12,6 @@ from manimlib.mobject.mobject import Point
|
|||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
from manimlib.utils.simple_functions import fdiv
|
from manimlib.utils.simple_functions import fdiv
|
||||||
from manimlib.utils.shaders import shader_info_to_id
|
|
||||||
from manimlib.utils.shaders import shader_info_program_id
|
|
||||||
from manimlib.utils.shaders import shader_info_to_program_code
|
|
||||||
from manimlib.utils.simple_functions import clip
|
from manimlib.utils.simple_functions import clip
|
||||||
from manimlib.utils.space_ops import angle_of_vector
|
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
|
||||||
@ -168,7 +165,7 @@ class Camera(object):
|
|||||||
self.init_textures()
|
self.init_textures()
|
||||||
self.init_light_source()
|
self.init_light_source()
|
||||||
self.refresh_perspective_uniforms()
|
self.refresh_perspective_uniforms()
|
||||||
self.static_mobjects_to_shader_info_list = {}
|
self.static_mobject_to_render_group_list = {}
|
||||||
|
|
||||||
def init_frame(self):
|
def init_frame(self):
|
||||||
self.frame = CameraFrame(**self.frame_config)
|
self.frame = CameraFrame(**self.frame_config)
|
||||||
@ -321,91 +318,92 @@ class Camera(object):
|
|||||||
self.refresh_perspective_uniforms()
|
self.refresh_perspective_uniforms()
|
||||||
for mobject in mobjects:
|
for mobject in mobjects:
|
||||||
try:
|
try:
|
||||||
info_list = self.static_mobjects_to_shader_info_list[id(mobject)]
|
rg_list = self.static_mobject_to_render_group_list[id(mobject)]
|
||||||
|
release_when_done = False
|
||||||
except KeyError:
|
except KeyError:
|
||||||
info_list = mobject.get_shader_info_list()
|
rg_list = map(self.get_render_group, mobject.get_shader_wrapper_list())
|
||||||
|
release_when_done = True
|
||||||
|
|
||||||
for shader_info in info_list:
|
for render_group in rg_list:
|
||||||
self.render(shader_info)
|
self.render(render_group, release_when_done)
|
||||||
|
|
||||||
def render(self, shader_info):
|
def render(self, render_group, release_when_done=True):
|
||||||
cached_buffers = "render_group" in shader_info
|
shader_wrapper = render_group["shader_wrapper"]
|
||||||
if cached_buffers:
|
shader_program = render_group["prog"]
|
||||||
vbo, ibo, vao, shader = shader_info["render_group"]
|
self.set_shader_uniforms(shader_program, shader_wrapper)
|
||||||
else:
|
self.update_depth_test(shader_wrapper)
|
||||||
vbo, ibo, vao, shader = self.get_render_group(shader_info)
|
render_group["vao"].render(int(shader_wrapper.render_primative))
|
||||||
|
if release_when_done:
|
||||||
|
self.release_render_group(render_group)
|
||||||
|
|
||||||
self.set_shader_uniforms(shader, shader_info)
|
def update_depth_test(self, shader_wrapper):
|
||||||
|
if shader_wrapper.depth_test:
|
||||||
if shader_info["depth_test"]:
|
|
||||||
self.ctx.enable(moderngl.DEPTH_TEST)
|
self.ctx.enable(moderngl.DEPTH_TEST)
|
||||||
else:
|
else:
|
||||||
self.ctx.disable(moderngl.DEPTH_TEST)
|
self.ctx.disable(moderngl.DEPTH_TEST)
|
||||||
|
|
||||||
vao.render(int(shader_info["render_primative"]))
|
|
||||||
|
|
||||||
if not cached_buffers:
|
|
||||||
self.release_gl_objects(vbo, ibo, vao)
|
|
||||||
|
|
||||||
def get_render_group(self, shader_info):
|
|
||||||
shader, vert_format = self.get_shader(shader_info)
|
|
||||||
# vbo = self.ctx.buffer(shader_info["vert_data"].tobytes())
|
|
||||||
vbo = self.ctx.buffer(shader_info["vert_data"])
|
|
||||||
|
|
||||||
vert_indices = shader_info["vert_indices"]
|
|
||||||
if vert_indices is None:
|
|
||||||
ibo = None
|
|
||||||
else:
|
|
||||||
ibo = self.ctx.buffer(vert_indices.astype('i4').tobytes())
|
|
||||||
|
|
||||||
vao = self.ctx.vertex_array(
|
|
||||||
program=shader,
|
|
||||||
content=[(vbo, vert_format, *shader_info["attributes"])],
|
|
||||||
index_buffer=ibo,
|
|
||||||
)
|
|
||||||
return (vbo, ibo, vao, shader)
|
|
||||||
|
|
||||||
def set_mobjects_as_static(self, *mobjects):
|
def set_mobjects_as_static(self, *mobjects):
|
||||||
# Create buffer and array objects holding each mobjects shader data
|
# Creates buffer and array objects holding each mobjects shader data
|
||||||
for mob in mobjects:
|
for mob in mobjects:
|
||||||
info_list = mob.get_shader_info_list()
|
self.static_mobject_to_render_group_list[id(mob)] = [
|
||||||
for info in info_list:
|
self.get_render_group(sw)
|
||||||
info["render_group"] = self.get_render_group(info)
|
for sw in mob.get_shader_wrapper_list()
|
||||||
self.static_mobjects_to_shader_info_list[id(mob)] = info_list
|
]
|
||||||
|
|
||||||
def release_static_mobjects(self):
|
def release_static_mobjects(self):
|
||||||
for mob, info_list in self.static_mobjects_to_shader_info_list.items():
|
for rg_list in self.static_mobject_to_render_group_list.values():
|
||||||
for info in info_list:
|
for render_group in rg_list:
|
||||||
self.release_gl_objects(*info["render_group"][:3])
|
self.release_render_group(render_group)
|
||||||
self.static_mobjects_to_shader_info_list = {}
|
self.static_mobject_to_render_group_list = {}
|
||||||
|
|
||||||
def release_gl_objects(self, *objs):
|
def get_render_group(self, shader_wrapper):
|
||||||
for obj in objs:
|
# Data buffers
|
||||||
if obj:
|
vbo = self.ctx.buffer(shader_wrapper.vert_data.tobytes())
|
||||||
obj.release()
|
if shader_wrapper.vert_indices is None:
|
||||||
|
ibo = None
|
||||||
|
else:
|
||||||
|
ibo = self.ctx.buffer(shader_wrapper.vert_indices.astype('i4').tobytes())
|
||||||
|
|
||||||
|
# Program and vertex array
|
||||||
|
shader_program, vert_format = self.get_shader_program(shader_wrapper)
|
||||||
|
vao = self.ctx.vertex_array(
|
||||||
|
program=shader_program,
|
||||||
|
content=[(vbo, vert_format, *shader_wrapper.vert_attributes)],
|
||||||
|
index_buffer=ibo,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"vbo": vbo,
|
||||||
|
"ibo": ibo,
|
||||||
|
"vao": vao,
|
||||||
|
"prog": shader_program,
|
||||||
|
"shader_wrapper": shader_wrapper,
|
||||||
|
}
|
||||||
|
|
||||||
|
def release_render_group(self, render_group):
|
||||||
|
for key in ["vbo", "ibo", "vao"]:
|
||||||
|
if render_group[key] is not None:
|
||||||
|
render_group[key].release()
|
||||||
|
|
||||||
# Shaders
|
# Shaders
|
||||||
def init_shaders(self):
|
def init_shaders(self):
|
||||||
# Initialize with the null id going to None
|
# Initialize with the null id going to None
|
||||||
self.id_to_shader = {"": None}
|
self.id_to_shader_program = {"": None}
|
||||||
|
|
||||||
def get_shader(self, shader_info):
|
def get_shader_program(self, shader_wrapper):
|
||||||
sid = shader_info_program_id(shader_info)
|
sid = shader_wrapper.get_program_id()
|
||||||
if sid not in self.id_to_shader:
|
if sid not in self.id_to_shader_program:
|
||||||
# Create shader program for the first time, then cache
|
# Create shader program for the first time, then cache
|
||||||
# in the id_to_shader dictionary
|
# in the id_to_shader_program dictionary
|
||||||
program = self.ctx.program(**shader_info_to_program_code(shader_info))
|
program = self.ctx.program(**shader_wrapper.get_program_code())
|
||||||
vert_format = moderngl.detect_format(program, shader_info["attributes"])
|
vert_format = moderngl.detect_format(program, shader_wrapper.vert_attributes)
|
||||||
self.id_to_shader[sid] = (program, vert_format)
|
self.id_to_shader_program[sid] = (program, vert_format)
|
||||||
program, vert_format = self.id_to_shader[sid]
|
return self.id_to_shader_program[sid]
|
||||||
# self.set_shader_uniforms(program, shader_info)
|
|
||||||
return program, vert_format
|
|
||||||
|
|
||||||
def set_shader_uniforms(self, shader, shader_info):
|
def set_shader_uniforms(self, shader, shader_wrapper):
|
||||||
for name, path in shader_info["texture_paths"].items():
|
for name, path in shader_wrapper.texture_paths.items():
|
||||||
tid = self.get_texture_id(path)
|
tid = self.get_texture_id(path)
|
||||||
shader[name].value = tid
|
shader[name].value = tid
|
||||||
for name, value in it.chain(shader_info["uniforms"].items(), self.perspective_uniforms.items()):
|
for name, value in it.chain(shader_wrapper.uniforms.items(), self.perspective_uniforms.items()):
|
||||||
try:
|
try:
|
||||||
shader[name].value = value
|
shader[name].value = value
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -22,10 +22,7 @@ from manimlib.utils.simple_functions import get_parameters
|
|||||||
from manimlib.utils.space_ops import angle_of_vector
|
from manimlib.utils.space_ops import angle_of_vector
|
||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
from manimlib.utils.space_ops import rotation_matrix_transpose
|
from manimlib.utils.space_ops import rotation_matrix_transpose
|
||||||
from manimlib.utils.shaders import refresh_shader_info_id
|
from manimlib.utils.shaders import ShaderWrapper
|
||||||
from manimlib.utils.shaders import get_shader_info
|
|
||||||
from manimlib.utils.shaders import shader_info_to_id
|
|
||||||
from manimlib.utils.shaders import is_valid_shader_info
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Explain array_attrs
|
# TODO: Explain array_attrs
|
||||||
@ -217,6 +214,8 @@ class Mobject(Container):
|
|||||||
setattr(copy_mobject, attr, value.copy())
|
setattr(copy_mobject, attr, value.copy())
|
||||||
if isinstance(value, np.ndarray):
|
if isinstance(value, np.ndarray):
|
||||||
setattr(copy_mobject, attr, np.array(value))
|
setattr(copy_mobject, attr, np.array(value))
|
||||||
|
if isinstance(value, ShaderWrapper):
|
||||||
|
setattr(copy_mobject, attr, value.copy())
|
||||||
return copy_mobject
|
return copy_mobject
|
||||||
|
|
||||||
def deepcopy(self):
|
def deepcopy(self):
|
||||||
@ -235,6 +234,7 @@ class Mobject(Container):
|
|||||||
return self.target
|
return self.target
|
||||||
|
|
||||||
# Updating
|
# Updating
|
||||||
|
|
||||||
def init_updaters(self):
|
def init_updaters(self):
|
||||||
self.time_based_updaters = []
|
self.time_based_updaters = []
|
||||||
self.non_time_updaters = []
|
self.non_time_updaters = []
|
||||||
@ -1194,7 +1194,7 @@ class Mobject(Container):
|
|||||||
def wrapper(self):
|
def wrapper(self):
|
||||||
for mob in self.get_family():
|
for mob in self.get_family():
|
||||||
func(mob)
|
func(mob)
|
||||||
mob.refresh_shader_info_template_id()
|
mob.refresh_shader_wrapper_id()
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
@affects_shader_info_id
|
@affects_shader_info_id
|
||||||
@ -1221,8 +1221,8 @@ class Mobject(Container):
|
|||||||
def init_shader_data(self):
|
def init_shader_data(self):
|
||||||
self.shader_data = np.zeros(len(self.points), dtype=self.shader_dtype)
|
self.shader_data = np.zeros(len(self.points), dtype=self.shader_dtype)
|
||||||
self.shader_indices = None
|
self.shader_indices = None
|
||||||
self.shader_info_template = get_shader_info(
|
self.shader_wrapper = ShaderWrapper(
|
||||||
attributes=self.shader_data.dtype.names,
|
vert_data=self.shader_data,
|
||||||
vert_file=self.vert_shader_file,
|
vert_file=self.vert_shader_file,
|
||||||
geom_file=self.geom_shader_file,
|
geom_file=self.geom_shader_file,
|
||||||
frag_file=self.frag_shader_file,
|
frag_file=self.frag_shader_file,
|
||||||
@ -1231,8 +1231,8 @@ class Mobject(Container):
|
|||||||
render_primative=self.render_primative,
|
render_primative=self.render_primative,
|
||||||
)
|
)
|
||||||
|
|
||||||
def refresh_shader_info_template_id(self):
|
def refresh_shader_wrapper_id(self):
|
||||||
refresh_shader_info_id(self.shader_info_template)
|
self.shader_wrapper.refresh_id()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_blank_shader_data_array(self, size, name="shader_data"):
|
def get_blank_shader_data_array(self, size, name="shader_data"):
|
||||||
@ -1245,41 +1245,30 @@ class Mobject(Container):
|
|||||||
return new_arr
|
return new_arr
|
||||||
return arr
|
return arr
|
||||||
|
|
||||||
def get_shader_info_list(self):
|
def get_shader_wrapper(self):
|
||||||
shader_infos = it.chain(
|
self.shader_wrapper.vert_data = self.get_shader_data()
|
||||||
[self.get_shader_info()],
|
self.shader_wrapper.vert_indices = self.get_shader_vert_indices()
|
||||||
*[sm.get_shader_info_list() for sm in self.submobjects]
|
self.shader_wrapper.uniforms = self.get_shader_uniforms()
|
||||||
|
self.shader_wrapper.depth_test = self.depth_test
|
||||||
|
return self.shader_wrapper
|
||||||
|
|
||||||
|
def get_shader_wrapper_list(self):
|
||||||
|
shader_wrappers = it.chain(
|
||||||
|
[self.get_shader_wrapper()],
|
||||||
|
*[sm.get_shader_wrapper_list() for sm in self.submobjects]
|
||||||
)
|
)
|
||||||
batches = batch_by_property(shader_infos, shader_info_to_id)
|
batches = batch_by_property(shader_wrappers, lambda sw: sw.get_id())
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for info_group, sid in batches:
|
for wrapper_group, sid in batches:
|
||||||
combined_info = info_group[0]
|
shader_wrapper = wrapper_group[0]
|
||||||
if not is_valid_shader_info(combined_info):
|
if not shader_wrapper.is_valid():
|
||||||
continue
|
continue
|
||||||
data_list = []
|
shader_wrapper.combine_with(*wrapper_group[1:])
|
||||||
indices_list = []
|
if len(shader_wrapper.vert_data) > 0:
|
||||||
num_verts = 0
|
result.append(shader_wrapper)
|
||||||
for info in info_group:
|
|
||||||
data_list.append(info["vert_data"])
|
|
||||||
if info["vert_indices"] is not None:
|
|
||||||
indices_list.append(info["vert_indices"] + num_verts)
|
|
||||||
num_verts += len(info["vert_data"])
|
|
||||||
# Combine lists
|
|
||||||
combined_info["vert_data"] = np.hstack(data_list)
|
|
||||||
if combined_info["vert_indices"] is not None:
|
|
||||||
combined_info["vert_indices"] = np.hstack(indices_list)
|
|
||||||
if len(combined_info["vert_indices"]) > 0:
|
|
||||||
result.append(combined_info)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_shader_info(self):
|
|
||||||
shader_info = dict(self.shader_info_template)
|
|
||||||
shader_info["vert_data"] = self.get_shader_data()
|
|
||||||
shader_info["vert_indices"] = self.get_shader_vert_indices()
|
|
||||||
shader_info["uniforms"] = self.get_shader_uniforms()
|
|
||||||
return shader_info
|
|
||||||
|
|
||||||
def get_shader_uniforms(self):
|
def get_shader_uniforms(self):
|
||||||
return {
|
return {
|
||||||
"is_fixed_in_frame": float(self.is_fixed_in_frame),
|
"is_fixed_in_frame": float(self.is_fixed_in_frame),
|
||||||
|
@ -30,24 +30,14 @@ class ImageMobject(Mobject):
|
|||||||
|
|
||||||
def init_points(self):
|
def init_points(self):
|
||||||
self.points = np.array([UL, DL, UR, DR])
|
self.points = np.array([UL, DL, UR, DR])
|
||||||
|
self.im_coords = np.array([(0, 0), (0, 1), (1, 0), (1, 1)])
|
||||||
size = self.image.size
|
size = self.image.size
|
||||||
self.set_width(2 * size[0] / size[1], stretch=True)
|
self.set_width(2 * size[0] / size[1], stretch=True)
|
||||||
self.set_height(self.height)
|
self.set_height(self.height)
|
||||||
|
|
||||||
self.im_coords = np.array(
|
|
||||||
[(0, 0), (0, 1), (1, 0), (1, 1)]
|
|
||||||
)
|
|
||||||
|
|
||||||
def init_colors(self):
|
def init_colors(self):
|
||||||
self.set_opacity(self.opacity)
|
self.set_opacity(self.opacity)
|
||||||
|
|
||||||
def get_shader_data(self):
|
|
||||||
data = self.get_blank_shader_data_array(len(self.points))
|
|
||||||
data["point"] = self.points
|
|
||||||
data["im_coords"] = self.im_coords
|
|
||||||
data["opacity"] = self.opacity
|
|
||||||
return data
|
|
||||||
|
|
||||||
def set_opacity(self, alpha, family=True):
|
def set_opacity(self, alpha, family=True):
|
||||||
opacity = listify(alpha)
|
opacity = listify(alpha)
|
||||||
diff = 4 - len(opacity)
|
diff = 4 - len(opacity)
|
||||||
@ -67,3 +57,10 @@ class ImageMobject(Mobject):
|
|||||||
self.opacity = interpolate(
|
self.opacity = interpolate(
|
||||||
mobject1.opacity, mobject2.opacity, alpha
|
mobject1.opacity, mobject2.opacity, alpha
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_shader_data(self):
|
||||||
|
data = self.get_blank_shader_data_array(len(self.points))
|
||||||
|
data["point"] = self.points
|
||||||
|
data["im_coords"] = self.im_coords
|
||||||
|
data["opacity"] = self.opacity
|
||||||
|
return data
|
||||||
|
@ -27,8 +27,7 @@ from manimlib.utils.space_ops import earclip_triangulation
|
|||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
from manimlib.utils.space_ops import get_unit_normal
|
from manimlib.utils.space_ops import get_unit_normal
|
||||||
from manimlib.utils.space_ops import z_to_vector
|
from manimlib.utils.space_ops import z_to_vector
|
||||||
from manimlib.utils.shaders import refresh_shader_info_id
|
from manimlib.utils.shaders import ShaderWrapper
|
||||||
from manimlib.utils.shaders import get_shader_info
|
|
||||||
|
|
||||||
|
|
||||||
class VMobject(Mobject):
|
class VMobject(Mobject):
|
||||||
@ -882,70 +881,67 @@ class VMobject(Mobject):
|
|||||||
def init_shader_data(self):
|
def init_shader_data(self):
|
||||||
self.fill_data = np.zeros(len(self.points), dtype=self.fill_dtype)
|
self.fill_data = np.zeros(len(self.points), dtype=self.fill_dtype)
|
||||||
self.stroke_data = np.zeros(len(self.points), dtype=self.stroke_dtype)
|
self.stroke_data = np.zeros(len(self.points), dtype=self.stroke_dtype)
|
||||||
self.fill_shader_info_template = get_shader_info(
|
self.fill_shader_wrapper = ShaderWrapper(
|
||||||
attributes=self.fill_data.dtype.names,
|
vert_data=self.fill_data,
|
||||||
|
vert_indices=np.zeros(0, dtype='i4'),
|
||||||
vert_file=self.fill_vert_shader_file,
|
vert_file=self.fill_vert_shader_file,
|
||||||
geom_file=self.fill_geom_shader_file,
|
geom_file=self.fill_geom_shader_file,
|
||||||
frag_file=self.fill_frag_shader_file,
|
frag_file=self.fill_frag_shader_file,
|
||||||
depth_test=self.depth_test,
|
|
||||||
render_primative=self.render_primative,
|
render_primative=self.render_primative,
|
||||||
)
|
)
|
||||||
self.stroke_shader_info_template = get_shader_info(
|
self.stroke_shader_wrapper = ShaderWrapper(
|
||||||
attributes=self.stroke_data.dtype.names,
|
vert_data=self.stroke_data,
|
||||||
vert_file=self.stroke_vert_shader_file,
|
vert_file=self.stroke_vert_shader_file,
|
||||||
geom_file=self.stroke_geom_shader_file,
|
geom_file=self.stroke_geom_shader_file,
|
||||||
frag_file=self.stroke_frag_shader_file,
|
frag_file=self.stroke_frag_shader_file,
|
||||||
depth_test=self.depth_test,
|
|
||||||
render_primative=self.render_primative,
|
render_primative=self.render_primative,
|
||||||
)
|
)
|
||||||
|
|
||||||
def refresh_shader_info_template_id(self):
|
def refresh_shader_wrapper_id(self):
|
||||||
for template in [self.fill_shader_info_template, self.stroke_shader_info_template]:
|
for wrapper in [self.fill_shader_wrapper, self.stroke_shader_wrapper]:
|
||||||
refresh_shader_info_id(template)
|
wrapper.refresh_id()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_shader_info_list(self):
|
def get_fill_shader_wrapper(self):
|
||||||
fill_info = dict(self.fill_shader_info_template)
|
self.fill_shader_wrapper.vert_data = self.get_fill_shader_data()
|
||||||
stroke_info = dict(self.stroke_shader_info_template)
|
self.fill_shader_wrapper.vert_indices = self.get_fill_shader_vert_indices()
|
||||||
fill_info["uniforms"] = self.get_shader_uniforms()
|
self.fill_shader_wrapper.uniforms = self.get_shader_uniforms()
|
||||||
stroke_info["uniforms"] = self.get_stroke_uniforms()
|
self.fill_shader_wrapper.depth_test = self.depth_test
|
||||||
for info in fill_info, stroke_info:
|
return self.fill_shader_wrapper
|
||||||
info["depth_test"] = self.depth_test
|
|
||||||
|
|
||||||
|
def get_stroke_shader_wrapper(self):
|
||||||
|
self.stroke_shader_wrapper.vert_data = self.get_stroke_shader_data()
|
||||||
|
self.stroke_shader_wrapper.uniforms = self.get_stroke_uniforms()
|
||||||
|
self.stroke_shader_wrapper.depth_test = self.depth_test
|
||||||
|
return self.stroke_shader_wrapper
|
||||||
|
|
||||||
|
def get_shader_wrapper_list(self):
|
||||||
# Build up data lists
|
# Build up data lists
|
||||||
back_stroke_data = []
|
fill_shader_wrappers = []
|
||||||
stroke_data = []
|
stroke_shader_wrappers = []
|
||||||
fill_data = []
|
back_stroke_shader_wrappers = []
|
||||||
fill_vert_indices = []
|
|
||||||
num_fill_verts = 0 # Number of fill verts
|
|
||||||
for submob in self.family_members_with_points():
|
for submob in self.family_members_with_points():
|
||||||
if submob.has_fill():
|
if submob.has_fill():
|
||||||
data = submob.get_fill_shader_data()
|
fill_shader_wrappers.append(submob.get_fill_shader_wrapper())
|
||||||
indices = submob.get_fill_shader_vert_indices() + num_fill_verts
|
|
||||||
num_fill_verts += len(data)
|
|
||||||
|
|
||||||
fill_data.append(data)
|
|
||||||
fill_vert_indices.append(indices)
|
|
||||||
if submob.has_stroke():
|
if submob.has_stroke():
|
||||||
data = submob.get_stroke_shader_data()
|
ssw = submob.get_stroke_shader_wrapper()
|
||||||
if submob.draw_stroke_behind_fill:
|
if submob.draw_stroke_behind_fill:
|
||||||
back_stroke_data.append(data)
|
back_stroke_shader_wrappers.append(ssw)
|
||||||
else:
|
else:
|
||||||
stroke_data.append(data)
|
stroke_shader_wrappers.append(ssw)
|
||||||
|
|
||||||
# Combine data lists
|
# Combine data lists
|
||||||
|
wrapper_lists = [
|
||||||
|
back_stroke_shader_wrappers,
|
||||||
|
fill_shader_wrappers,
|
||||||
|
stroke_shader_wrappers
|
||||||
|
]
|
||||||
result = []
|
result = []
|
||||||
if back_stroke_data:
|
for wlist in wrapper_lists:
|
||||||
back_stroke_info = dict(stroke_info) # Copy
|
if wlist:
|
||||||
back_stroke_info["vert_data"] = np.hstack(back_stroke_data)
|
wrapper = wlist[0]
|
||||||
result.append(back_stroke_info)
|
wrapper.combine_with(*wlist[1:])
|
||||||
if fill_data:
|
result.append(wrapper)
|
||||||
fill_info["vert_data"] = np.hstack(fill_data)
|
|
||||||
fill_info["vert_indices"] = np.hstack(fill_vert_indices)
|
|
||||||
result.append(fill_info)
|
|
||||||
if stroke_data:
|
|
||||||
stroke_info["vert_data"] = np.hstack(stroke_data)
|
|
||||||
result.append(stroke_info)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_stroke_uniforms(self):
|
def get_stroke_uniforms(self):
|
||||||
@ -1013,7 +1009,7 @@ class VMobject(Mobject):
|
|||||||
return self.saved_triangulation
|
return self.saved_triangulation
|
||||||
|
|
||||||
if len(self.points) <= 1:
|
if len(self.points) <= 1:
|
||||||
return []
|
return np.zeros(0, dtype='i4')
|
||||||
|
|
||||||
# Rotate points such that unit normal vector is OUT
|
# Rotate points such that unit normal vector is OUT
|
||||||
# TODO, 99% of the time this does nothing. Do a check for that?
|
# TODO, 99% of the time this does nothing. Do a check for that?
|
||||||
|
@ -2,6 +2,8 @@ import os
|
|||||||
import warnings
|
import warnings
|
||||||
import re
|
import re
|
||||||
import moderngl
|
import moderngl
|
||||||
|
import numpy as np
|
||||||
|
import copy
|
||||||
|
|
||||||
from manimlib.constants import SHADER_DIR
|
from manimlib.constants import SHADER_DIR
|
||||||
|
|
||||||
@ -12,112 +14,98 @@ from manimlib.constants import SHADER_DIR
|
|||||||
# to that shader
|
# to that shader
|
||||||
|
|
||||||
|
|
||||||
# TODO, this should all be treated as an object
|
class ShaderWrapper(object):
|
||||||
# This object a shader program instead of the vert,
|
def __init__(self,
|
||||||
# geom and frag file names, and it should cache those
|
vert_data=None,
|
||||||
# programs in the way currently handled by Camera
|
|
||||||
# It should replace the Camera.get_shader method with
|
|
||||||
# its own get_shader_program method, which will take
|
|
||||||
# in the camera's perspective_uniforms.
|
|
||||||
|
|
||||||
|
|
||||||
SHADER_INFO_KEYS = [
|
|
||||||
# Vertex data for the shader (as structured array)
|
|
||||||
"vert_data",
|
|
||||||
# Index data (if applicable) for the shader
|
|
||||||
"index_data",
|
|
||||||
# List of variable names corresponding to inputs of vertex shader
|
|
||||||
"attributes",
|
|
||||||
# Filename of vetex shader
|
|
||||||
"vert",
|
|
||||||
# Filename of geometry shader, if there is one
|
|
||||||
"geom",
|
|
||||||
# Filename of fragment shader
|
|
||||||
"frag",
|
|
||||||
# A dictionary mapping names of uniform variables
|
|
||||||
"uniforms",
|
|
||||||
# A dictionary mapping names (as they show up in)
|
|
||||||
# the shader to filepaths for textures.
|
|
||||||
"texture_paths",
|
|
||||||
# Whether or not to apply depth test
|
|
||||||
"depth_test",
|
|
||||||
# E.g. moderngl.TRIANGLE_STRIP
|
|
||||||
"render_primative",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Exclude data
|
|
||||||
SHADER_KEYS_FOR_ID = SHADER_INFO_KEYS[3:]
|
|
||||||
|
|
||||||
|
|
||||||
def get_shader_info(vert_data=None,
|
|
||||||
vert_indices=None,
|
vert_indices=None,
|
||||||
attributes=None,
|
|
||||||
vert_file=None,
|
vert_file=None,
|
||||||
geom_file=None,
|
geom_file=None,
|
||||||
frag_file=None,
|
frag_file=None,
|
||||||
uniforms=None,
|
uniforms=None, # A dictionary mapping names of uniform variables
|
||||||
texture_paths=None,
|
texture_paths=None, # A dictionary mapping names to filepaths for textures.
|
||||||
depth_test=False,
|
depth_test=False,
|
||||||
render_primative=moderngl.TRIANGLE_STRIP,
|
render_primative=moderngl.TRIANGLE_STRIP,
|
||||||
):
|
):
|
||||||
result = {
|
self.vert_data = vert_data
|
||||||
"vert_data": vert_data,
|
self.vert_indices = vert_indices
|
||||||
"vert_indices": vert_indices,
|
self.vert_attributes = vert_data.dtype.names
|
||||||
"attributes": attributes,
|
self.vert_file = vert_file
|
||||||
"vert": vert_file,
|
self.geom_file = geom_file
|
||||||
"geom": geom_file,
|
self.frag_file = frag_file
|
||||||
"frag": frag_file,
|
self.uniforms = uniforms or dict()
|
||||||
"uniforms": uniforms or dict(),
|
self.texture_paths = texture_paths or dict()
|
||||||
"texture_paths": texture_paths or dict(),
|
self.depth_test = depth_test
|
||||||
"depth_test": depth_test,
|
self.render_primative = str(render_primative)
|
||||||
"render_primative": str(render_primative),
|
self.id = self.create_id()
|
||||||
}
|
self.program_id = self.create_program_id()
|
||||||
result["id"] = create_shader_info_id(result)
|
|
||||||
result["prog_id"] = create_shader_info_program_id(result)
|
def copy(self):
|
||||||
|
result = copy.copy(self)
|
||||||
|
result.vert_data = np.array(self.vert_data)
|
||||||
|
if result.vert_indices is not None:
|
||||||
|
result.vert_indices = np.array(self.vert_indices)
|
||||||
|
if self.uniforms:
|
||||||
|
result.uniforms = dict(self.uniforms)
|
||||||
|
if self.texture_paths:
|
||||||
|
result.texture_paths = dict(self.texture_paths)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
def is_valid_shader_info(shader_info):
|
|
||||||
vert_data = shader_info["vert_data"]
|
|
||||||
return all([
|
return all([
|
||||||
vert_data is not None,
|
self.vert_data is not None,
|
||||||
shader_info["vert"],
|
self.vert_file,
|
||||||
shader_info["frag"],
|
self.frag_file,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.id
|
||||||
|
|
||||||
def shader_info_to_id(shader_info):
|
def get_program_id(self):
|
||||||
return shader_info["id"]
|
return self.program_id
|
||||||
|
|
||||||
|
def create_id(self):
|
||||||
def shader_info_program_id(shader_info):
|
|
||||||
return shader_info["prog_id"]
|
|
||||||
|
|
||||||
|
|
||||||
def create_shader_info_id(shader_info):
|
|
||||||
# A unique id for a shader
|
# A unique id for a shader
|
||||||
return "|".join([str(shader_info[key]) for key in SHADER_KEYS_FOR_ID])
|
return "|".join(map(str, [
|
||||||
|
self.vert_file,
|
||||||
|
self.geom_file,
|
||||||
|
self.frag_file,
|
||||||
|
self.uniforms,
|
||||||
|
self.texture_paths,
|
||||||
|
self.depth_test,
|
||||||
|
self.render_primative,
|
||||||
|
]))
|
||||||
|
|
||||||
|
def refresh_id(self):
|
||||||
|
self.id = self.create_id()
|
||||||
|
|
||||||
def refresh_shader_info_id(shader_info):
|
def create_program_id(self):
|
||||||
shader_info["id"] = create_shader_info_id(shader_info)
|
return "|".join(map(str, [self.vert_file, self.geom_file, self.frag_file]))
|
||||||
|
|
||||||
|
def get_program_code(self):
|
||||||
def create_shader_info_program_id(shader_info):
|
|
||||||
return "|".join([str(shader_info[key]) for key in ["vert", "geom", "frag"]])
|
|
||||||
|
|
||||||
|
|
||||||
def same_shader_type(info1, info2):
|
|
||||||
return info1["id"] == info2["id"]
|
|
||||||
|
|
||||||
|
|
||||||
def shader_info_to_program_code(shader_info):
|
|
||||||
return {
|
return {
|
||||||
"vertex_shader": get_shader_code_from_file(shader_info["vert"]),
|
"vertex_shader": get_shader_code_from_file(self.vert_file),
|
||||||
"geometry_shader": get_shader_code_from_file(shader_info["geom"]),
|
"geometry_shader": get_shader_code_from_file(self.geom_file),
|
||||||
"fragment_shader": get_shader_code_from_file(shader_info["frag"]),
|
"fragment_shader": get_shader_code_from_file(self.frag_file),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def combine_with(self, *shader_wrappers):
|
||||||
|
# Assume they are of the same type
|
||||||
|
if len(shader_wrappers) == 0:
|
||||||
|
return
|
||||||
|
if self.vert_indices is not None:
|
||||||
|
num_verts = len(self.vert_data)
|
||||||
|
indices_list = [self.vert_indices]
|
||||||
|
data_list = [self.vert_data]
|
||||||
|
for sw in shader_wrappers:
|
||||||
|
indices_list.append(sw.vert_indices + num_verts)
|
||||||
|
data_list.append(sw.vert_data)
|
||||||
|
num_verts += len(sw.vert_data)
|
||||||
|
self.vert_indices = np.hstack(indices_list)
|
||||||
|
self.vert_data = np.hstack(data_list)
|
||||||
|
else:
|
||||||
|
self.vert_data = np.hstack([self.vert_data, *[sw.vert_data for sw in shader_wrappers]])
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
def get_shader_code_from_file(filename):
|
def get_shader_code_from_file(filename):
|
||||||
if not filename:
|
if not filename:
|
||||||
|
Reference in New Issue
Block a user