#!/bin/env python import sys import math import thread from PyOSG import * from OpenGL import GL depth_texture_height = 2048 depth_texture_width = 2048 ##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)) #////////////////////////////////////////////////////////////////// #// fragment shader #// fragmentShaderSource = r""" uniform sampler2D baseTexture; uniform sampler2DShadow shadowTexture; uniform vec2 ambientBias; void main(void) { vec4 color = gl_Color * texture2D( baseTexture, gl_TexCoord[0].xy ); gl_FragColor = color * (ambientBias.x + shadow2DProj( shadowTexture, gl_TexCoord[1] ) * ambientBias.y); } """ 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)) 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.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) def _create_lights(): transform_0 = osg.MatrixTransform() radius = 0.001 angle = osg.inDegrees(0.0) height = 5.0 # create a spot light. light_0 = osg.Light() light_0.setLightNum(0) light_0.setPosition(osg.Vec4(0, 0, 0, 1.0)) light_0.setDirection(osg.Vec3(0.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(50.0) light_0.setSpotExponent(0.0) ## light_0.setConstantAttenuation(1.0) ## light_0.setLinearAttenuation(0.5/height) ## light_0.setQuadraticAttenuation(0.5/osg.square(height)) light_source_0 = osg.LightSource() light_source_0.setLight(light_0) light_source_0.setLocalStateSetModes(osg.StateAttribute.ON) ## transform_0.setUpdateCallback(LightTransformCallback(osg.inDegrees(0.0), 8, 0.001)) matrix = osg.Matrix.rotate(math.atan2(height, radius), -osg.X_AXIS) *\ osg.Matrix.rotate(osg.PI_2, osg.Y_AXIS) *\ osg.Matrix.translate(osg.Vec3(radius, 0, 0)) *\ osg.Matrix.rotate(angle, osg.Y_AXIS) *\ osg.Matrix.translate(osg.Vec3(0, height, 0)) transform_0.addChild(light_source_0) transform_0.setMatrix(matrix) return transform_0 def _create_scene(): scene = osg.Group() geode_1 = osg.Geode() scene.addChild(geode_1) sphere = 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) geode_1.getOrCreateStateSet().setTextureAttributeAndModes( 0, osg.Texture2D(osgDB.readImageFile("text2.tif")), osg.StateAttribute.ON); 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) geode_2.getOrCreateStateSet().setTextureAttributeAndModes( 0, osg.Texture2D(osgDB.readImageFile("tex2.tif")), osg.StateAttribute.ON); 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, sphere class UpdateCameraAndTexGenCallback(osg.NodeCallback): def __init__(self,light_transform,cameraNode,texgenNode): osg.NodeCallback.__init__(self) self._light_transform = light_transform self._cameraNode = cameraNode self._texgenNode = texgenNode def apply(self,node, nv): # first update subgraph to make sure objects are all moved into postion self.traverse(node,nv) # now compute the camera's view and projection matrix to point at the shadower (the camera's children) bs = osg.BoundingSphere() for i in range(self._cameraNode.getNumChildren()): bs.expandBy(self._cameraNode.getChild(i).getBound()) if (not bs): print osg.notify(osg.WARN) + "bb invalid" + _cameraNode return position = self._light_transform.getMatrix().getTrans() centerDistance = (position-bs.center()).length() znear = centerDistance-bs.radius() zfar = centerDistance+bs.radius() zNearRatio = 0.001 if (znear < zfar*zNearRatio): znear = zfar*zNearRatio #if 0 # hack to illustrate the precision problems of excessive gap between near far range. #znear = 0.00001*zfar #endif top = (bs.radius()/centerDistance)*znear right = top self._cameraNode.setReferenceFrame(osg.CameraNode.ABSOLUTE_RF) self._cameraNode.setProjectionMatrixAsFrustum(-right,right,-top,top,znear,zfar) self._cameraNode.setViewMatrixAsLookAt(position,bs.center(),osg.Vec3(0.0,1.0,0.0)) # compute the matrix which takes a vertex from local coords into tex coords # will use this later to specify osg.TexGen.. MVPT = self._cameraNode.getViewMatrix() * self._cameraNode.getProjectionMatrix() * \ osg.Matrix.translate(1.0,1.0,1.0) * osg.Matrix.scale(0.5,0.5,0.5) self._texgenNode.getTexGen().setMode(osg.TexGen.EYE_LINEAR) self._texgenNode.getTexGen().setPlanesFromMatrix(MVPT) def createShadowedScene(shadowed,light_transform,unit): group = osg.Group() tex_width = 1024 tex_height = 1024 texture = osg.Texture2D() texture.setTextureSize(tex_width, tex_height) texture.setInternalFormat(GL.GL_DEPTH_COMPONENT) texture.setShadowComparison(True) texture.setShadowTextureMode(osg.Texture.LUMINANCE) texture.setFilter(osg.Texture2D.MIN_FILTER,osg.Texture2D.LINEAR) texture.setFilter(osg.Texture2D.MAG_FILTER,osg.Texture2D.LINEAR) # set up the render to texture camera. # create the camera camera = osg.CameraNode() camera.setClearMask(GL.GL_DEPTH_BUFFER_BIT) camera.setClearColor(osg.Vec4(1.0,1.0,1.0,1.0)) camera.setComputeNearFarMode(camera.DO_NOT_COMPUTE_NEAR_FAR) # set viewport camera.setViewport(0,0,tex_width,tex_height) _local_stateset = camera.getOrCreateStateSet() _local_stateset.setMode(GL.GL_LIGHTING, osg.StateAttribute.OFF) factor = 0.0 units = 1.0 polygon_offset = osg.PolygonOffset() polygon_offset.setFactor(factor) polygon_offset.setUnits(units) _local_stateset.setAttribute(polygon_offset, osg.StateAttribute.ON | osg.StateAttribute.OVERRIDE) _local_stateset.setMode(GL.GL_POLYGON_OFFSET_FILL, osg.StateAttribute.ON | osg.StateAttribute.OVERRIDE) cull_face = osg.CullFace() cull_face.setMode(osg.CullFace.FRONT) _local_stateset.setAttribute(cull_face, osg.StateAttribute.ON | osg.StateAttribute.OVERRIDE) _local_stateset.setMode(GL.GL_CULL_FACE, osg.StateAttribute.ON | osg.StateAttribute.OVERRIDE) # set the camera to render before the main camera. camera.setRenderOrder(osg.CameraNode.PRE_RENDER) # tell the camera to use OpenGL frame buffer object where supported. camera.setRenderTargetImplementation(osg.CameraNode.FRAME_BUFFER_OBJECT) # attach the texture and use it as the color buffer. camera.attach(osg.CameraNode.DEPTH_BUFFER, texture, 0, 0, False) # add subgraph to render camera.addChild(shadowed) group.addChild(camera) # create the texgen node to project the tex coords onto the subgraph texgenNode = osg.TexGenNode() texgenNode.setTextureUnit(unit) group.addChild(texgenNode) # set an update callback to keep moving the camera and tex gen in the right direction. group.setUpdateCallback(UpdateCameraAndTexGenCallback(light_transform, camera, texgenNode)) # set the shadowed subgraph so that it uses the texture and tex gen settings. shadowedGroup = osg.Group() shadowedGroup.addChild(shadowed) group.addChild(shadowedGroup) stateset = shadowedGroup.getOrCreateStateSet() stateset.setTextureAttributeAndModes(unit,texture,osg.StateAttribute.ON) stateset.setTextureMode(unit,GL.GL_TEXTURE_GEN_S,osg.StateAttribute.ON) stateset.setTextureMode(unit,GL.GL_TEXTURE_GEN_T,osg.StateAttribute.ON) stateset.setTextureMode(unit,GL.GL_TEXTURE_GEN_R,osg.StateAttribute.ON) stateset.setTextureMode(unit,GL.GL_TEXTURE_GEN_Q,osg.StateAttribute.ON) program = osg.Program() stateset.setAttribute(program) fragment_shader = osg.Shader(osg.Shader.FRAGMENT, fragmentShaderSource) program.addShader(fragment_shader) baseTextureSampler = osg.Uniform("baseTexture",0) stateset.addUniform(baseTextureSampler) shadowTextureSampler = osg.Uniform("shadowTexture",unit) stateset.addUniform(shadowTextureSampler) ambientBias = osg.Uniform("ambientBias",osg.Vec2(0.5,0.7)) stateset.addUniform(ambientBias) # add the shadower and shadowed. group.addChild(light_transform) return group 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().setDescription(arguments.getApplicationName() + " is the example which demonstrates using of GL_ARB_shadow extension implemented in osg::Texture class") arguments.getApplicationUsage().setCommandLineUsage(arguments.getApplicationName()) arguments.getApplicationUsage().addCommandLineOption("-h or --help", "Display this information") arguments.getApplicationUsage().addCommandLineOption("--with-base-texture", "Adde base texture to shadowed model.") arguments.getApplicationUsage().addCommandLineOption("--no-base-texture", "Adde base texture to shadowed model.") # 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()) withBaseTexture = True while(arguments.read("--with-base-texture")): withBaseTexture = True while(arguments.read("--no-base-texture")): withBaseTexture = False # 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,sphere = _create_scene() if not shadowed_scene: return 1 light_transform = _create_lights() if not light_transform: return 1 shadowed_scene.getOrCreateStateSet().setTextureAttributeAndModes( 0, osg.Texture2D(osgDB.readImageFile("text.tif")), osg.StateAttribute.ON); shadowedScene = createShadowedScene(shadowed_scene,light_transform,1); scene.addChild(shadowedScene) ## scene.addChild(light_transform) ## scene.addChild(shadowed_scene) # 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)