Beispiel #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
Beispiel #2
0
 def video_load_frame(self, frame):
     """
     Load frame (numpy array) in texture
     """
     # return self.video_texture.update_ndarray(frame, 0)
     tex = self.video_texture
     opengles.glActiveTexture(GL_TEXTURE0)
     opengles.glBindTexture(GL_TEXTURE_2D, tex._tex)
     opengles.glTexSubImage2D(
         GL_TEXTURE_2D, 0, 0, 0, tex.ix, tex.iy, self.frame_format,
         GL_UNSIGNED_BYTE,
         frame.ctypes.data_as(ctypes.POINTER(ctypes.c_ubyte)))
     opengles.glGenerateMipmap(GL_TEXTURE_2D)
    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
Beispiel #4
0
  def draw(self, shape=None, M=None, unif=None, shader=None,
                     textures=None, ntl=None, shny=None, fullset=True):
    """Draw this Buffer, called by the parent Shape.draw()

    Keyword arguments:
      *shape*
        Shape object this Buffer belongs to, has to be passed at draw to avoid
        circular reference
      *shader*
        Shader object
      *textures*
        array of Texture objects
      *ntl*
        multiple for tiling normal map which can be less than or greater
        than 1.0. 0.0 disables the normal mapping, float
      *shiny*
        how strong to make the reflection 0.0 to 1.0, float
    """
    self.load_opengl()

    shader = shader or self.shader or shape.shader or Shader.instance()
    shader.use()
    opengles.glUniformMatrix4fv(shader.unif_modelviewmatrix, GLsizei(3),
                                GLboolean(0), M.ctypes.data)

    opengles.glUniform3fv(shader.unif_unif, GLsizei(20), unif)
    textures = textures or self.textures
    if ntl is not None:
      self.unib[0] = ntl
    if shny is not None:
      self.unib[1] = shny
    self._select()

    opengles.glVertexAttribPointer(shader.attr_vertex, GLint(3), GL_FLOAT, GLboolean(0), self.N_BYTES, 0)
    opengles.glEnableVertexAttribArray(shader.attr_vertex)
    if self.N_BYTES > 12:
      opengles.glVertexAttribPointer(shader.attr_normal, GLint(3), GL_FLOAT, GLboolean(0), self.N_BYTES, 12)
      opengles.glEnableVertexAttribArray(shader.attr_normal)
      if self.N_BYTES > 24:
        opengles.glVertexAttribPointer(shader.attr_texcoord, GLint(2), GL_FLOAT, GLboolean(0), self.N_BYTES, 24)
        opengles.glEnableVertexAttribArray(shader.attr_texcoord)

    opengles.glDisable(GL_BLEND)

    self.unib[2] = 0.6
    for t, texture in enumerate(textures):
      if (self.disp.last_textures[t] != texture or self.disp.last_shader != shader or
            self.disp.offscreen_tex): # very slight speed increase for sprites
        opengles.glActiveTexture(GL_TEXTURE0 + t)
        assert texture.tex(), 'There was an empty texture in your Buffer.'
        opengles.glBindTexture(GL_TEXTURE_2D, texture.tex())
        opengles.glUniform1i(shader.unif_tex[t], GLint(t))
        self.disp.last_textures[t] = texture

      if texture.blend:
        # i.e. if any of the textures set to blend then all will for this shader.
        self.unib[2] = 0.05

    if self.unib[2] != 0.6 or shape.unif[13] < 1.0 or shape.unif[14] < 1.0:
      #use unib[2] as flag to indicate if any Textures to be blended
      #needs to be done outside for..textures so materials can be transparent
        opengles.glEnable(GL_BLEND)
        self.unib[2] = 0.05

    self.disp.last_shader = shader

    opengles.glUniform3fv(shader.unif_unib, GLsizei(5), self.unib)

    opengles.glEnable(GL_DEPTH_TEST) # TODO find somewhere more efficient to do this

    opengles.glDrawElements(self.draw_method, GLsizei(self.ntris * 3), GL_UNSIGNED_SHORT, 0)
Beispiel #5
0
    def draw(self) -> None:
        import numpy as np
        from scipy.ndimage.filters import gaussian_filter
        from pi3d.Camera import Camera
        from pi3d.constants import (
            opengles,
            GL_SRC_ALPHA,
            GL_ONE_MINUS_SRC_ALPHA,
            GLsizei,
            GLboolean,
            GLint,
            GL_FLOAT,
            GL_ARRAY_BUFFER,
            GL_UNSIGNED_SHORT,
            GL_TEXTURE_2D,
            GL_UNSIGNED_BYTE,
        )

        time_logging = False

        if self.should_prepare:
            self._prepare()

        if self.lights.alarm_program.factor != -1:
            self.alarm_factor = max(0.001, self.lights.alarm_program.factor)
        else:
            self.alarm_factor = 0

        then = time.time()

        self.display.loop_running()
        now = self.display.time
        self.time_delta = now - self.last_loop
        self.last_loop = now
        self.time_elapsed += self.time_delta

        if time_logging:
            print(f"{time.time() - then} main loop")
            then = time.time()

        # use a sliding window to smooth the spectrum with a gauss function
        # truncating does not save significant time (~3% for this step)

        # new_frame = np.array(self.cava.current_frame, dtype="float32")
        new_frame = gaussian_filter(self.cava.current_frame, sigma=1.5, mode="nearest")
        new_frame = new_frame[self.SPECTRUM_CUT : -self.SPECTRUM_CUT]
        new_frame = -0.5 * new_frame ** 3 + 1.5 * new_frame
        new_frame *= 255
        current_frame = new_frame

        if time_logging:
            print(f"{time.time() - then} spectrum smoothing")
            then = time.time()

        # Value used for circle shake and background color cycle
        # select the first few values and compute their average
        bass_elements = math.ceil(self.BASS_MAX * self.cava.bars)
        self.bass_value = sum(current_frame[0:bass_elements]) / bass_elements / 255
        self.bass_value = max(self.bass_value, self.alarm_factor)
        self.total_bass = self.total_bass + self.bass_value
        # the fraction of time that there was bass
        self.bass_fraction = self.total_bass / self.time_elapsed / self.lights.UPS

        self.uniform_values = {
            48: self.width / self.scale,
            49: self.height / self.scale,
            50: self.scale,
            51: self.FFT_HIST,
            52: self.NUM_PARTICLES,
            53: self.PARTICLE_SPAWN_Z,
            54: self.time_elapsed,
            55: self.time_delta,
            56: self.alarm_factor,
            57: self.bass_value,
            58: self.total_bass,
            59: self.bass_fraction,
        }

        # start rendering to the smaller OffscreenTexture
        # we decrease the size of the texture so it only allocates that much memory
        # otherwise it would use as much as the displays size, negating its positive effect
        self.post.ix = int(self.post.ix / self.scale)
        self.post.iy = int(self.post.iy / self.scale)
        opengles.glViewport(
            GLint(0),
            GLint(0),
            GLsizei(int(self.width / self.scale)),
            GLsizei(int(self.height / self.scale)),
        )
        self.post._start()
        self.post.ix = self.width
        self.post.iy = self.height

        self._set_unif(self.background, [48, 49, 54, 56, 58])
        self.background.draw()

        if time_logging:
            print(f"{time.time() - then} background draw")
            then = time.time()

        # enable additive blending so the draw order of overlapping particles does not matter
        opengles.glBlendFunc(1, 1)

        self._set_unif(self.particle_sprite, [53, 54, 59])

        # copied code from pi3d.Shape.draw()
        # we don't need modelmatrices, normals ord textures and always blend
        self.particle_sprite.load_opengl()
        camera = Camera.instance()
        if not camera.mtrx_made:
            camera.make_mtrx()
        self.particle_sprite.MRaw = self.particle_sprite.tr1
        self.particle_sprite.M[0, :, :] = self.particle_sprite.MRaw[:, :]
        self.particle_sprite.M[1, :, :] = np.dot(
            self.particle_sprite.MRaw, camera.mtrx
        )[:, :]

        # Buffer.draw()
        buf = self.particle_sprite.buf[0]
        buf.load_opengl()
        shader = buf.shader
        shader.use()
        opengles.glUniformMatrix4fv(
            shader.unif_modelviewmatrix,
            GLsizei(2),
            GLboolean(0),
            self.particle_sprite.M.ctypes.data,
        )
        opengles.glUniform3fv(shader.unif_unif, GLsizei(20), self.particle_sprite.unif)
        buf._select()
        opengles.glVertexAttribPointer(
            shader.attr_vertex, GLint(3), GL_FLOAT, GLboolean(0), buf.N_BYTES, 0
        )
        opengles.glEnableVertexAttribArray(shader.attr_vertex)
        opengles.glVertexAttribPointer(
            shader.attr_texcoord, GLint(2), GL_FLOAT, GLboolean(0), buf.N_BYTES, 24
        )
        opengles.glEnableVertexAttribArray(shader.attr_texcoord)
        buf.disp.last_shader = shader
        opengles.glUniform3fv(shader.unif_unib, GLsizei(5), buf.unib)

        opengles.glBindBuffer(GL_ARRAY_BUFFER, self.instance_vbo)
        opengles.glDrawElementsInstanced(
            buf.draw_method,
            GLsizei(buf.ntris * 3),
            GL_UNSIGNED_SHORT,
            0,
            self.NUM_PARTICLES,
        )

        # restore normal blending
        opengles.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        if time_logging:
            print(f"{time.time() - then} particle draw")
            then = time.time()

        # roll the history one further, insert the current one.
        # we use a texture with four channels eventhough we only need one, refer to this post:
        # https://community.khronos.org/t/updating-textures-per-frame/75020/3
        # basically the gpu converts it anyway, so other formats would be slower
        history = np.zeros(
            (self.FFT_HIST, self.cava.bars - 2 * self.SPECTRUM_CUT, 4), dtype="uint8"
        )
        self.fft = np.roll(self.fft, 1, 0)
        self.fft[0] = current_frame
        history[:, :, 0] = self.fft

        if time_logging:
            print(f"{time.time() - then} spectrum roll")
            then = time.time()

        # change the spectrum part of the texture (the lower 256xFFT_HIST pixels)
        opengles.glBindTexture(GL_TEXTURE_2D, self.dynamic_texture._tex)
        iformat = self.dynamic_texture._get_format_from_array(
            history, self.dynamic_texture.i_format
        )
        opengles.glTexSubImage2D(
            GL_TEXTURE_2D,
            0,
            0,
            self.dynamic_texture.ix,
            history.shape[1],
            history.shape[0],
            iformat,
            GL_UNSIGNED_BYTE,
            history.ctypes.data_as(ctypes.POINTER(ctypes.c_ubyte)),
        )

        if time_logging:
            print(f"{time.time() - then} glTexImage2D")
            then = time.time()

        self._set_unif(self.spectrum, [48, 49, 51, 52, 53, 54, 55, 57, 58])
        self.spectrum.draw()

        if time_logging:
            print(f"{time.time() - then} spectrum draw")
            then = time.time()

        self._set_unif(self.logo, [48, 49, 51, 54, 57, 58])
        self.logo.draw()

        if time_logging:
            print(f"{time.time() - then} logo draw")
            then = time.time()

        self._set_unif(self.after, [48, 49, 54, 57])
        self.after.draw()

        if time_logging:
            print(f"{time.time() - then} after draw")
            then = time.time()

        self.post._end()

        opengles.glViewport(
            GLint(0), GLint(0), GLsizei(self.width), GLsizei(self.height)
        )
        self._set_unif(self.post_sprite, [50])
        self.post_sprite.draw()

        if time_logging:
            print(f"{time.time() - then} post draw")
            print(f"scale: {self.scale}")
            print("=====")
Beispiel #6
0
  def draw(self, shape=None, M=None, unif=None, shader=None,
                     textures=None, ntl=None, shny=None, fullset=True):
    """Draw this Buffer, called by the parent Shape.draw()

    Keyword arguments:
      *shape*
        Shape object this Buffer belongs to, has to be passed at draw to avoid
        circular reference
      *shader*
        Shader object
      *textures*
        array of Texture objects
      *ntl*
        multiple for tiling normal map which can be less than or greater
        than 1.0. 0.0 disables the normal mapping, float
      *shiny*
        how strong to make the reflection 0.0 to 1.0, float
    """
    self.load_opengl()

    shader = shader or self.shader or shape.shader or Shader.instance()
    shader.use()
    opengles.glUniformMatrix4fv(shader.unif_modelviewmatrix, 3,
                                ctypes.c_ubyte(0), M.ctypes.data)

    opengles.glUniform3fv(shader.unif_unif, 20, unif)
    textures = textures or self.textures
    if ntl is not None:
      self.unib[0] = ntl
    if shny is not None:
      self.unib[1] = shny
    self._select()

    opengles.glVertexAttribPointer(shader.attr_vertex, 3, GL_FLOAT, 0, self.N_BYTES, 0)
    opengles.glEnableVertexAttribArray(shader.attr_vertex)
    if self.N_BYTES > 12:
      opengles.glVertexAttribPointer(shader.attr_normal, 3, GL_FLOAT, 0, self.N_BYTES, 12)
      opengles.glEnableVertexAttribArray(shader.attr_normal)
      if self.N_BYTES > 24:
        opengles.glVertexAttribPointer(shader.attr_texcoord, 2, GL_FLOAT, 0, self.N_BYTES, 24)
        opengles.glEnableVertexAttribArray(shader.attr_texcoord)

    opengles.glDisable(GL_BLEND)

    self.unib[2] = 0.6
    for t, texture in enumerate(textures):
      if (self.disp.last_textures[t] != texture or self.disp.last_shader != shader or
            self.disp.offscreen_tex): # very slight speed increase for sprites
        opengles.glActiveTexture(GL_TEXTURE0 + t)
        assert texture.tex(), 'There was an empty texture in your Buffer.'
        opengles.glBindTexture(GL_TEXTURE_2D, texture.tex())
        opengles.glUniform1i(shader.unif_tex[t], t)
        self.disp.last_textures[t] = texture

      if texture.blend:
        # i.e. if any of the textures set to blend then all will for this shader.
        self.unib[2] = 0.05

    if self.unib[2] != 0.6 or shape.unif[13] < 1.0 or shape.unif[14] < 1.0:
      #use unib[2] as flag to indicate if any Textures to be blended
      #needs to be done outside for..textures so materials can be transparent
        opengles.glEnable(GL_BLEND)
        self.unib[2] = 0.05

    self.disp.last_shader = shader

    opengles.glUniform3fv(shader.unif_unib, 5, self.unib)

    opengles.glEnable(GL_DEPTH_TEST) # TODO find somewhere more efficient to do this

    opengles.glDrawElements(self.draw_method, self.ntris * 3, GL_UNSIGNED_SHORT, 0)