Source code for pi3d.util.OffScreenTexture

import ctypes, time
import numpy as np

import pi3d
from pi3d.constants import (GL_FRAMEBUFFER, GL_RENDERBUFFER, GL_COLOR_ATTACHMENT0,
                    GL_TEXTURE_2D, GL_DEPTH_COMPONENT16, GL_DEPTH_ATTACHMENT,
                    GL_DEPTH_BUFFER_BIT, GL_COLOR_BUFFER_BIT, GL_TEXTURE_MIN_FILTER,
                    GL_TEXTURE_MAG_FILTER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_NEAREST,
                    GL_LINEAR, GL_CLAMP_TO_EDGE, GL_UNSIGNED_BYTE, GL_RGBA, GL_DEPTH_COMPONENT,
                    GL_UNSIGNED_SHORT, GLuint, GLsizei, PLATFORM, PLATFORM_PI)

OFFSCREEN_QUEUE = []

[docs]class TextureShell(): """ Allows OffscreenTexture to hold a color texture and a depth texture that can be added to Buffer.texture allowing it to have tex() function called as with a normal Texture """ def __init__(self, _tex, ix, iy, blend, mipmap): self._tex = _tex self.ix = ix self.iy = iy self.blend = False self.mipmap = False
[docs] def tex(self): return self._tex
[docs]class OffScreenTexture(object): """For creating special effect after rendering to texture rather than onto the display. Used by Defocus, ShadowCaster, Clashtest etc """ def __init__(self, name=""): """ new system doesn't use Texture """ from pi3d.Display import Display self.disp = Display.INSTANCE self.ix, self.iy = self.disp.width, self.disp.height color = GLuint() depth = GLuint() self.framebuffer = (GLuint * 1)() pi3d.opengles.glGenFramebuffers(GLsizei(1), self.framebuffer) self.depthbuffer = (GLuint * 1)() pi3d.opengles.glGenRenderbuffers(GLsizei(1), self.depthbuffer) pi3d.opengles.glGenTextures(1, ctypes.byref(color)) pi3d.opengles.glBindTexture(GL_TEXTURE_2D, color) pi3d.opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) pi3d.opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) pi3d.opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) pi3d.opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) pi3d.opengles.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self.ix, self.iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, None) pi3d.opengles.glGenerateMipmap(GL_TEXTURE_2D) pi3d.opengles.glBindTexture(GL_TEXTURE_2D, 0) pi3d.opengles.glGenTextures(1, ctypes.byref(depth)) pi3d.opengles.glBindTexture(GL_TEXTURE_2D, depth) pi3d.opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) pi3d.opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) pi3d.opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) pi3d.opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) pi3d.opengles.glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, self.ix, self.iy, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, None) pi3d.opengles.glGenerateMipmap(GL_TEXTURE_2D) pi3d.opengles.glBindTexture(GL_TEXTURE_2D, 0) pi3d.opengles.glEnable(GL_TEXTURE_2D) self.color = TextureShell(color, self.ix, self.iy, False, True) self.depth = TextureShell(depth, self.ix, self.iy, False, True) def _start(self, clear=True): """ after calling this method all object.draw()s will rendered to this texture and not appear on the display. Large objects will obviously take a while to draw and re-draw """ self.disp.offscreen_tex = True # flag used in Buffer.draw() pi3d.opengles.glBindFramebuffer(GL_FRAMEBUFFER, self.framebuffer[0]) #thanks to PeterO c.o. RPi forum for pointing out missing depth attchmnt pi3d.opengles.glBindRenderbuffer(GL_RENDERBUFFER, self.depthbuffer[0]) pi3d.opengles.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, self.ix, self.iy) pi3d.opengles.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, self.depthbuffer[0]) pi3d.opengles.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.color._tex.value, 0) pi3d.opengles.glBindTexture(GL_TEXTURE_2D, 0) # this seems to be needed here if PLATFORM != PLATFORM_PI: # depth capture this way doesn't work with bcm pi3d.opengles.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, self.depth._tex.value, 0) if clear: # TODO allow just depth or just color clearing? pi3d.opengles.glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT) #assert opengles.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE global OFFSCREEN_QUEUE if self not in OFFSCREEN_QUEUE: OFFSCREEN_QUEUE.append(self) def _end(self): """ stop capturing to texture and resume normal rendering to default """ self.disp.offscreen_tex = False # flag used in Buffer.draw() pi3d.opengles.glBindTexture(GL_TEXTURE_2D, 0) pi3d.opengles.glBindFramebuffer(GL_FRAMEBUFFER, 0) global OFFSCREEN_QUEUE del OFFSCREEN_QUEUE[-1] if OFFSCREEN_QUEUE: # resume previously active offscreen texture if any OFFSCREEN_QUEUE[-1]._start(clear=False)
[docs] def tex(self): return self.color.tex()
@property def blend(self): return self.color.blend @blend.setter def blend(self, val): self.color.blend = val @property def mipmap(self): return self.color.mipmap @mipmap.setter def mipmap(self, val): self.color.mipmap = val
[docs] def delete_buffers(self): pi3d.opengles.glDeleteFramebuffers(GLsizei(1), self.framebuffer) pi3d.opengles.glDeleteRenderbuffers(GLsizei(1), self.depthbuffer)