Esempio n. 1
0
 def _load_opengl(self):
   self.vbuf = GLuint()
   opengles.glGenBuffers(GLsizei(1), ctypes.byref(self.vbuf))
   self.ebuf = GLuint()
   opengles.glGenBuffers(GLsizei(1), ctypes.byref(self.ebuf))
   self.disp.vbufs_dict[str(self.vbuf)] = [self.vbuf, 0]
   self.disp.ebufs_dict[str(self.ebuf)] = [self.ebuf, 0]
   self._select()
   opengles.glBufferData(GL_ARRAY_BUFFER,
                         self.array_buffer.nbytes,
                         self.array_buffer.ctypes.data_as(ctypes.POINTER(GLfloat)),
                         GL_STATIC_DRAW)
   opengles.glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                         self.element_array_buffer.nbytes,
                         self.element_array_buffer.ctypes.data_as(ctypes.POINTER(GLfloat)),
                         GL_STATIC_DRAW)
   if opengles.glGetError() == GL_OUT_OF_MEMORY:
     LOGGER.critical('Out of GPU memory')
Esempio n. 2
0
File: Buffer.py Progetto: tipam/pi3d
 def _load_opengl(self):
   self.vbuf = c_uint()
   opengles.glGenBuffers(1, ctypes.byref(self.vbuf))
   self.ebuf = c_uint()
   opengles.glGenBuffers(1, ctypes.byref(self.ebuf))
   self.disp.vbufs_dict[str(self.vbuf)] = [self.vbuf, 0]
   self.disp.ebufs_dict[str(self.ebuf)] = [self.ebuf, 0]
   self._select()
   opengles.glBufferData(GL_ARRAY_BUFFER,
                         self.array_buffer.nbytes,
                         self.array_buffer.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
                         GL_STATIC_DRAW)
   opengles.glBufferData(GL_ELEMENT_ARRAY_BUFFER,
                         self.element_array_buffer.nbytes,
                         self.element_array_buffer.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
                         GL_STATIC_DRAW)
   if opengles.glGetError() == GL_OUT_OF_MEMORY:
     LOGGER.critical('Out of GPU memory')
Esempio n. 3
0
    def _prepare(self) -> None:
        self.should_prepare = False

        import numpy as np

        # pi3d has to be imported in the same thread that it will draw in
        # Thus, import it here instead of at the top of the file
        import pi3d
        from pi3d.util.OffScreenTexture import OffScreenTexture
        from pi3d.constants import (
            opengles,
            GL_CLAMP_TO_EDGE,
            GL_ALWAYS,
        )

        # used for reimplementing the draw call with instancing
        from pi3d.constants import (
            GLsizei,
            GLint,
            GLboolean,
            GL_FLOAT,
            GLuint,
            GL_ARRAY_BUFFER,
            GL_STATIC_DRAW,
            GLfloat,
        )
        from PIL import Image

        # Setup display and initialise pi3d
        self.display = pi3d.Display.create(
            w=self.width, h=self.height, window_title="Raveberry"
        )
        # error 0x500 after Display create
        # error = opengles.glGetError()
        # Set a pink background color so mistakes are clearly visible
        # self.display.set_background(1, 0, 1, 1)
        self.display.set_background(0, 0, 0, 1)

        # print OpenGL Version, useful for debugging
        # import ctypes
        # def print_char_p(addr):
        #    g = (ctypes.c_char*32).from_address(addr)
        #    i = 0
        #    while True:
        #        c = g[i]
        #        if c == b'\x00':
        #            break
        #        sys.stdout.write(c.decode())
        #        i += 1
        #    sys.stdout.write('\n')
        # print_char_p(opengles.glGetString(GL_VERSION))

        # Visualization is split into five parts:
        # The background, the particles, the spectrum, the logo and after effects.
        # * The background is a vertical gradient that cycles through HSV color space,
        #     speeding up with strong bass.
        # * Particles are multiple sprites that are created at a specified x,y-coordinate
        #     and fly towards the camera.
        #     Due to the projection into screenspace they seem to move away from the center.
        # * The spectrum is a white circle that represents the fft-transformation of the
        #     currently played audio. It is smoothed to avoid strong spikes.
        # * The logo is a black circle on top of the spectrum containing the logo.
        # * After effects add a vignette.
        # Each of these parts is represented with pi3d Shapes.
        # They have their own shader and are ordered on the z-axis to ensure correct overlapping.

        background_shader = pi3d.Shader(
            os.path.join(settings.BASE_DIR, "core/lights/circle/background")
        )
        self.background = pi3d.Sprite(w=2, h=2)
        self.background.set_shader(background_shader)
        self.background.positionZ(0)

        self.particle_shader = pi3d.Shader(
            os.path.join(settings.BASE_DIR, "core/lights/circle/particle")
        )

        # create one sprite for all particles
        self.particle_sprite = pi3d.Sprite(w=self.PARTICLE_SIZE, h=self.PARTICLE_SIZE)
        self.particle_sprite.set_shader(self.particle_shader)
        self.particle_sprite.positionZ(0)
        # this array containes the position and speed for all particles
        particles = self._initial_particles()

        # This part was modified from https://learnopengl.com/Advanced-OpenGL/Instancing
        self.instance_vbo = GLuint()
        opengles.glGenBuffers(GLsizei(1), ctypes.byref(self.instance_vbo))
        opengles.glBindBuffer(GL_ARRAY_BUFFER, self.instance_vbo)
        particles_raw = particles.ctypes.data_as(ctypes.POINTER(GLfloat))
        opengles.glBufferData(
            GL_ARRAY_BUFFER, particles.nbytes, particles_raw, GL_STATIC_DRAW
        )
        opengles.glBindBuffer(GL_ARRAY_BUFFER, GLuint(0))

        attr_particle = opengles.glGetAttribLocation(
            self.particle_shader.program, b"particle"
        )
        opengles.glEnableVertexAttribArray(attr_particle)
        opengles.glBindBuffer(GL_ARRAY_BUFFER, self.instance_vbo)
        opengles.glVertexAttribPointer(
            attr_particle, GLint(4), GL_FLOAT, GLboolean(0), 0, 0
        )
        opengles.glBindBuffer(GL_ARRAY_BUFFER, GLuint(0))
        opengles.glVertexAttribDivisor(attr_particle, GLuint(1))

        spectrum_shader = pi3d.Shader(
            os.path.join(settings.BASE_DIR, "core/lights/circle/spectrum")
        )

        # use the ratio to compute small sizes for the sprites
        ratio = self.width / self.height
        self.spectrum = pi3d.Sprite(w=2 / ratio, h=2)
        self.spectrum.set_shader(spectrum_shader)
        self.spectrum.positionZ(0)

        # initialize the spectogram history with zeroes
        self.fft = np.zeros(
            (self.FFT_HIST, self.cava.bars - 2 * self.SPECTRUM_CUT), dtype=np.uint8
        )

        logo_shader = pi3d.Shader(
            os.path.join(settings.BASE_DIR, "core/lights/circle/logo")
        )
        self.logo = pi3d.Sprite(w=1.375 / ratio, h=1.375)
        self.logo.set_shader(logo_shader)
        self.logo.positionZ(0)

        logo_image = Image.open(
            os.path.join(settings.STATIC_ROOT, "graphics/raveberry_square.png")
        )
        self.logo_array = np.frombuffer(logo_image.tobytes(), dtype=np.uint8)
        self.logo_array = self.logo_array.reshape(
            (logo_image.size[1], logo_image.size[0], 3)
        )
        # add space for the spectrum
        self.logo_array = np.concatenate(
            (
                self.logo_array,
                np.zeros((self.FFT_HIST, logo_image.size[0], 3), dtype=np.uint8),
            ),
            axis=0,
        )
        # add alpha channel
        self.logo_array = np.concatenate(
            (
                self.logo_array,
                np.ones(
                    (self.logo_array.shape[0], self.logo_array.shape[1], 1),
                    dtype=np.uint8,
                ),
            ),
            axis=2,
        )

        # In order to save memory, the logo and the spectrum share one texture.
        # The upper 256x256 pixels are the raveberry logo.
        # Below are 256xFFT_HIST pixels for the spectrum.
        # The lower part is periodically updated every frame while the logo stays static.
        self.dynamic_texture = pi3d.Texture(self.logo_array)
        # Prevent interpolation from opposite edge
        self.dynamic_texture.m_repeat = GL_CLAMP_TO_EDGE
        self.spectrum.set_textures([self.dynamic_texture])
        self.logo.set_textures([self.dynamic_texture])

        after_shader = pi3d.Shader(
            os.path.join(settings.BASE_DIR, "core/lights/circle/after")
        )
        self.after = pi3d.Sprite(w=2, h=2)
        self.after.set_shader(after_shader)
        self.after.positionZ(0)

        # create an OffscreenTexture to allow scaling.
        # By first rendering into a smaller Texture a lot of computation is saved.
        # This OffscreenTexture is then drawn at the end of the draw loop.
        self.post = OffScreenTexture("scale")
        self.post_sprite = pi3d.Sprite(w=2, h=2)
        post_shader = pi3d.Shader(
            os.path.join(settings.BASE_DIR, "core/lights/circle/scale")
        )
        self.post_sprite.set_shader(post_shader)
        self.post_sprite.set_textures([self.post])

        self.total_bass = 0
        self.last_loop = time.time()
        self.time_elapsed = 0

        opengles.glDepthFunc(GL_ALWAYS)