#!/bin/env python import sys import math import thread from PyOSG import * from OpenGL import GL depth_texture_height = 512 depth_texture_width = 512 bias = osg.RefMatrix() bias.set((0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0)) class LightTransformCallback( osg.NodeCallback): def __init__(self, angular_velocity, height, radius): osg.NodeCallback.__init__(self) self._angular_velocity = angular_velocity self._height = height self._radius = radius self._previous_traversal_number = -1 self._previous_time = -1.0 self._angle = 0 def apply(self, node, nv): transform = node.asMatrixTransform() if nv and transform: fs = nv.getFrameStamp() if not fs: return # not frame stamp, no handle on the time so can't move. new_time = fs.getReferenceTime() if nv.getTraversalNumber() != self._previous_traversal_number: self._angle += self._angular_velocity * (new_time - self._previous_time) matrix = osg.Matrix.rotate(math.atan(self._height / self._radius), -osg.X_AXIS) *\ osg.Matrix.rotate(osg.PI_2, osg.Y_AXIS) *\ osg.Matrix.translate(osg.Vec3(self._radius, 0, 0)) *\ osg.Matrix.rotate(self._angle, osg.Y_AXIS) *\ osg.Matrix.translate(osg.Vec3(0, self._height, 0)) # update the specified transform transform.setMatrix(matrix) self._previous_traversal_number = nv.getTraversalNumber(); self._previous_time = new_time # must call any nested node callbacks and continue subgraph traversal. self.traverse(node,nv) class RenderToTextureCallback(osg.NodeCallback): def __init__(self, subgraph, texture, light_transform, tex_gen): osg.NodeCallback.__init__(self) self._subgraph=subgraph self._texture=texture self._local_stateset=osg.StateSet() self._viewport = osg.Viewport() self._light_projection = osg.RefMatrix() self._light_transform=light_transform self._tex_gen=tex_gen self._local_stateset.setAttribute(self._viewport) self._local_stateset.setMode(GL.GL_LIGHTING, osg.StateAttribute.OFF) polygon_offset = osg.ref(osg.PolygonOffset()) polygon_offset().setFactor(1.1) polygon_offset().setUnits(4.0) self._local_stateset.setAttribute(polygon_offset(), osg.StateAttribute.ON | osg.StateAttribute.OVERRIDE) self._local_stateset.setMode(GL.GL_POLYGON_OFFSET_FILL, osg.StateAttribute.ON | osg.StateAttribute.OVERRIDE) cull_face = osg.CullFace() cull_face.setMode(osg.CullFace.FRONT) self._local_stateset.setAttribute(cull_face, osg.StateAttribute.ON | osg.StateAttribute.OVERRIDE) self._local_stateset.setMode(GL.GL_CULL_FACE, osg.StateAttribute.ON | osg.StateAttribute.OVERRIDE) self._viewport.setViewport(0, 0, depth_texture_width, depth_texture_height) znear = 1.0 * self._subgraph.getBound().radius() zfar = 3.0 * self._subgraph.getBound().radius() top = 0.5 * self._subgraph.getBound().radius() right = 0.5 * self._subgraph.getBound().radius() znear *= 0.8 zfar *= 1.2 self._light_projection.makeFrustum(-right, right, -top, top, znear, zfar) def apply(self, node, nv): cullVisitor = osgUtil.asCullVisitor(nv) if cullVisitor and self._texture and self._subgraph: self._request_render_to_depth_texture(node, cullVisitor) # must traverse the subgraph self.traverse(node,nv) def _request_render_to_depth_texture(self, node, cv): # create the render to texture stage. rtts = osg.ref(osgUtil.RenderToTextureStage()) # set up lighting. # currently ignore lights in the scene graph itself.. # will do later. previous_stage = cv.getCurrentRenderBin().getStage() # set up the background color and clear mask. rtts().setClearMask(GL.GL_DEPTH_BUFFER_BIT) rtts().setColorMask(osg.ColorMask(False, False, False, False)) # set up to charge the same RenderStageLighting is the parent previous stage. rtts().setRenderStageLighting(previous_stage.getRenderStageLighting()) # record the render bin, to be restored after creation # of the render to text previousRenderBin = cv.getCurrentRenderBin() saved_compute_near_far_mode = cv.getComputeNearFarMode(); cv.setComputeNearFarMode(osgUtil.CullVisitor.DO_NOT_COMPUTE_NEAR_FAR) # set the current renderbin to be the newly created stage. cv.setCurrentRenderBin(rtts.get()) light_view = osg.RefMatrix() light_view.makeLookAt(self._light_transform.getMatrix().getTrans(), osg.Vec3(0, 0, 0), osg.Z_AXIS) texture_matrix = light_view * (self._light_projection * bias) self._tex_gen.setPlane(osg.TexGen.S, osg.Vec4(texture_matrix(0, 0), texture_matrix(1, 0), texture_matrix(2, 0), texture_matrix(3, 0))) self._tex_gen.setPlane(osg.TexGen.T, osg.Vec4(texture_matrix(0, 1), texture_matrix(1, 1), texture_matrix(2, 1), texture_matrix(3, 1))) self._tex_gen.setPlane(osg.TexGen.R, osg.Vec4(texture_matrix(0, 2), texture_matrix(1, 2), texture_matrix(2, 2), texture_matrix(3, 2))) self._tex_gen.setPlane(osg.TexGen.Q, osg.Vec4(texture_matrix(0, 3), texture_matrix(1, 3), texture_matrix(2, 3), texture_matrix(3, 3))) cv.pushProjectionMatrix(self._light_projection) cv.pushModelViewMatrix(light_view) cv.pushStateSet(self._local_stateset) # traverse the subgraph self._subgraph.accept(cv) cv.popStateSet() cv.popModelViewMatrix() cv.popProjectionMatrix() cv.setComputeNearFarMode(saved_compute_near_far_mode) # restore the previous renderbin. cv.setCurrentRenderBin(previousRenderBin) if rtts().getRenderGraphList().size()==0 and rtts().getRenderBinList().size()==0: # getting to this point means that all the subgraph has been # culled by small feature culling or is beyond LOD ranges. return rtts().setViewport(self._viewport) # and the render to texture stage to the current stages # dependancy list. cv.getCurrentRenderBin().getStage().addToDependencyList(rtts.get()) # if one exist attach texture to the RenderToTextureStage. rtts().setTexture(self._texture) def _create_lights(root_stateset): transform_0 = osg.MatrixTransform() # create a spot light. light_0 = osg.Light() light_0.setLightNum(0) light_0.setPosition(osg.Vec4(0, 0, 0, 1.0)) light_0.setAmbient(osg.Vec4(0.0, 0.0, 0.0, 1.0)) light_0.setDiffuse(osg.Vec4(1.0, 0.8, 0.8, 1.0)) light_0.setSpotCutoff(60.0) light_0.setSpotExponent(2.0) light_source_0 = osg.LightSource() light_source_0.setLight(light_0) light_source_0.setLocalStateSetModes(osg.StateAttribute.ON) transform_0.setUpdateCallback(LightTransformCallback(osg.inDegrees(90.0), 8, 5)) transform_0.addChild(light_source_0) geode = osg.Geode() hints = osg.TessellationHints() hints.setDetailRatio(0.3); shape = osg.ShapeDrawable(osg.Sphere(osg.Vec3(0.0, 0.0, 0.0), 0.15), hints) shape.setColor(osg.Vec4(1.0, 0.5, 0.5, 1.0)) geode.addDrawable(shape) shape = osg.ShapeDrawable(osg.Cylinder(osg.Vec3(0.0, 0.0, -0.4), 0.05, 0.8), hints) shape.setColor(osg.Vec4(1.0, 0.5, 0.5, 1.0)) geode.addDrawable(shape) geode.getOrCreateStateSet().setMode(GL.GL_LIGHTING, osg.StateAttribute.OFF) transform_0.addChild(geode) light_source_0.setStateSetModes(root_stateset, osg.StateAttribute.ON); return transform_0 def _create_scene(): scene = osg.Group() geode_1 = osg.Geode() scene.addChild(geode_1) geode_2 = osg.Geode() transform_2 = osg.MatrixTransform() transform_2.addChild(geode_2) transform_2.setUpdateCallback(osgUtil.TransformCallback(osg.Vec3(0, 0, 0), osg.Y_AXIS, osg.inDegrees(45.0))) scene.addChild(transform_2) geode_3 = osg.Geode() transform_3 = osg.MatrixTransform() transform_3.addChild(geode_3) transform_3.setUpdateCallback(osgUtil.TransformCallback(osg.Vec3(0, 0, 0), osg.Y_AXIS, osg.inDegrees(-22.5))) scene.addChild(transform_3) radius = 0.8 height = 1.0 hints = osg.TessellationHints() hints.setDetailRatio(2.0) shape = osg.ShapeDrawable(osg.Box(osg.Vec3(0.0, -2.0, 0.0), 10, 0.1, 10), hints) shape.setColor(osg.Vec4(0.5, 0.5, 0.7, 1.0)) geode_1.addDrawable(shape) shape = osg.ShapeDrawable(osg.Sphere(osg.Vec3(0.0, 0.0, 0.0), radius * 2), hints) shape.setColor(osg.Vec4(0.8, 0.8, 0.8, 1.0)); geode_1.addDrawable(shape); shape = osg.ShapeDrawable(osg.Sphere(osg.Vec3(-3.0, 0.0, 0.0), radius), hints); shape.setColor(osg.Vec4(0.6, 0.8, 0.8, 1.0)); geode_2.addDrawable(shape) shape = osg.ShapeDrawable(osg.Box(osg.Vec3(3.0, 0.0, 0.0), 2 * radius), hints) shape.setColor(osg.Vec4(0.4, 0.9, 0.3, 1.0)); geode_2.addDrawable(shape); shape = osg.ShapeDrawable(osg.Cone(osg.Vec3(0.0, 0.0, -3.0), radius, height), hints); shape.setColor(osg.Vec4(0.2, 0.5, 0.7, 1.0)); geode_2.addDrawable(shape); shape = osg.ShapeDrawable(osg.Cylinder(osg.Vec3(0.0, 0.0, 3.0), radius, height), hints); shape.setColor(osg.Vec4(1.0, 0.3, 0.3, 1.0)); geode_2.addDrawable(shape); shape = osg.ShapeDrawable(osg.Box(osg.Vec3(0.0, 3.0, 0.0), 2, 0.1, 2), hints); shape.setColor(osg.Vec4(0.8, 0.8, 0.4, 1.0)); geode_3.addDrawable(shape); # material matirial = osg.Material() matirial.setColorMode(osg.Material.DIFFUSE); matirial.setAmbient(osg.Material.FRONT_AND_BACK, osg.Vec4(0, 0, 0, 1)); matirial.setSpecular(osg.Material.FRONT_AND_BACK, osg.Vec4(1, 1, 1, 1)); matirial.setShininess(osg.Material.FRONT_AND_BACK, 64.0); scene.getOrCreateStateSet().setAttributeAndModes(matirial, osg.StateAttribute.ON); return scene def main(argv): # use an ArgumentParser object to manage the program arguments. arguments = osg.ArgumentParser(argv) # set up the usage document, in case we need to print out how to use this program. arguments.getApplicationUsage().setApplicationName(arguments.getApplicationName()) arguments.getApplicationUsage().setDescription(arguments.getApplicationName()+" is the example which demonstrates pre rendering of scene to a texture, and then apply this texture to geometry.") arguments.getApplicationUsage().setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...") # construct the viewer. viewer = osgProducer.Viewer(arguments) # set up the value with sensible default event handlers. viewer.setUpViewer(osgProducer.Viewer.STANDARD_SETTINGS) # get details on keyboard and mouse bindings used by the viewer. viewer.getUsage(arguments.getApplicationUsage()) # if user request help write it out to cout. if arguments.read("-h") or arguments.read("--help"): arguments.getApplicationUsage().write() return 1 # any option left unread are converted into errors to write out later. arguments.reportRemainingOptionsAsUnrecognized() # report any errors if they have occured when parsing the program aguments. if arguments.errors(): arguments.writeErrorMessages() return 1 scene = osg.MatrixTransform() scene.setMatrix(osg.Matrix.rotate(osg.DegreesToRadians(125.0),1.0,0.0,0.0)) shadowed_scene = _create_scene() if not shadowed_scene: return 1 scene.addChild(shadowed_scene) light_transform = _create_lights(scene.getOrCreateStateSet()) if not scene: return 1 scene.addChild(light_transform) texture = osg.Texture2D() texture.setInternalFormat(GL.GL_DEPTH_COMPONENT) texture.setShadowComparison(True) texture.setShadowTextureMode(osg.Texture.LUMINANCE) tex_gen = osg.TexGen() tex_gen.setMode(osg.TexGen.EYE_LINEAR) shadowed_scene.getOrCreateStateSet().setTextureAttributeAndModes(0, texture, osg.StateAttribute.ON) shadowed_scene.getOrCreateStateSet().setTextureAttributeAndModes(0, tex_gen, osg.StateAttribute.ON) scene.setCullCallback(RenderToTextureCallback(shadowed_scene, texture, light_transform, tex_gen)) # add model to viewer. viewer.setSceneData(scene) # create the windows and run the threads. viewer.realize() while not viewer.done(): # wait for all cull and draw threads to complete. viewer.sync() # update the scene by traversing it with the the update visitor which will # call all node update callbacks and animations. viewer.update() # fire off the cull and draw traversals of the scene. viewer.frame() # wait for all cull and draw threads to complete before exit. viewer.sync() return 0 if __name__ == "__main__": main(sys.argv)