def __init__(self, width, height): """:param width & height: size of the main window in pixels. """ pyglet.window.Window.__init__( self, width, height, caption="Polyhedra Mirrors", resizable=True, visible=False, vsync=False, ) self._start_time = time.perf_counter() self.shader = Shader([os.path.join(GLSL_DIR, "default.vert")], [ os.path.join(GLSL_DIR, "common.frag"), os.path.join(GLSL_DIR, "data.frag"), os.path.join(GLSL_DIR, "main.frag") ]) wood = create_image_texture(WOOD_IMAGE) cubemap = create_cubemap_texture(CUBEMAP_IMAGES) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(gl.GL_TEXTURE_CUBE_MAP, cubemap) gl.glActiveTexture(gl.GL_TEXTURE1) gl.glBindTexture(gl.GL_TEXTURE_2D, wood) with self.shader: self.shader.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shader.uniformf("iResolution", self.width, self.height, 0.0) self.shader.uniformf("iTime", 0.0) self.shader.uniformi("iChannel0", 0) self.shader.uniformi("iChannel1", 1)
def __init__(self, width, height, zoom): """ :param width & height: size of the main window in pixels. :param zoom: zoom factor of the scene. """ pyglet.window.Window.__init__( self, width, height, caption="Wang-Tile", resizable=True, visible=False, vsync=False, ) self._start_time = time.perf_counter() self.shader = Shader(["./glsl/default.vert"], ["./glsl/wang.frag"]) cubemap = create_cubemap_texture(CUBEMAP_IMAGES) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(gl.GL_TEXTURE_CUBE_MAP, cubemap) with self.shader: self.shader.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shader.uniformf("iResolution", self.width, self.height, 0.0) self.shader.uniformf("iTime", 0.0) self.shader.uniformf("zoom", zoom) self.shader.uniformi("iChannel0", 0)
class WangTile(pyglet.window.Window): """ Keyboard control: 1. press Enter to save screenshots. 2. press Esc to exit. """ def __init__(self, width, height, zoom): """ :param width & height: size of the main window in pixels. :param zoom: zoom factor of the scene. """ pyglet.window.Window.__init__( self, width, height, caption="Wang-Tile", resizable=True, visible=False, vsync=False, ) self._start_time = time.perf_counter() self.shader = Shader(["./glsl/default.vert"], ["./glsl/wang.frag"]) cubemap = create_cubemap_texture(CUBEMAP_IMAGES) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(gl.GL_TEXTURE_CUBE_MAP, cubemap) with self.shader: self.shader.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shader.uniformf("iResolution", self.width, self.height, 0.0) self.shader.uniformf("iTime", 0.0) self.shader.uniformf("zoom", zoom) self.shader.uniformi("iChannel0", 0) def on_draw(self): gl.glClearColor(0, 0, 0, 0) self.clear() gl.glViewport(0, 0, self.width, self.height) with self.shader: self.shader.uniformf("iTime", time.perf_counter() - self._start_time) gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) def on_key_press(self, symbol, modifiers): if symbol == key.ENTER: self.save_screenshot() if symbol == key.ESCAPE: pyglet.app.exit() def save_screenshot(self): buff = pyglet.image.get_buffer_manager().get_color_buffer() buff.save("wangtile.png") def run(self, fps=None): self.set_visible(True) if fps is None: pyglet.clock.schedule(lambda dt: None) else: pyglet.clock.schedule_interval(lambda dt: None, 1.0 / fps) pyglet.app.run()
class PolyhedraMirror(pyglet.window.Window): """ Keyboard control: 1. press Enter to save screenshots. 2. press Esc to exit. """ def __init__(self, width, height): """:param width & height: size of the main window in pixels. """ pyglet.window.Window.__init__( self, width, height, caption="Polyhedra Mirrors", resizable=True, visible=False, vsync=False, ) self._start_time = time.perf_counter() self.shader = Shader([os.path.join(GLSL_DIR, "default.vert")], [ os.path.join(GLSL_DIR, "common.frag"), os.path.join(GLSL_DIR, "data.frag"), os.path.join(GLSL_DIR, "main.frag") ]) wood = create_image_texture(WOOD_IMAGE) cubemap = create_cubemap_texture(CUBEMAP_IMAGES) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(gl.GL_TEXTURE_CUBE_MAP, cubemap) gl.glActiveTexture(gl.GL_TEXTURE1) gl.glBindTexture(gl.GL_TEXTURE_2D, wood) with self.shader: self.shader.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shader.uniformf("iResolution", self.width, self.height, 0.0) self.shader.uniformf("iTime", 0.0) self.shader.uniformi("iChannel0", 0) self.shader.uniformi("iChannel1", 1) def on_draw(self): gl.glClearColor(0, 0, 0, 0) self.clear() gl.glViewport(0, 0, self.width, self.height) with self.shader: self.shader.uniformf("iTime", time.perf_counter() - self._start_time) gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) def on_key_press(self, symbol, modifiers): if symbol == key.ENTER: self.save_screenshot() if symbol == key.ESCAPE: pyglet.app.exit() def on_mouse_press(self, x, y, button, modifiers): if button & pyglet.window.mouse.LEFT: with self.shader: self.shader.uniformf("iMouse", x, y, x, y) def on_mouse_release(self, x, y, button, modifiers): """Don't forget reset 'iMouse' when mouse is released. """ with self.shader: self.shader.uniformf("iMouse", 0, 0, 0, 0) def on_mouse_drag(self, x, y, dx, dy, button, modifiers): if button & pyglet.window.mouse.LEFT: with self.shader: x += dx y += dy x = max(min(x, self.width), 0) y = max(min(y, self.height), 0) self.shader.uniformf("iMouse", x, y, x, y) def save_screenshot(self): buff = pyglet.image.get_buffer_manager().get_color_buffer() buff.save("screenshot.png") def run(self, fps=None): self.set_visible(True) if fps is None: pyglet.clock.schedule(lambda dt: None) else: pyglet.clock.schedule_interval(lambda dt: None, 1.0 / fps) pyglet.app.run()
def __init__(self, width, height): """ :param width and height: size of the window in pixels. """ pyglet.window.Window.__init__( self, width, height, caption="Wythoff Explorer", resizable=True, visible=False, vsync=False, ) self._start_time = time.perf_counter() self._last = self._now = self._start_time self._frame_count = 0 # count number of frames rendered so far self.shaderA = Shader( ["./glsl/default.vert"], [ "./glsl/wythoff_hyperbolic/common.frag", "./glsl/wythoff_hyperbolic/BufferA.frag" ], ) self.shaderB = Shader( ["./glsl/default.vert"], [ "./glsl/wythoff_hyperbolic/common.frag", "./glsl/wythoff_hyperbolic/main.frag" ], ) self.font_texture = create_image_texture(FONT_TEXTURE) self.noise_texture = create_image_texture(NOISE_TEXTURE) self.iChannel0 = pyglet.image.Texture.create_for_size( gl.GL_TEXTURE_2D, width, height, gl.GL_RGBA32F_ARB ) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(self.iChannel0.target, self.iChannel0.id) gl.glActiveTexture(gl.GL_TEXTURE1) gl.glBindTexture(gl.GL_TEXTURE_2D, self.font_texture) gl.glActiveTexture(gl.GL_TEXTURE2) gl.glBindTexture(gl.GL_TEXTURE_2D, self.noise_texture) with FrameBuffer() as self.bufferA: self.bufferA.attach_texture(self.iChannel0) # initialize the shaders with self.shaderA: self.shaderA.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shaderA.uniformf("iResolution", width, height, 0.0) self.shaderA.uniformf("iTime", 0.0) self.shaderA.uniformf("iMouse", 0.0, 0.0, 0.0, 0.0) self.shaderA.uniformi("iChannel0", 0) self.shaderA.uniformi("iChannel1", 1) self.shaderA.uniformi("iChannel2", 2) self.shaderA.uniformf("iDate", *get_idate()) self.shaderA.uniformf("iTimeDelta", 0) with self.shaderB: self.shaderB.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shaderB.uniformf("iResolution", width, height, 0.0) self.shaderB.uniformf("iTime", 0.0) self.shaderB.uniformf("iMouse", 0.0, 0.0, 0.0, 0.0) self.shaderB.uniformi("iChannel0", 0) self.shaderB.uniformi("iChannel1", 1) self.shaderB.uniformi("iChannel2", 2) self.shaderB.uniformf("iDate", *get_idate()) self.shaderA.uniformf("iTimeDelta", 0)
class Wythoff(pyglet.window.Window): def __init__(self, width, height): """ :param width and height: size of the window in pixels. """ pyglet.window.Window.__init__( self, width, height, caption="Wythoff Explorer", resizable=True, visible=False, vsync=False, ) self._start_time = time.perf_counter() self._last = self._now = self._start_time self._frame_count = 0 # count number of frames rendered so far self.shaderA = Shader( ["./glsl/default.vert"], [ "./glsl/wythoff_hyperbolic/common.frag", "./glsl/wythoff_hyperbolic/BufferA.frag" ], ) self.shaderB = Shader( ["./glsl/default.vert"], [ "./glsl/wythoff_hyperbolic/common.frag", "./glsl/wythoff_hyperbolic/main.frag" ], ) self.font_texture = create_image_texture(FONT_TEXTURE) self.noise_texture = create_image_texture(NOISE_TEXTURE) self.iChannel0 = pyglet.image.Texture.create_for_size( gl.GL_TEXTURE_2D, width, height, gl.GL_RGBA32F_ARB ) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(self.iChannel0.target, self.iChannel0.id) gl.glActiveTexture(gl.GL_TEXTURE1) gl.glBindTexture(gl.GL_TEXTURE_2D, self.font_texture) gl.glActiveTexture(gl.GL_TEXTURE2) gl.glBindTexture(gl.GL_TEXTURE_2D, self.noise_texture) with FrameBuffer() as self.bufferA: self.bufferA.attach_texture(self.iChannel0) # initialize the shaders with self.shaderA: self.shaderA.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shaderA.uniformf("iResolution", width, height, 0.0) self.shaderA.uniformf("iTime", 0.0) self.shaderA.uniformf("iMouse", 0.0, 0.0, 0.0, 0.0) self.shaderA.uniformi("iChannel0", 0) self.shaderA.uniformi("iChannel1", 1) self.shaderA.uniformi("iChannel2", 2) self.shaderA.uniformf("iDate", *get_idate()) self.shaderA.uniformf("iTimeDelta", 0) with self.shaderB: self.shaderB.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shaderB.uniformf("iResolution", width, height, 0.0) self.shaderB.uniformf("iTime", 0.0) self.shaderB.uniformf("iMouse", 0.0, 0.0, 0.0, 0.0) self.shaderB.uniformi("iChannel0", 0) self.shaderB.uniformi("iChannel1", 1) self.shaderB.uniformi("iChannel2", 2) self.shaderB.uniformf("iDate", *get_idate()) self.shaderA.uniformf("iTimeDelta", 0) def on_draw(self): gl.glClearColor(0, 0, 0, 0) self.clear() gl.glViewport(0, 0, self.width, self.height) self._last = self._now self._now = time.perf_counter() itime = self._now - self._start_time idate = get_idate() delta = self._now - self._last with self.bufferA: with self.shaderA: self.shaderA.uniformf("iTime", itime) self.shaderA.uniformf("iDate", *idate) self.shaderA.uniformf("iTimeDelta", delta) gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) with self.shaderB: self.shaderB.uniformf("iTime", itime) self.shaderB.uniformf("iDate", *idate) self.shaderB.uniformf("iTimeDelta", delta) gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) self._frame_count += 1 def on_key_press(self, symbol, modifiers): """Keyboard interface. """ if symbol == key.ENTER: self.save_screenshot() if symbol == key.ESCAPE: pyglet.app.exit() def on_mouse_press(self, x, y, button, modifiers): if button & pyglet.window.mouse.LEFT: with self.shaderA: self.shaderA.uniformf("iMouse", x, y, x, y) def on_mouse_release(self, x, y, button, modifiers): """Don't forget reset 'iMouse' when mouse is released. """ with self.shaderA: self.shaderA.uniformf("iMouse", 0, 0, 0, 0) def on_mouse_drag(self, x, y, dx, dy, button, modifiers): if button & pyglet.window.mouse.LEFT: with self.shaderA: x += dx y += dy x = max(min(x, self.width), 0) y = max(min(y, self.height), 0) self.shaderA.uniformf("iMouse", x, y, x, y) def save_screenshot(self): image_buffer = pyglet.image.get_buffer_manager().get_color_buffer() image_buffer.save("screenshoot.png") def run(self, fps=None): self.set_visible(True) if fps is None: pyglet.clock.schedule(lambda dt: None) else: pyglet.clock.schedule_interval(lambda dt: None, 1.0 / fps) pyglet.app.run()
class GrayScott(pyglet.window.Window): """ ---------------------------------------------------------- | This simulation uses mouse and keyboard to control the | | patterns and colors. At any time you may click or drag | | your mouse to draw on the screen. | | | | Keyboard control: | | 1. press "space" to clear the window to blank. | | 2. press "p" to change to a random palette. | | 3. press "s" to change to another species. | | 4. press "Ctrl + s" to save current config. | | 5. press "Ctrl + o" to load a config from the file. | | 6. press "Enter" to take screenshots. | | 7. press "Ctrl + v" to start saving the animation to | | the video and press "Ctrl + v" again to stop. | | 8. press "Esc" to exit. | ---------------------------------------------------------- """ def __init__( self, width, height, scale=1.5, conf=1, mask=None, flip=False, video=False, sample_rate=None, video_rate=None, ): """ Parameters ---------- :width & height: size of the window in pixels. :scale: scaling factor of the texture. :conf: line number of the config in the file (`config.txt`). :mask: a user-specified image that is used to control the growth of the pattern. :flip: flip the white/black pixels in the mask image, only meaningful if there is a mask image. :video: whether the video is turned on or off. :sample_rate: sample a frame from the animation every these frames. :video_rate: frames per second of the video. """ pyglet.window.Window.__init__( self, width, height, caption="GrayScott Simulation", resizable=True, visible=False, vsync=False, ) # use two shaders, one for doing the computations and one for rendering to the screen. self.reaction_shader = Shader(["./glsl/default.vert"], ["./glsl/grayscott/compute.frag"]) self.render_shader = Shader(["./glsl/default.vert"], ["./glsl/grayscott/render.frag"]) self.tex_width, self.tex_height = int(width / scale), int(height / scale) try: self.species, self.palette = self.load_config(conf) print("> Current species: " + self.species + "\n") except: conf = "unstable: #00000000 #00FF0033 #FFFF0035 #FF000066 #FFFFFF99" self.species, self.palette = parse(conf) print("> Failed to load the config, using the default one.\n") self.species_list = list(ALL_SPECIES.keys()) # create the uv_texture uv_grid = np.zeros((self.tex_height, self.tex_width, 4), dtype=np.float32) uv_grid[:, :, 0] = 1.0 rand_rows = np.random.choice(range(self.tex_height), 5) rand_cols = np.random.choice(range(self.tex_width), 5) uv_grid[rand_rows, rand_cols, 1] = 1.0 self.uv_texture = create_texture_from_ndarray(uv_grid) # create the mask_texture mask_grid = np.ones_like(uv_grid) if mask is not None: img = (Image.open(mask).convert("L").resize( (self.tex_width, self.tex_height))) img = (np.asarray(img) / 255.0).astype(np.float32) if flip: img = 1.0 - img mask_grid[:, :, 0] = img[::-1] self.mask_texture = create_texture_from_ndarray(mask_grid) # bind the two textures gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(self.uv_texture.target, self.uv_texture.id) gl.glActiveTexture(gl.GL_TEXTURE1) gl.glBindTexture(self.mask_texture.target, self.mask_texture.id) # use an invisible buffer to do the computations. with FrameBuffer() as self.fbo: self.fbo.attach_texture(self.uv_texture) # we need this because the program samples the position of the mouse in discrete times. self.mouse_down = False # initialize the two shaders with self.reaction_shader: self.reaction_shader.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.reaction_shader.vertex_attrib("texcoord", [0, 0, 1, 0, 0, 1, 1, 1]) self.reaction_shader.uniformi("iChannel0", 0) self.reaction_shader.uniformi("mask_texture", 1) self.reaction_shader.uniformf("iResolution", self.tex_width, self.tex_height, 0) self.reaction_shader.uniformf("iMouse", -1, -1) self.reaction_shader.uniformf("params", *ALL_SPECIES[self.species]) with self.render_shader: self.render_shader.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.render_shader.vertex_attrib("texcoord", [0, 0, 1, 0, 0, 1, 1, 1]) self.render_shader.uniformi("iChannel0", 0) self.render_shader.uniformfv("palette", 5, self.palette) self.video_on = video self.buffer = pyglet.image.get_buffer_manager().get_color_buffer() self.sample_rate = sample_rate self.video_rate = video_rate self.frame_count = 0 if video: self.ffmpeg_pipe = self.create_new_pipe() self.start_time = time.perf_counter() def on_draw(self): gl.glClearColor(0.0, 0.0, 0.0, 0.0) self.clear() if time.perf_counter() - self.start_time > 0.1: gl.glViewport(0, 0, self.tex_width, self.tex_height) with self.fbo: with self.reaction_shader: gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) gl.glViewport(0, 0, self.width, self.height) with self.render_shader: gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) self.frame_count += 1 if self.video_on and (self.frame_count % self.sample_rate == 0): self.write_video_frame() def on_key_press(self, symbol, modifiers): """ Keyboard interface. """ if symbol == key.ENTER: self.save_screenshot() if symbol == key.ESCAPE: pyglet.app.exit() if symbol == key.SPACE: self.clear_blank_window() if symbol == key.P: self.use_random_palette() if symbol == key.S and not modifiers: self.use_next_species() if symbol == key.S and (modifiers & key.LCTRL): self.save_config() if symbol == key.O and (modifiers & key.LCTRL): self.require_config_input() if symbol == key.V and (modifiers & key.LCTRL): self.switch_video() def update_species(self, species): self.species = species with self.reaction_shader: self.reaction_shader.uniformf("params", *ALL_SPECIES[species]) def update_palette(self, palette): self.palette = palette with self.render_shader: self.render_shader.uniformfv("palette", 5, palette) def update_mouse(self, x, y): with self.fbo: with self.reaction_shader: self.reaction_shader.uniformf("iMouse", x, y) def save_screenshot(self): self.buffer.save("screenshot-" + self.species + ".png") def save_config(self): with open("grayscott_config.txt", "a+") as f: f.write(self.species + ": " + " ".join(rgba_to_htmlcolors(self.palette)) + "\n") print("> Config saved.\n") def load_config(self, k): with open("grayscott_config.txt", "r") as f: for i, line in enumerate(f.readlines()): if k == i: return parse(line) print("> Config does not exist.\n") def require_config_input(self): try: k = int( input("> Enter the line number in the config file: ").strip()) except ValueError: print("> Invalid input.\n") return try: species, palette = self.load_config(k) self.update_species(species) self.update_palette(palette) print("> Config loaded. Current species: " + self.species) except: return def write_video_frame(self): """ Read frame data from buffer and write it to the video. """ data = self.buffer.get_image_data().get_data("RGBA", -4 * self.width) self.ffmpeg_pipe.write(data) def on_mouse_press(self, x, y, button, modifiers): self.mouse_down = True self.update_mouse(x / self.width, y / self.height) def on_mouse_release(self, x, y, button, modifiers): self.mouse_down = False self.update_mouse(-1, -1) def on_mouse_drag(self, x, y, dx, dy, button, modifiers): if self.mouse_down: self.update_mouse(x / self.width, y / self.height) def clear_blank_window(self): self.update_mouse(-10, -10) def use_random_palette(self): new_palette = np.random.random(20) new_palette[[3, 7, 11, 15, 19]] = sorted(np.random.random(5)) new_palette[:3] = 0 self.update_palette(new_palette) def use_next_species(self): index = self.species_list.index(self.species) new_species = self.species_list[(index + 1) % len(self.species_list)] self.update_species(new_species) print("> Current species: " + self.species + "\n") def switch_video(self): self.video_on = not self.video_on if self.video_on: self.ffmpeg_pipe = self.create_new_pipe() print("> Writing to video...\n") else: self.ffmpeg_pipe.close() print("> The video is closed.\n") def create_new_pipe(self): ffmpeg = subprocess.Popen( ( FFMPEG_EXE, "-threads", "0", "-loglevel", "panic", "-r", "%d" % self.video_rate, "-f", "rawvideo", "-pix_fmt", "rgba", "-s", "%dx%d" % (self.width, self.height), "-i", "-", "-c:v", "libx264", "-crf", "20", "-y", self.species + ".mp4", ), stdin=subprocess.PIPE, ) return ffmpeg.stdin def run(self, fps=None): self.set_visible(True) if fps is None: pyglet.clock.schedule(lambda dt: None) else: pyglet.clock.schedule_interval(lambda dt: None, 1.0 / fps) pyglet.app.run()
def __init__( self, width, height, scale=1.5, conf=1, mask=None, flip=False, video=False, sample_rate=None, video_rate=None, ): """ Parameters ---------- :width & height: size of the window in pixels. :scale: scaling factor of the texture. :conf: line number of the config in the file (`config.txt`). :mask: a user-specified image that is used to control the growth of the pattern. :flip: flip the white/black pixels in the mask image, only meaningful if there is a mask image. :video: whether the video is turned on or off. :sample_rate: sample a frame from the animation every these frames. :video_rate: frames per second of the video. """ pyglet.window.Window.__init__( self, width, height, caption="GrayScott Simulation", resizable=True, visible=False, vsync=False, ) # use two shaders, one for doing the computations and one for rendering to the screen. self.reaction_shader = Shader(["./glsl/default.vert"], ["./glsl/grayscott/compute.frag"]) self.render_shader = Shader(["./glsl/default.vert"], ["./glsl/grayscott/render.frag"]) self.tex_width, self.tex_height = int(width / scale), int(height / scale) try: self.species, self.palette = self.load_config(conf) print("> Current species: " + self.species + "\n") except: conf = "unstable: #00000000 #00FF0033 #FFFF0035 #FF000066 #FFFFFF99" self.species, self.palette = parse(conf) print("> Failed to load the config, using the default one.\n") self.species_list = list(ALL_SPECIES.keys()) # create the uv_texture uv_grid = np.zeros((self.tex_height, self.tex_width, 4), dtype=np.float32) uv_grid[:, :, 0] = 1.0 rand_rows = np.random.choice(range(self.tex_height), 5) rand_cols = np.random.choice(range(self.tex_width), 5) uv_grid[rand_rows, rand_cols, 1] = 1.0 self.uv_texture = create_texture_from_ndarray(uv_grid) # create the mask_texture mask_grid = np.ones_like(uv_grid) if mask is not None: img = (Image.open(mask).convert("L").resize( (self.tex_width, self.tex_height))) img = (np.asarray(img) / 255.0).astype(np.float32) if flip: img = 1.0 - img mask_grid[:, :, 0] = img[::-1] self.mask_texture = create_texture_from_ndarray(mask_grid) # bind the two textures gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(self.uv_texture.target, self.uv_texture.id) gl.glActiveTexture(gl.GL_TEXTURE1) gl.glBindTexture(self.mask_texture.target, self.mask_texture.id) # use an invisible buffer to do the computations. with FrameBuffer() as self.fbo: self.fbo.attach_texture(self.uv_texture) # we need this because the program samples the position of the mouse in discrete times. self.mouse_down = False # initialize the two shaders with self.reaction_shader: self.reaction_shader.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.reaction_shader.vertex_attrib("texcoord", [0, 0, 1, 0, 0, 1, 1, 1]) self.reaction_shader.uniformi("iChannel0", 0) self.reaction_shader.uniformi("mask_texture", 1) self.reaction_shader.uniformf("iResolution", self.tex_width, self.tex_height, 0) self.reaction_shader.uniformf("iMouse", -1, -1) self.reaction_shader.uniformf("params", *ALL_SPECIES[self.species]) with self.render_shader: self.render_shader.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.render_shader.vertex_attrib("texcoord", [0, 0, 1, 0, 0, 1, 1, 1]) self.render_shader.uniformi("iChannel0", 0) self.render_shader.uniformfv("palette", 5, self.palette) self.video_on = video self.buffer = pyglet.image.get_buffer_manager().get_color_buffer() self.sample_rate = sample_rate self.video_rate = video_rate self.frame_count = 0 if video: self.ffmpeg_pipe = self.create_new_pipe() self.start_time = time.perf_counter()
def __init__(self, width, height, aa=1, video_rate=16, sample_rate=4): """ :param width and height: size of the window in pixels. :param aa: antialiasing level, a higher value will give better result but also slow down the animation. (aa=2 is recommended) """ pyglet.window.Window.__init__( self, width, height, caption="Wythoff Explorer", resizable=True, visible=False, vsync=False, ) self.video_rate = video_rate self.video_on = False self.sample_rate = sample_rate self._start_time = time.process_time() self._frame_count = 0 # count number of frames rendered so far self._speed = 4 # control speed of the animation self.aa = aa # shader A draws the UI self.shaderA = Shader(["./glsl/default.vert"], [ "./glsl/wythoff_polyhedra/common.frag", "./glsl/wythoff_polyhedra/BufferA.frag" ]) # shader B draws the polyhedra self.shaderB = Shader(["./glsl/default.vert"], [ "./glsl/wythoff_polyhedra/common.frag", "./glsl/wythoff_polyhedra/BufferB.frag" ]) # shader C puts them together self.shaderC = Shader(["./glsl/default.vert"], [ "./glsl/wythoff_polyhedra/common.frag", "./glsl/wythoff_polyhedra/main.frag" ]) self.font_texture = create_image_texture(FONT_TEXTURE) self.iChannel0 = pyglet.image.Texture.create_for_size( gl.GL_TEXTURE_2D, width, height, gl.GL_RGBA32F_ARB) self.iChannel1 = pyglet.image.Texture.create_for_size( gl.GL_TEXTURE_2D, width, height, gl.GL_RGBA32F_ARB) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(self.iChannel0.target, self.iChannel0.id) gl.glActiveTexture(gl.GL_TEXTURE1) gl.glBindTexture(self.iChannel1.target, self.iChannel1.id) gl.glActiveTexture(gl.GL_TEXTURE2) gl.glBindTexture(gl.GL_TEXTURE_2D, self.font_texture) # frame buffer A renders the UI to texture iChannel0 with FrameBuffer() as self.bufferA: self.bufferA.attach_texture(self.iChannel0) # frame buffer B render the polyhedra to texture iChannel1 with FrameBuffer() as self.bufferB: self.bufferB.attach_texture(self.iChannel1) # initialize the shaders with self.shaderA: self.shaderA.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shaderA.uniformf("iResolution", width, height, 0.0) self.shaderA.uniformf("iTime", 0.0) self.shaderA.uniformf("iMouse", 0.0, 0.0, 0.0, 0.0) self.shaderA.uniformi("iChannel0", 0) self.shaderA.uniformi("iFrame", 0) with self.shaderB: self.shaderB.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shaderB.uniformf("iResolution", width, height, 0.0) self.shaderB.uniformf("iTime", 0.0) self.shaderB.uniformf("iMouse", 0.0, 0.0, 0.0, 0.0) self.shaderB.uniformi("iChannel0", 0) self.shaderB.uniformi("AA", self.aa) with self.shaderC: self.shaderC.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shaderC.uniformf("iResolution", width, height, 0.0) self.shaderC.uniformf("iTime", 0.0) self.shaderC.uniformf("iMouse", 0.0, 0.0, 0.0, 0.0) self.shaderC.uniformi("iChannel0", 0) self.shaderC.uniformi("iChannel1", 1) self.shaderC.uniformi("iChannel2", 2)
class Wythoff(pyglet.window.Window): """ User interface: 1. use mouse click to play with the ui. 2. press `Enter` to save screenshots. 3. press `Ctrl+v` to start saving video and `Ctrl+v` again to stop. 4. press `Esc` to exit. """ def __init__(self, width, height, aa=1, video_rate=16, sample_rate=4): """ :param width and height: size of the window in pixels. :param aa: antialiasing level, a higher value will give better result but also slow down the animation. (aa=2 is recommended) """ pyglet.window.Window.__init__( self, width, height, caption="Wythoff Explorer", resizable=True, visible=False, vsync=False, ) self.video_rate = video_rate self.video_on = False self.sample_rate = sample_rate self._start_time = time.process_time() self._frame_count = 0 # count number of frames rendered so far self._speed = 4 # control speed of the animation self.aa = aa # shader A draws the UI self.shaderA = Shader(["./glsl/default.vert"], [ "./glsl/wythoff_polyhedra/common.frag", "./glsl/wythoff_polyhedra/BufferA.frag" ]) # shader B draws the polyhedra self.shaderB = Shader(["./glsl/default.vert"], [ "./glsl/wythoff_polyhedra/common.frag", "./glsl/wythoff_polyhedra/BufferB.frag" ]) # shader C puts them together self.shaderC = Shader(["./glsl/default.vert"], [ "./glsl/wythoff_polyhedra/common.frag", "./glsl/wythoff_polyhedra/main.frag" ]) self.font_texture = create_image_texture(FONT_TEXTURE) self.iChannel0 = pyglet.image.Texture.create_for_size( gl.GL_TEXTURE_2D, width, height, gl.GL_RGBA32F_ARB) self.iChannel1 = pyglet.image.Texture.create_for_size( gl.GL_TEXTURE_2D, width, height, gl.GL_RGBA32F_ARB) gl.glActiveTexture(gl.GL_TEXTURE0) gl.glBindTexture(self.iChannel0.target, self.iChannel0.id) gl.glActiveTexture(gl.GL_TEXTURE1) gl.glBindTexture(self.iChannel1.target, self.iChannel1.id) gl.glActiveTexture(gl.GL_TEXTURE2) gl.glBindTexture(gl.GL_TEXTURE_2D, self.font_texture) # frame buffer A renders the UI to texture iChannel0 with FrameBuffer() as self.bufferA: self.bufferA.attach_texture(self.iChannel0) # frame buffer B render the polyhedra to texture iChannel1 with FrameBuffer() as self.bufferB: self.bufferB.attach_texture(self.iChannel1) # initialize the shaders with self.shaderA: self.shaderA.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shaderA.uniformf("iResolution", width, height, 0.0) self.shaderA.uniformf("iTime", 0.0) self.shaderA.uniformf("iMouse", 0.0, 0.0, 0.0, 0.0) self.shaderA.uniformi("iChannel0", 0) self.shaderA.uniformi("iFrame", 0) with self.shaderB: self.shaderB.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shaderB.uniformf("iResolution", width, height, 0.0) self.shaderB.uniformf("iTime", 0.0) self.shaderB.uniformf("iMouse", 0.0, 0.0, 0.0, 0.0) self.shaderB.uniformi("iChannel0", 0) self.shaderB.uniformi("AA", self.aa) with self.shaderC: self.shaderC.vertex_attrib("position", [-1, -1, 1, -1, -1, 1, 1, 1]) self.shaderC.uniformf("iResolution", width, height, 0.0) self.shaderC.uniformf("iTime", 0.0) self.shaderC.uniformf("iMouse", 0.0, 0.0, 0.0, 0.0) self.shaderC.uniformi("iChannel0", 0) self.shaderC.uniformi("iChannel1", 1) self.shaderC.uniformi("iChannel2", 2) def on_draw(self): gl.glClearColor(0, 0, 0, 0) self.clear() gl.glViewport(0, 0, self.width, self.height) now = time.process_time() - self._start_time now *= self._speed with self.bufferA: with self.shaderA: self.shaderA.uniformf("iTime", now) self.shaderA.uniformi("iFrame", self._frame_count) gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) with self.bufferB: with self.shaderB: self.shaderB.uniformf("iTime", now) gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) with self.shaderC: self.shaderC.uniformf("iTime", now) gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4) if self.video_on and (self._frame_count % self.sample_rate == 0): self.write_video_frame() self._frame_count += 1 def on_key_press(self, symbol, modifiers): """ Keyboard interface. """ if symbol == key.ENTER: self.save_screenshot() if symbol == key.ESCAPE: pyglet.app.exit() if symbol == key.V and (modifiers & key.LCTRL): self.switch_video() def on_mouse_press(self, x, y, button, modifiers): if button & pyglet.window.mouse.LEFT: with self.shaderA: self.shaderA.uniformf("iMouse", x, y, x, y) def on_mouse_release(self, x, y, button, modifiers): """ Don't forget reset 'iMouse' when mouse is released. """ with self.shaderA: self.shaderA.uniformf("iMouse", 0, 0, 0, 0) def on_mouse_drag(self, x, y, dx, dy, button, modifiers): if button & pyglet.window.mouse.LEFT: with self.shaderA: x += dx y += dy x = max(min(x, self.width), 0) y = max(min(y, self.height), 0) self.shaderA.uniformf("iMouse", x, y, x, y) def save_screenshot(self): image_buffer = pyglet.image.get_buffer_manager().get_color_buffer() image_buffer.save("screenshot.png") def switch_video(self): self.video_on = not self.video_on if self.video_on: self.ffmpeg_pipe = self.create_new_pipe() print("> Writing to video...\n") else: self.ffmpeg_pipe.close() print("> The video is closed.\n") def write_video_frame(self): data = (pyglet.image.get_buffer_manager().get_color_buffer(). get_image_data().get_data("RGBA", -4 * self.width)) self.ffmpeg_pipe.write(data) def create_new_pipe(self): ffmpeg = subprocess.Popen( ( FFMPEG_EXE, "-threads", "0", "-loglevel", "panic", "-r", "%d" % self.video_rate, "-f", "rawvideo", "-pix_fmt", "rgba", "-s", "%dx%d" % (self.width, self.height), "-i", "-", "-c:v", "libx264", "-crf", "20", "-y", "test.mp4", ), stdin=subprocess.PIPE, ) return ffmpeg.stdin def run(self, fps=None): self.set_visible(True) if fps is None: pyglet.clock.schedule(lambda dt: None) else: pyglet.clock.schedule_interval(lambda dt: None, 1.0 / fps) pyglet.app.run()