1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
|
"""
Loads two gif files into texture arrays and renders them into an offscreen buffer
that is then displayed on the screen using nearest neighbor filtering.
Possible improvements:
- Make the example configurable to load any gif file(s)
- Load the raw byte data for each frame and instead use a palette texture to
reduce the memory footprint
- Take the gif transparency key into account?
"""
from pathlib import Path
import glm
import moderngl
import moderngl_window as mglw
from moderngl_window import geometry
class AnimatedGif(mglw.WindowConfig):
title = "Animated Sprite"
resource_dir = (Path(__file__) / "../../resources").resolve()
aspect_ratio = 320 / 256
window_size = 320 * 3, 256 * 3
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.buffer_size = 320, 256
# Textures
self.background_texture = self.load_texture_array("textures/animated_sprites/giphy.gif")
self.background_texture.repeat_x = False
self.background_texture.repeat_y = False
self.caveman_texture = self.load_texture_array(
"textures/animated_sprites/player_2.gif",
layers=35, # Number of frames in the gif
)
self.caveman_texture.repeat_x = False
self.caveman_texture.repeat_y = False
self.caveman_texture.filter = moderngl.NEAREST, moderngl.NEAREST
# Geometry
# One pixel quad 0, 0 -> 1.0, 1.0
self.sprite_geometry = geometry.quad_2d(size=(1.0, 1.0), pos=(0.5, 0.5))
self.quad_fs = geometry.quad_fs()
# Programs
self.sprite_program = self.load_program("programs/animated_sprites/sprite_array.glsl")
self.texture_program = self.load_program("programs/texture.glsl")
# Offscreen buffer
self.offscreen_texture = self.ctx.texture(self.buffer_size, 4)
self.offscreen_texture.filter = moderngl.NEAREST, moderngl.NEAREST
self.offscreen = self.ctx.framebuffer(color_attachments=[self.offscreen_texture])
self.projection = glm.ortho(0, 320, 0, 256, -1.0, 1.0)
self.sprite_program["projection"].write(self.projection)
def on_render(self, time, frame_time):
# Render sprite of offscreen
self.offscreen.use()
self.ctx.clear(0.5, 0.5, 0.5, 0.0)
self.render_sprite(
self.background_texture,
frame=int(time * 15) % self.background_texture.layers,
)
self.render_sprite(
self.caveman_texture,
frame=int(time * 15) % self.caveman_texture.layers,
blend=True,
position=(260, 20),
)
# Display offscreen
self.ctx.screen.use()
self.offscreen_texture.use(location=0)
self.quad_fs.render(self.texture_program)
def render_sprite(self, texture: moderngl.TextureArray, blend=False, frame=0, position=(0, 0)):
if blend:
self.ctx.enable(moderngl.BLEND)
texture.use(location=0)
self.sprite_program["layer_id"] = frame
self.sprite_program["position"] = position
self.sprite_geometry.render(self.sprite_program)
if blend:
self.ctx.disable(moderngl.BLEND)
if __name__ == "__main__":
AnimatedGif.run()
|