Exemple #1
0
    def update_ndarray(self, new_array=None):
        """to allow numpy arrays to be patched in to textures without regenerating
    new glTextureBuffers i.e. for movie textures"""
        if new_array is not None:
            self.image = new_array
        opengles.glBindTexture(GL_TEXTURE_2D, self._tex)
        # set filters according to mipmap and filter request
        for t in [GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER]:
            opengles.glTexParameteri(GL_TEXTURE_2D, t, self._get_filter(t))

        opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
                                 self.m_repeat)
        opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
                                 self.m_repeat)

        iformat = self._get_format_from_array(self.image, self.i_format)
        opengles.glTexImage2D(
            GL_TEXTURE_2D, 0, iformat, self.ix, self.iy, 0, iformat,
            GL_UNSIGNED_BYTE,
            self.image.ctypes.data_as(ctypes.POINTER(ctypes.c_ubyte)))
        if opengles.glGetError() == GL_OUT_OF_MEMORY:
            LOGGER.critical('Out of GPU memory')
        #opengles.glEnable(GL_TEXTURE_2D) # invalid in OpenGLES 2
        if self.mipmap:
            opengles.glGenerateMipmap(GL_TEXTURE_2D)

        if self.free_after_load:
            self.image = None
            self._loaded = False
Exemple #2
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')
Exemple #3
0
 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')
    def update_ndarray(self, new_array=None, texture_num=None):
        """to allow numpy arrays to be patched in to textures without regenerating
    new glTextureBuffers i.e. for movie textures
    
      *new_array*
        ndarray, if supplied this will be the pixel data for the new
        Texture2D
        
      *texture_num*
        int, if supplied this will make the update effective for a specific
        sampler number i.e. as held in the Buffer.textures array. This
        will be required where multiple textures are used on some of the
        Buffers being drawn in the scene"""

        if new_array is not None:
            self.image = new_array
        if texture_num is not None:
            opengles.glActiveTexture(GL_TEXTURE0 + texture_num)
        opengles.glBindTexture(GL_TEXTURE_2D, self._tex)
        # set filters according to mipmap and filter request
        for t in [GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER]:
            opengles.glTexParameteri(GL_TEXTURE_2D, t, self._get_filter(t))

        opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
                                 self.m_repeat)
        opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
                                 self.m_repeat)

        iformat = self._get_format_from_array(self.image, self.i_format)
        opengles.glTexImage2D(
            GL_TEXTURE_2D, 0, iformat, self.ix, self.iy, 0, iformat,
            GL_UNSIGNED_BYTE,
            self.image.ctypes.data_as(ctypes.POINTER(ctypes.c_ubyte)))
        if opengles.glGetError() == GL_OUT_OF_MEMORY:
            LOGGER.critical('Out of GPU memory in Texture.update_ndarray')
        #opengles.glEnable(GL_TEXTURE_2D) # invalid in OpenGLES 2
        if self.mipmap:
            opengles.glGenerateMipmap(GL_TEXTURE_2D)

        if self.free_after_load:
            self.image = None
            self.file_string = None
            self._loaded = False
Exemple #5
0
    def _prepare(self):
        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.constants import opengles, GL_CLAMP_TO_EDGE, GL_ALWAYS, GL_NEVER, GL_DEPTH_TEST, GL_VERSION
        # used for reimplementing the draw call with instancing
        from pi3d.constants import GLsizei, GLint, GLboolean, GL_FLOAT, GL_UNSIGNED_SHORT, GLuint, GL_ARRAY_BUFFER, GL_STATIC_DRAW, GLsizeiptr, 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 and an array containing the position and speed for all of them
        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)
        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 = pi3d.util.OffScreenTexture.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)