def __init__(self): self.models = [] self.lights = [] # render to texture scenes self.sceneFBOS = [] # all units <self.textureCounter are reserved self.textureCounter = 0 self.textureMatrixCounter = 0 # matrix used for projective textures will be stored at this unit # only used when self.useProjectiveTextures=True self.useProjectiveTextures = False self.projectiveTextureIndex = -1 self.projectiveTextureMatrixIndex = -1 self.projectivTextureMatrix = ProjectiveTextureMatrix(self) # set to true if you need the frustum points/centroid calculated self.needFrustumPoints = False # and the associated frustum self.sceneFrustum = Frustum(0.0, 1.0) GLApp.__init__(self) # camera for user movement self.sceneCamera = UserCamera(self) # create screen textures self.sceneTexture = Texture2D(width=self.winSize[0], height=self.winSize[1]) self.sceneTexture.create() self.sceneDepthTexture = DepthTexture2D(width=self.winSize[0], height=self.winSize[1]) self.sceneDepthTexture.create() # a shader that only handles the depth self.depthShader = DepthShader() self.visualizeDepthShader = VisualizeDepthShader() ### TESTING ### self.lightAngle = 0.0
class ModelApp(GLApp): """ ModelApp splits rendering in multiple passes using fbos. """ def __init__(self): self.models = [] self.lights = [] # render to texture scenes self.sceneFBOS = [] # all units <self.textureCounter are reserved self.textureCounter = 0 self.textureMatrixCounter = 0 # matrix used for projective textures will be stored at this unit # only used when self.useProjectiveTextures=True self.useProjectiveTextures = False self.projectiveTextureIndex = -1 self.projectiveTextureMatrixIndex = -1 self.projectivTextureMatrix = ProjectiveTextureMatrix(self) # set to true if you need the frustum points/centroid calculated self.needFrustumPoints = False # and the associated frustum self.sceneFrustum = Frustum(0.0, 1.0) GLApp.__init__(self) # camera for user movement self.sceneCamera = UserCamera(self) # create screen textures self.sceneTexture = Texture2D(width=self.winSize[0], height=self.winSize[1]) self.sceneTexture.create() self.sceneDepthTexture = DepthTexture2D(width=self.winSize[0], height=self.winSize[1]) self.sceneDepthTexture.create() # a shader that only handles the depth self.depthShader = DepthShader() self.visualizeDepthShader = VisualizeDepthShader() ### TESTING ### self.lightAngle = 0.0 def reserveTextureUnit(self): """ increase texture counter. """ self.textureCounter += 1 return self.textureCounter - 1 def reserveTextureMatrixUnit(self): """ increase texture counter. """ self.textureMatrixCounter += 1 return self.textureMatrixCounter - 1 def enableProjectiveTextures(self): """ sets flag for enabling projective textures and reserves one texture unit for the projection matrix. """ if self.projectiveTextureIndex != -1: return self.projectiveTextureIndex = self.reserveTextureUnit() self.projectiveTextureMatrixIndex = self.reserveTextureMatrixUnit() self.useProjectiveTextures = True def _loadProjectiveTextureMatrix(): self.projectivTextureMatrix.load(self.projectiveTextureMatrixIndex) self.signalConnect(GLApp.APP_PROJECTION_CHANGED, _loadProjectiveTextureMatrix) self.sceneCamera.signalConnect(Camera.CAMERA_MODELVIEW_SIGNAL, _loadProjectiveTextureMatrix) def addModel(self, model): """ adds a model to this application. """ self.models.append(model) self.lights += model.lights self.lights = unique(self.lights) for s in model.segments: if s.usesProjectiveTexture(): self.enableProjectiveTextures() break def prependFBO(self, sceneFBO, drawScene, sceneCamera): """ prepends a render to texture operation before scene rendering is done. """ self.sceneFBOS = [(sceneFBO, drawScene, sceneCamera)] + self.sceneFBOS def appendFBO(self, sceneFBO, drawScene, sceneCamera): """ appends a render to texture operation before scene rendering is done. """ self.sceneFBOS.append((sceneFBO, drawScene, sceneCamera)) def setDefaultSceneFBO(self): """ set the default fbo for offscreen scene rendering. """ drawSceneFBO = FBO() drawSceneFBO.addColorTexture(self.sceneTexture) drawSceneFBO.setDepthTexture(self.sceneDepthTexture) drawSceneFBO.create() self.appendFBO(drawSceneFBO, self.drawSceneComplete, self.sceneCamera) def drawSceneComplete(self): """ draws scene with color. """ map(lambda m: m.draw(self, m), self.models) def drawSceneGeometry(self): """ draws the scene geometry only. usefull for depth value rendering. """ map(_drawModelGeometry, self.models) def setWindowSize(self, size): """ also set frustum projection. """ ret = GLApp.setWindowSize(self, size) # set the frustum projection self.sceneFrustum.setProjection(self.fov, self.aspect, self.nearClip, self.farClip) return ret def orthogonalPass(self, debugShadows=False): """ draw stuff in orthogonal projection. mainly the scene can be post processed here and some gui elements could be drawn. """ ### DRAW THE SCENE ### # TODO: post shader magic ! glUseProgram(0) # enable the scene texture # Note that texture unit 0 should be active now. glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.sceneTexture.glID) # make sure texture matrix is set to identity glMatrixMode(GL_TEXTURE) glLoadIdentity() glMatrixMode(GL_MODELVIEW) glBegin(GL_QUADS) glTexCoord2f(0.0, 0.0) glVertex3f(0.0, 0.0, -1.0) glTexCoord2f(1.0, 0.0) glVertex3f(self.winSize[0], 0.0, -1.0) glTexCoord2f(1.0, 1.0) glVertex3f(self.winSize[0], self.winSize[1], -1.0) glTexCoord2f(0.0, 1.0) glVertex3f(0.0, self.winSize[1], -1.0) glEnd() ### DEBUG DRAWINGS ### # debug shadow maps if debugShadows: # draw the shadow maps self.visualizeDepthShader.enable() layerLoc = glGetUniformLocation(self.visualizeDepthShader.program, "layer") drawShadowMaps(self.lights, layerLoc) # reset viewport and reset to ffp glViewport(0, 0, self.winSize[0], self.winSize[1]) glUseProgram(0) glBindTexture(GL_TEXTURE_2D, 0) glDisable(GL_TEXTURE_2D) GLApp.orthogonalPass(self) def processEvent(self, event): """ processes one event. """ # pass some events to the scene camera if event.type == MOUSEMOTION: self.sceneCamera.motion(*event.pos) elif event.type == MOUSEBUTTONUP or event.type == MOUSEBUTTONDOWN: self.sceneCamera.mouseEvent(event.button, event.type, event.pos[0], event.pos[1]) def animationStep(self): ####### TESTING ########## self.lightAngle += 0.005 if self.lightAngle >= 2.0 * pi: self.lightAngle -= 2.0 * pi lightRadius = 4.0 self.lights[0].lightPosition = array( [lightRadius * sin(self.lightAngle), lightRadius, lightRadius * cos(self.lightAngle), 0.0], float32 ) self.lights[0].signalQueueEmit(Light.LIGHT_POSITION_SIGNAL) ####### TESTING ########## def processFrame(self, timePassedSecs): """ draws a scene """ # update the model view matrix self.sceneCamera.updateKeys() self.sceneCamera.update() # process queued GLObject events GLObject.signalsEmit() # enable some default scene states glEnable(GL_DEPTH_TEST) ######## SHADOW MAP RENDERING START ######## # offset the geometry slightly to prevent z-fighting # note that this introduces some light-leakage artifacts glEnable(GL_POLYGON_OFFSET_FILL) glPolygonOffset(1.1, 4096.0) # cull front faces for shadow rendering, # this moves z-fighting to backfaces. glCullFace(GL_FRONT) # enable depth rendering shader. # FIXME: support segment geometry shader! # geometry shader could change the shadow shape! self.depthShader.enable() map(_updateLightShadowMap, self.lights) glBindFramebuffer(GL_FRAMEBUFFER, 0) glDisable(GL_POLYGON_OFFSET_FILL) ######## SHADOW MAP RENDERING STOP ######## #### TODO: FOG: integrate FOG #### glEnable(GL_FOG) glFogi(GL_FOG_MODE, GL_EXP2) # approximate the atmosphere's filtering effect as a linear function sunDir = array([4.0, 4.0, 4.0, 0.0], float32) # TODO: FOG: what is the sun dir ? skyColor = array([0.8, sunDir[1] * 0.1 + 0.7, sunDir[1] * 0.4 + 0.5, 1.0], float32) glClearColor(*skyColor) glFogf(GL_FOG_DENSITY, 0.4) glFogf(GL_FOG_START, 16.0) glFogf(GL_FOG_END, self.farClip) glFogfv(GL_FOG_COLOR, skyColor) # fill projection matrix glMatrixMode(GL_PROJECTION) glLoadMatrixf(self.projectionMatrix) glMatrixMode(GL_MODELVIEW) # draw stuff in 3d projection lastCam = None glPushAttrib(GL_COLOR_BUFFER_BIT) for (fbo, draw, cam) in self.sceneFBOS: # render to fbo fbo.enable() if cam != lastCam: cam.enable() lastCam = cam draw() # disable render to texture FBO.disable() glPopAttrib() glViewport(0, 0, self.winSize[0], self.winSize[1]) #### TODO: FOG: integrate FOG #### glDisable(GL_FOG) # change to orthogonal projection glMatrixMode(GL_PROJECTION) glLoadMatrixf(self.orthoMatrix) glMatrixMode(GL_MODELVIEW) glLoadIdentity() # no depth test needed in orthogonal rendering glDisable(GL_DEPTH_TEST) # draw orthogonal to the screen self.orthogonalPass()