class RiftApp(): def __init__(self): Rift.initialize() self.rift = Rift() self.rift.init() self.frame = 0 self.mirror = True self.eyeRenderDescs = (ovr.EyeRenderDesc * 2)() self.eyeOffsets = (ovr.Vector3f * 2)() self.projections = (ovr.Matrix4f * 2)() self.textureSizes = (ovr.Sizei * 2)() self.fovPorts = (ovr.FovPort * 2)() self.poses = (ovr.Posef * 2)() self.bufferSize = ovr.Sizei() self.viewScale = ovr.ViewScaleDesc() self.viewScale.HmdSpaceToWorldScaleInMeters = 1.0 for eye in range(0, 2): self.fovPorts[eye] = self.rift.hmdDesc.DefaultEyeFov[eye] self.eyeRenderDescs[eye] = self.rift.get_render_desc( eye, self.fovPorts[eye]) self.eyeOffsets[eye] = self.eyeRenderDescs[eye].HmdToEyeOffset self.projections[eye] = Rift.get_perspective( self.fovPorts[eye], 0.01, 1000) self.textureSizes[eye] = self.rift.get_fov_texture_size( eye, self.fovPorts[eye]) self.viewScale.HmdToEyeOffset[eye] = self.eyeOffsets[eye] self.bufferSize.w = self.textureSizes[0].w + self.textureSizes[1].w self.bufferSize.h = max(self.textureSizes[0].h, self.textureSizes[1].h) # TODO make the Rift wrapper class manage the layers and provide an API for enabling and disabling them # Initialize our single full screen Fov layer. layer = ovr.LayerEyeFov() layer.Header.Type = ovr.LayerType_EyeFov layer.Header.Flags = ovr.LayerFlag_TextureOriginAtBottomLeft # OpenGL convention layer.Fov[0] = self.fovPorts[0] layer.Fov[1] = self.fovPorts[1] viewportSize = ovr.Sizei(self.bufferSize.w / 2, self.bufferSize.h) viewportPos = ovr.Vector2i(0, 0) layer.Viewport[0] = ovr.Recti(viewportPos, viewportSize) viewportPos = ovr.Vector2i(self.bufferSize.w / 2, 0) layer.Viewport[1] = ovr.Recti(viewportPos, viewportSize) self.layer = layer def close(self): self.framebuffer.destroy() self.rift.destroy() self.rift = None Rift.shutdown() def create_window(self): import os os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100, 100) pygame.init() resolution = self.rift.get_resolution() resolution.w /= 4 resolution.h /= 4 self.windowSize = resolution # Note we DO NOT want double bufering here as that will trigger v-sync flags = pgl.HWSURFACE | pgl.OPENGL | pgl.NOFRAME size = (self.windowSize.w, self.windowSize.h) pygame.display.set_mode(size, flags) def init_gl(self): print("GL Version: " + glGetString(GL_VERSION)) print("GL Shader Language Version: " + glGetString(GL_SHADING_LANGUAGE_VERSION)) print("GL Vendor: " + glGetString(GL_VENDOR)) print("GL Renderer: " + glGetString(GL_RENDERER)) self.framebuffer = RiftSwapFramebuffer(self.rift, self.bufferSize) self.layer.ColorTexture[ 0] = self.framebuffer.pTextureSet # single texture for both eyes self.layer.ColorTexture[ 1] = self.framebuffer.pTextureSet # single texture for both eyes def submit_frame(self): layers = [self.layer.Header] for eye in range(0, 2): self.layer.RenderPose[eye] = self.poses[eye] self.framebuffer.commit() self.rift.submit_frame(self.frame, self.viewScale, layers, 1) def render_frame(self): self.frame += 1 # Fetch the head pose self.poses = self.rift.get_eye_poses(self.frame, ovr.ovrTrue, self.eyeOffsets) # Active the offscreen framebuffer and render the scene self.framebuffer.bind() self.framebuffer.attachCurrentTexture() for eye in range(0, 2): self.currentEye = eye vp = self.layer.Viewport[eye] glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h) glEnable(GL_SCISSOR_TEST) glScissor(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h) self.render_scene() self.currentEye = -1 self.framebuffer.unbind() # Optional, mirror to the screen if self.mirror: glDisable(GL_SCISSOR_TEST) glViewport(0, 0, self.windowSize.w, self.windowSize.h) glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT) self.framebuffer.bind(GL_READ_FRAMEBUFFER) glBlitFramebuffer(0, 0, self.framebuffer.size.w, self.framebuffer.size.h, 0, 0, self.windowSize.w, self.windowSize.h, GL_COLOR_BUFFER_BIT, GL_NEAREST) self.framebuffer.unbind(GL_READ_FRAMEBUFFER) self.submit_frame() def update(self): for event in pygame.event.get(): self.on_event(event) def on_event(self, event): if event.type == pgl.QUIT: self.running = False return True if event.type == pgl.KEYUP and event.key == pgl.K_ESCAPE: self.running = False return True return False def run(self): self.create_window() self.init_gl() self.running = True while self.running: self.update() self.render_frame() #pygame.display.flip() self.close() pygame.quit()
class RiftGLRendererCompatibility(list): "Class RiftGLRenderer is a list of OpenGL actors" def __init__(self): self.layers = list() self.width = 100 self.height = 100 self.frame_index = 0 self.textureSwapChain = None self.rift = Rift() Rift.initialize() self.rift.init() # TODO: Yuck initialize() init() # self.rift.configure_tracking() def display_gl(self): self.display_rift_gl() self.display_desktop_gl() def display_desktop_gl(self): # 1) desktop (non-Rift) pass glBindFramebuffer(GL_FRAMEBUFFER, 0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) self._set_up_desktop_projection() glMatrixMode(GL_MODELVIEW) glLoadIdentity() for actor in self: actor.display_gl() def display_rift_gl(self): # 2) Rift pass glBindFramebuffer(GL_FRAMEBUFFER, self.fbo) layer, texId = self._update_gl_poses() glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0) # print format(glCheckFramebufferStatus(GL_FRAMEBUFFER), '#X'), GL_FRAMEBUFFER_COMPLETE glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) for eye in range(2): # Set up eye viewport v = layer.Viewport[eye] glViewport(v.Pos.x, v.Pos.y, v.Size.w, v.Size.h) # Get projection matrix for the Rift camera glMatrixMode(GL_PROJECTION) glLoadIdentity() proj = self.rift.get_perspective( layer.Fov[eye], 0.2, 100.0, ) glMultTransposeMatrixf(proj.M) # Get view matrix for the Rift camera glMatrixMode(GL_MODELVIEW) glLoadIdentity() p = layer.RenderPose[eye].Position q = layer.RenderPose[eye].Orientation pitch, yaw, roll = q.getEulerAngles() glRotatef(-roll * 180 / math.pi, 0, 0, 1) glRotatef(-yaw * 180 / math.pi, 0, 1, 0) glRotatef(-pitch * 180 / math.pi, 1, 0, 0) glTranslatef(-p.x, -p.y, -p.z) # Render the scene for this eye. for actor in self: actor.display_gl() self.submit_frame() glBindFramebuffer(GL_FRAMEBUFFER, 0) def dispose_gl(self): for actor in self: actor.dispose_gl() if self.textureSwapChain is not None: self.rift.destroy_swap_texture(self.textureSwapChain) def init_gl(self): glClearColor(0, 0, 1, 0) self._init_rift_render_layer() self.fbo = glGenFramebuffers(1) self._set_up_desktop_projection() for actor in self: actor.init_gl() def resize_gl(self, width, height): self.width = width self.height = height glViewport(0, 0, width, height) self._set_up_desktop_projection() def submit_frame(self): # 2c) Call ovr_SubmitFrame, passing swap texture set(s) from the previous step within a ovrLayerEyeFov structure. Although a single layer is required to submit a frame, you can use multiple layers and layer types for advanced rendering. ovr_SubmitFrame passes layer textures to the compositor which handles distortion, timewarp, and GPU synchronization before presenting it to the headset. layers = [self.layer.Header] viewScale = ovr.ViewScaleDesc() viewScale.HmdSpaceToWorldScaleInMeters = 1.0 viewScale.HmdToEyeOffset[0] = self.hmdToEyeOffset[0] viewScale.HmdToEyeOffset[1] = self.hmdToEyeOffset[1] self.rift.commit_texture_swap_chain(self.textureSwapChain) result = self.rift.submit_frame(self.frame_index, viewScale, layers, 1) self.frame_index += 1 def _init_rift_render_layer(self): """ NOTE: Initialize OpenGL first (elsewhere), before getting Rift textures here. """ # Configure Stereo settings. # Use a single shared texture for simplicity # 1bb) Compute texture sizes hmdDesc = self.rift.hmdDesc recommenedTex0Size = self.rift.get_fov_texture_size( ovr.Eye_Left, hmdDesc.DefaultEyeFov[0]) recommenedTex1Size = self.rift.get_fov_texture_size( ovr.Eye_Right, hmdDesc.DefaultEyeFov[1]) bufferSize = ovr.Sizei() bufferSize.w = recommenedTex0Size.w + recommenedTex1Size.w bufferSize.h = max(recommenedTex0Size.h, recommenedTex1Size.h) # print "Recommended buffer size = ", bufferSize, bufferSize.w, bufferSize.h # NOTE: We need to have set up OpenGL context before this point... # 1c) Allocate SwapTextureSets self.textureSwapChain = self.rift.create_swap_texture(bufferSize) # Initialize VR structures, filling out description. # 1ba) Compute FOV eyeRenderDesc = (ovr.EyeRenderDesc * 2)() hmdToEyeOffset = (ovr.Vector3f * 2)() eyeRenderDesc[0] = self.rift.get_render_desc(ovr.Eye_Left, hmdDesc.DefaultEyeFov[0]) eyeRenderDesc[1] = self.rift.get_render_desc(ovr.Eye_Right, hmdDesc.DefaultEyeFov[1]) hmdToEyeOffset[0] = eyeRenderDesc[0].HmdToEyeOffset hmdToEyeOffset[1] = eyeRenderDesc[1].HmdToEyeOffset self.hmdToEyeOffset = hmdToEyeOffset # Initialize our single full screen Fov layer. layer = ovr.LayerEyeFov() layer.Header.Type = ovr.LayerType_EyeFov layer.Header.Flags = ovr.LayerFlag_TextureOriginAtBottomLeft # OpenGL convention layer.ColorTexture[ 0] = self.textureSwapChain # single texture for both eyes layer.ColorTexture[ 1] = self.textureSwapChain # single texture for both eyes layer.Fov[0] = eyeRenderDesc[0].Fov layer.Fov[1] = eyeRenderDesc[1].Fov layer.Viewport[0] = ovr.Recti( ovr.Vector2i(0, 0), ovr.Sizei(int(bufferSize.w / 2), bufferSize.h)) layer.Viewport[1] = ovr.Recti( ovr.Vector2i(int(bufferSize.w / 2), 0), ovr.Sizei(int(bufferSize.w / 2), bufferSize.h)) self.layer = layer def _set_up_desktop_projection(self): # TODO: non-fixed-function pathway # Projection matrix for desktop (i.e. non-Rift) display glMatrixMode(GL_PROJECTION) glLoadIdentity() zNear = 0.1 zFar = 1000.0 fovY = 3.14159 / 4.0 # radians aspect = float(self.width) / float(self.height) fH = math.tan(fovY) * zNear fW = fH * aspect glFrustum(-fW, fW, -fH, fH, zNear, zFar) # glMatrixMode(GL_MODELVIEW) def _update_gl_poses(self): # 2a) Use ovr_GetTrackingState and ovr_CalcEyePoses to compute eye poses needed for view rendering based on frame timing information displayMidpointSeconds = self.rift.get_predicted_display_time( self.frame_index) hmdState = self.rift.get_tracking_state(displayMidpointSeconds, True) # print hmdState.HeadPose.ThePose self.rift.calc_eye_poses(hmdState.HeadPose.ThePose, self.hmdToEyeOffset, self.layer.RenderPose) # Increment to use next texture, just before writing # 2d) Advance CurrentIndex within each used texture set to target the next consecutive texture buffer for the following frame. textureId = self.rift.get_current_texture_id_GL(self.textureSwapChain) # TODO: mirror texture # mirrorTextureId = ovr.getMirrorTextureBufferGL(self.rift.session, self.mirrorTexture) return self.layer, textureId
class RiftGLRendererCompatibility(list): "Class RiftGLRenderer is a list of OpenGL actors" def __init__(self): self.layers = list() self.width = 100 self.height = 100 self.frame_index = 0 self.textureSwapChain = None self.rift = Rift() Rift.initialize() self.rift.init() # TODO: Yuck initialize() init() # self.rift.configure_tracking() def display_gl(self): self.display_rift_gl() self.display_desktop_gl() def display_desktop_gl(self): # 1) desktop (non-Rift) pass glBindFramebuffer(GL_FRAMEBUFFER, 0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) self._set_up_desktop_projection() glMatrixMode(GL_MODELVIEW) glLoadIdentity() for actor in self: actor.display_gl() def display_rift_gl(self): # 2) Rift pass glBindFramebuffer(GL_FRAMEBUFFER, self.fbo) layer, texId = self._update_gl_poses() glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0) # print format(glCheckFramebufferStatus(GL_FRAMEBUFFER), '#X'), GL_FRAMEBUFFER_COMPLETE glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) for eye in range(2): # Set up eye viewport v = layer.Viewport[eye] glViewport(v.Pos.x, v.Pos.y, v.Size.w, v.Size.h) # Get projection matrix for the Rift camera glMatrixMode(GL_PROJECTION) glLoadIdentity() proj = self.rift.get_perspective(layer.Fov[eye], 0.2, 100.0, ) glMultTransposeMatrixf(proj.M) # Get view matrix for the Rift camera glMatrixMode(GL_MODELVIEW) glLoadIdentity() p = layer.RenderPose[eye].Position q = layer.RenderPose[eye].Orientation pitch, yaw, roll = q.getEulerAngles() glRotatef(-roll*180/math.pi, 0, 0, 1) glRotatef(-yaw*180/math.pi, 0, 1, 0) glRotatef(-pitch*180/math.pi, 1, 0, 0) glTranslatef(-p.x, -p.y, -p.z) # Render the scene for this eye. for actor in self: actor.display_gl() self.submit_frame() glBindFramebuffer(GL_FRAMEBUFFER, 0) def dispose_gl(self): for actor in self: actor.dispose_gl() if self.textureSwapChain is not None: self.rift.destroy_swap_texture(self.textureSwapChain) def init_gl(self): glClearColor(0, 0, 1, 0) self._init_rift_render_layer() self.fbo = glGenFramebuffers(1) self._set_up_desktop_projection() for actor in self: actor.init_gl() def resize_gl(self, width, height): self.width = width self.height = height glViewport(0, 0, width, height) self._set_up_desktop_projection() def submit_frame(self): # 2c) Call ovr_SubmitFrame, passing swap texture set(s) from the previous step within a ovrLayerEyeFov structure. Although a single layer is required to submit a frame, you can use multiple layers and layer types for advanced rendering. ovr_SubmitFrame passes layer textures to the compositor which handles distortion, timewarp, and GPU synchronization before presenting it to the headset. layers = [self.layer.Header] viewScale = ovr.ViewScaleDesc() viewScale.HmdSpaceToWorldScaleInMeters = 1.0 viewScale.HmdToEyeOffset[0] = self.hmdToEyeOffset[0] viewScale.HmdToEyeOffset[1] = self.hmdToEyeOffset[1] self.rift.commit_texture_swap_chain(self.textureSwapChain) result = self.rift.submit_frame(self.frame_index, viewScale, layers, 1) self.frame_index += 1 def _init_rift_render_layer(self): """ NOTE: Initialize OpenGL first (elsewhere), before getting Rift textures here. """ # Configure Stereo settings. # Use a single shared texture for simplicity # 1bb) Compute texture sizes hmdDesc = self.rift.hmdDesc recommenedTex0Size = self.rift.get_fov_texture_size(ovr.Eye_Left, hmdDesc.DefaultEyeFov[0]) recommenedTex1Size = self.rift.get_fov_texture_size(ovr.Eye_Right, hmdDesc.DefaultEyeFov[1]) bufferSize = ovr.Sizei() bufferSize.w = recommenedTex0Size.w + recommenedTex1Size.w bufferSize.h = max ( recommenedTex0Size.h, recommenedTex1Size.h ) # print "Recommended buffer size = ", bufferSize, bufferSize.w, bufferSize.h # NOTE: We need to have set up OpenGL context before this point... # 1c) Allocate SwapTextureSets self.textureSwapChain = self.rift.create_swap_texture(bufferSize) # Initialize VR structures, filling out description. # 1ba) Compute FOV eyeRenderDesc = (ovr.EyeRenderDesc * 2)() hmdToEyeOffset = (ovr.Vector3f * 2)() eyeRenderDesc[0] = self.rift.get_render_desc(ovr.Eye_Left, hmdDesc.DefaultEyeFov[0]) eyeRenderDesc[1] = self.rift.get_render_desc(ovr.Eye_Right, hmdDesc.DefaultEyeFov[1]) hmdToEyeOffset[0] = eyeRenderDesc[0].HmdToEyeOffset hmdToEyeOffset[1] = eyeRenderDesc[1].HmdToEyeOffset self.hmdToEyeOffset = hmdToEyeOffset # Initialize our single full screen Fov layer. layer = ovr.LayerEyeFov() layer.Header.Type = ovr.LayerType_EyeFov layer.Header.Flags = ovr.LayerFlag_TextureOriginAtBottomLeft # OpenGL convention layer.ColorTexture[0] = self.textureSwapChain # single texture for both eyes layer.ColorTexture[1] = self.textureSwapChain # single texture for both eyes layer.Fov[0] = eyeRenderDesc[0].Fov layer.Fov[1] = eyeRenderDesc[1].Fov layer.Viewport[0] = ovr.Recti(ovr.Vector2i(0, 0), ovr.Sizei(bufferSize.w / 2, bufferSize.h)) layer.Viewport[1] = ovr.Recti(ovr.Vector2i(bufferSize.w / 2, 0), ovr.Sizei(bufferSize.w / 2, bufferSize.h)) self.layer = layer def _set_up_desktop_projection(self): # TODO: non-fixed-function pathway # Projection matrix for desktop (i.e. non-Rift) display glMatrixMode(GL_PROJECTION) glLoadIdentity() zNear = 0.1 zFar = 1000.0 fovY = 3.14159 / 4.0 # radians aspect = float(self.width) / float(self.height) fH = math.tan(fovY) * zNear fW = fH * aspect glFrustum( -fW, fW, -fH, fH, zNear, zFar ) # glMatrixMode(GL_MODELVIEW) def _update_gl_poses(self): # 2a) Use ovr_GetTrackingState and ovr_CalcEyePoses to compute eye poses needed for view rendering based on frame timing information displayMidpointSeconds = self.rift.get_predicted_display_time(self.frame_index) hmdState = self.rift.get_tracking_state(displayMidpointSeconds, True) # print hmdState.HeadPose.ThePose self.rift.calc_eye_poses(hmdState.HeadPose.ThePose, self.hmdToEyeOffset, self.layer.RenderPose) # Increment to use next texture, just before writing # 2d) Advance CurrentIndex within each used texture set to target the next consecutive texture buffer for the following frame. textureId = self.rift.get_current_texture_id_GL(self.textureSwapChain) # TODO: mirror texture # mirrorTextureId = ovr.getMirrorTextureBufferGL(self.rift.session, self.mirrorTexture) return self.layer, textureId
class RiftApp(): def __init__(self): Rift.initialize() self.rift = Rift() self.rift.init() self.frame = 0 self.mirror = True self.eyeRenderDescs = (ovr.EyeRenderDesc * 2)() self.eyeOffsets = (ovr.Vector3f * 2)() self.projections = (ovr.Matrix4f * 2)() self.textureSizes = (ovr.Sizei * 2)() self.fovPorts = (ovr.FovPort * 2)() self.poses = (ovr.Posef * 2)() self.bufferSize = ovr.Sizei() self.viewScale = ovr.ViewScaleDesc() self.viewScale.HmdSpaceToWorldScaleInMeters = 1.0 for eye in range(0, 2): self.fovPorts[eye] = self.rift.hmdDesc.DefaultEyeFov[eye] self.eyeRenderDescs[eye] = self.rift.get_render_desc(eye, self.fovPorts[eye]); self.eyeOffsets[eye] = self.eyeRenderDescs[eye].HmdToEyeOffset self.projections[eye] = Rift.get_perspective(self.fovPorts[eye], 0.01, 1000) self.textureSizes[eye] = self.rift.get_fov_texture_size(eye, self.fovPorts[eye]) self.viewScale.HmdToEyeOffset[eye] = self.eyeOffsets[eye] self.bufferSize.w = self.textureSizes[0].w + self.textureSizes[1].w self.bufferSize.h = max ( self.textureSizes[0].h, self.textureSizes[1].h ) # TODO make the Rift wrapper class manage the layers and provide an API for enabling and disabling them # Initialize our single full screen Fov layer. layer = ovr.LayerEyeFov() layer.Header.Type = ovr.LayerType_EyeFov layer.Header.Flags = ovr.LayerFlag_TextureOriginAtBottomLeft # OpenGL convention layer.Fov[0] = self.fovPorts[0] layer.Fov[1] = self.fovPorts[1] viewportSize = ovr.Sizei(self.bufferSize.w / 2, self.bufferSize.h) viewportPos = ovr.Vector2i(0, 0) layer.Viewport[0] = ovr.Recti(viewportPos, viewportSize) viewportPos = ovr.Vector2i(self.bufferSize.w / 2, 0) layer.Viewport[1] = ovr.Recti(viewportPos, viewportSize) self.layer = layer def close(self): self.framebuffer.destroy() self.rift.destroy() self.rift = None Rift.shutdown() def create_window(self): import os os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100, 100) pygame.init() resolution = self.rift.get_resolution() resolution.w /= 4 resolution.h /= 4 self.windowSize = resolution # Note we DO NOT want double bufering here as that will trigger v-sync flags = pgl.HWSURFACE | pgl.OPENGL | pgl.NOFRAME size = (self.windowSize.w, self.windowSize.h) pygame.display.set_mode(size, flags) def init_gl(self): print("GL Version: " + glGetString(GL_VERSION)); print("GL Shader Language Version: " + glGetString(GL_SHADING_LANGUAGE_VERSION)); print("GL Vendor: " + glGetString(GL_VENDOR)); print("GL Renderer: " + glGetString(GL_RENDERER)); self.framebuffer = RiftSwapFramebuffer(self.rift, self.bufferSize) self.layer.ColorTexture[0] = self.framebuffer.pTextureSet # single texture for both eyes self.layer.ColorTexture[1] = self.framebuffer.pTextureSet # single texture for both eyes def submit_frame(self): layers = [self.layer.Header] for eye in range(0, 2): self.layer.RenderPose[eye] = self.poses[eye] self.framebuffer.commit() self.rift.submit_frame(self.frame, self.viewScale, layers, 1) def render_frame(self): self.frame += 1 # Fetch the head pose self.poses = self.rift.get_eye_poses(self.frame, ovr.ovrTrue, self.eyeOffsets) # Active the offscreen framebuffer and render the scene self.framebuffer.bind() self.framebuffer.attachCurrentTexture() for eye in range(0, 2): self.currentEye = eye vp = self.layer.Viewport[eye] glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h) glEnable(GL_SCISSOR_TEST) glScissor(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h) self.render_scene() self.currentEye = -1 self.framebuffer.unbind() # Optional, mirror to the screen if self.mirror: glDisable(GL_SCISSOR_TEST) glViewport(0, 0, self.windowSize.w, self.windowSize.h) glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT) self.framebuffer.bind(GL_READ_FRAMEBUFFER) glBlitFramebuffer(0, 0, self.framebuffer.size.w, self.framebuffer.size.h, 0, 0, self.windowSize.w, self.windowSize.h, GL_COLOR_BUFFER_BIT, GL_NEAREST) self.framebuffer.unbind(GL_READ_FRAMEBUFFER) self.submit_frame() def update(self): for event in pygame.event.get(): self.on_event(event) def on_event(self, event): if event.type == pgl.QUIT: self.running = False return True if event.type == pgl.KEYUP and event.key == pgl.K_ESCAPE: self.running = False return True return False def run(self): self.create_window() self.init_gl() self.running = True while self.running: self.update() self.render_frame() #pygame.display.flip() self.close() pygame.quit()