#!/bin/env python # Copyright (C) 2002-2003 Gideon May (gideon@computer.org) # # Permission to copy, use, sell and distribute this software is granted # provided this copyright notice appears in all copies. # Permission to modify the code and to distribute modified code is granted # provided this copyright notice appears in all copies, and a notice # that the code was modified is included with the copyright notice. # # This software is provided "as is" without express or implied warranty, # and with no claim as to its suitability for any purpose. # -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield # # This application is open source and may be redistributed and/or modified # freely and without restriction, both in commericial and non commericial applications, # as long as this copyright notice is maintained. # # This application is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # ##try: ## import psyco ## #from psyco.classes import __metaclass__ ## psyco.full() ##except ImportError: ## pass import sys import math import thread from PyOSG import * from OpenGL.GL import * from glew import * ## profiling #import profile #import pstats class MyGeometryCallback(osg.Drawable.UpdateCallback, osg.Drawable.AttributeFunctor): """ call back which creates a deformation field to oscilate the model.""" def __init__(self, o, x, y, z, period, xphase, amplitude): osg.Drawable.UpdateCallback.__init__(self) osg.Drawable.AttributeFunctor.__init__(self) self._firstCall = True self._startTime = 0.0 self._time = 0.0 self._period = period self._xphase = xphase self._amplitude = amplitude self._origin = o self._xAxis = x self._yAxis = y self._zAxis = z def update (self, nv, drawable): fs = nv.getFrameStamp() referenceTime = fs.getReferenceTime() if self._firstCall: self._firstCall = False self._startTime = referenceTime self._time = referenceTime - self._startTime drawable.accept(self) drawable.dirtyBound() geometry = drawable.asGeometry() if geometry: osgUtil.SmoothingVisitor.smooth(geometry) def apply(self, drawtype, begin): if drawtype == osg.Drawable.VERTICES: TwoPI = 2.0 * osg.PI phase = -self._time/self._period for itr in begin: dv = itr - self._origin local = osg.Vec3(dv*self._xAxis,dv*self._yAxis,dv*self._zAxis) local[2] = local._x*self._amplitude * math.sin(TwoPI*(phase+local._x*self._xphase)) # Don't assign to itr, since this will create a new object itr.set(self._origin + self._xAxis*local._x+ self._yAxis*local._y+ self._zAxis*local._z) class MyCameraPostDrawCallback(osg.CameraNode.DrawCallback): def __init__(self,image): osg.CameraNode.DrawCallback.__init__(self) self._image = image def apply(self, CameraNode): if (self._image and self._image.getPixelFormat()==GL_RGBA and self._image.getDataType()==GL_UNSIGNED_BYTE): # we'll pick out the center 1/2 of the whole image, column_start = self._image.s()/4 column_end = 3*column_start row_start = self._image.t()/4 row_end = 3*row_start ## and then invert these pixels for r in range(row_start,row_end): for c in range(column_start,column_end): self._image.data(r,c)[0] = 255 - self._image.data(r,c)[0] self._image.data(r,c)[3] = 255 # dirty the image (increments the modified count) so that any textures # using the image can be informed that they need to update. self._image.dirty() elif (self._image and self._image.getPixelFormat()==GL_RGBA and self._image.getDataType()==GL_FLOAT): # we'll pick out the center 1/2 of the whole image, column_start = self._image.s()/4 column_end = 3*column_start row_start = self._image.t()/4 row_end = 3*row_start # and then invert these pixels for r in range(row_start,row_end): for c in range(column_start,column_end): self._image.data(r,c)[0] = 1.0 - self._image.data(r,c)[0] self._image.data(r,c)[3] = 1.0 # dirty the image (increments the modified count) so that any textures # using the image can be informed that they need to update. self._image.dirty() def createPreRenderSubGraph(subgraph,tex_width,tex_height,renderImplementation,useImage,useTextureRectangle,useHDR): if not subgraph: return 0 parent = osg.Group() if (useTextureRectangle): texture = osg.TextureRectangle() else: texture = osg.Texture2D() texture.setTextureSize(tex_width,tex_height) texture.setInternalFormat(GL_RGBA) texture.setFilter(osg.Texture2D.MIN_FILTER,osg.Texture2D.LINEAR) texture.setFilter(osg.Texture2D.MAG_FILTER,osg.Texture2D.LINEAR) if (useHDR): texture.setInternalFormat(GL_RGBA16F_ARB) texture.setSourceFormat(GL_RGBA) texture.setSourceType(GL_FLOAT) # create the quad to visualize. polyGeom = osg.Geometry() #polyGeom.setDataVariance(osg.Object.DYNAMIC) polyGeom.setSupportsDisplayList(False) #polyGeom.setUseVertexBufferObjects(True) origin = osg.Vec3(0.0,0.0,0.0) xAxis = osg.Vec3(1.0,0.0,0.0) yAxis = osg.Vec3(0.0,0.0,1.0) zAxis = osg.Vec3(0.0,-1.0,0.0) height = 100.0 width = 200.0 noSteps = 20 vertices = osg.Vec3Array() bottom = osg.Vec3() bottom.set(origin) top = osg.Vec3() top.set(origin) top[2] += height dv = xAxis * (width/(noSteps-1.0)) texcoords = osg.Vec2Array() bottom_texcoord = osg.Vec2(0.0,0.0) if (useTextureRectangle): top_texcoord = osg.Vec2(0.0,tex_height) dv_texcoord = osg.Vec2(tex_width/(float)(noSteps-1),0.0) else: top_texcoord = osg.Vec2(0.0,1.0) dv_texcoord = osg.Vec2(1.0/(float)(noSteps-1),0.0) for i in range(noSteps): vertices.push_back(top) vertices.push_back(bottom) top+=dv bottom+=dv texcoords.push_back(top_texcoord) texcoords.push_back(bottom_texcoord) top_texcoord+=dv_texcoord bottom_texcoord+=dv_texcoord # pass the created vertex array to the points geometry object. polyGeom.setVertexArray(vertices) polyGeom.setTexCoordArray(0,texcoords) colors = osg.Vec4Array() colors.push_back(osg.Vec4(1.0,1.0,1.0,1.0)) polyGeom.setColorArray(colors) polyGeom.setColorBinding(osg.Geometry.BIND_OVERALL) polyGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.QUAD_STRIP,0,vertices.size())) # new we need to add the texture to the Drawable, we do so by creating a # StateSet to contain the Texture StateAttribute. stateset = osg.StateSet() stateset.setTextureAttributeAndModes(0, texture,osg.StateAttribute.ON) polyGeom.setStateSet(stateset) polyGeom.setUpdateCallback(MyGeometryCallback(origin,xAxis,yAxis,zAxis,1.0,1.0/width,0.2)) geode = osg.Geode() geode.addDrawable(polyGeom) parent.addChild(geode) camera = osg.CameraNode() # set up the background color and clear mask. camera.setClearColor(osg.Vec4(0.1,0.1,0.3,1.0)) camera.setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) bs = subgraph.getBound() if (not bs.valid()): return subgraph znear = 1.0*bs.radius() zfar = 3.0*bs.radius() # 2:1 aspect ratio as per flag geomtry below. proj_top = 0.25*znear proj_right = 0.5*znear znear *= 0.9 zfar *= 1.1 # set up projection. camera.setProjectionMatrixAsFrustum(-proj_right,proj_right,-proj_top,proj_top,znear,zfar) # set view camera.setReferenceFrame(osg.Transform.ABSOLUTE_RF) camera.setViewMatrixAsLookAt(bs.center()-osg.Vec3(0.0,2.0,0.0)*bs.radius(),bs.center(),osg.Vec3(0.0,0.0,1.0)) #set viewport camera.setViewport(0,0,tex_width,tex_height) # 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(renderImplementation) if (useImage): image = osg.Image() #//image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_UNSIGNED_BYTE) image.allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_FLOAT) #// attach the image so its copied on each frame. camera.attach(osg.CameraNode.COLOR_BUFFER, image) camera.setPostDrawCallback(MyCameraPostDrawCallback(image)) #// Rather than attach the texture directly to illustrate the texture's ability to #// detect an image update and to subload the image onto the texture. You needn't #// do this when using an Image for copying to, as a seperate camera->attach(..) #// would suffice as well, but we'll do it the long way round here just for demonstation #// purposes (long way round meaning we'll need to copy image to main memory, then #// copy it back to the graphics card to the texture in one frame). #// The long way round allows us to mannually modify the copied image via the callback #// and then let this modified image by reloaded back. texture.setImage(0, image) else: # attach the texture and use it as the color buffer. camera.attach(osg.CameraNode.COLOR_BUFFER, texture,0,0,False) # add subgraph to render camera.addChild(subgraph) parent.addChild(camera) return(parent) 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 ...") arguments.getApplicationUsage().addCommandLineOption("-h or --help","Display this information") arguments.getApplicationUsage().addCommandLineOption("--fbo","Use Frame Buffer Object for render to texture, where supported.") arguments.getApplicationUsage().addCommandLineOption("--fb","Use FrameBuffer for render to texture.") arguments.getApplicationUsage().addCommandLineOption("--pbuffer","Use Pixel Buffer for render to texture, where supported.") arguments.getApplicationUsage().addCommandLineOption("--window","Use a seperate Window for render to texture.") arguments.getApplicationUsage().addCommandLineOption("--width","Set the width of the render to texture.") arguments.getApplicationUsage().addCommandLineOption("--height","Set the height of the render to texture.") arguments.getApplicationUsage().addCommandLineOption("--image","Render to an image, then apply a post draw callback to it, and use this image to update a texture.") arguments.getApplicationUsage().addCommandLineOption("--texture-rectangle","Use osg::TextureRectangle for doing the render to texure to.") # construct the viewer. viewer = osgProducer.Viewer(arguments) # set up the value with sensible default event handlers. viewer.setUpViewer(osgProducer.Viewer.STANDARD_SETTINGS) # not full screen for debugging viewer.getCamera(0).getRenderSurface().setWindowRectangle(100,100,1024,768) # 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 tex_width = 1024 tex_height = 512 ## while (arguments.read("--width", tex_width)): ## while (arguments.read("--height", tex_height)): renderImplementation = osg.CameraNode.RenderTargetImplementation() renderImplementation = osg.CameraNode.FRAME_BUFFER_OBJECT while (arguments.read("--fbo")): renderImplementation = osg.CameraNode.FRAME_BUFFER_OBJECT while (arguments.read("--pbuffer")): renderImplementation = osg.CameraNode.PIXEL_BUFFER while (arguments.read("--pbuffer-rtt")): renderImplementation = osg.CameraNode.PIXEL_BUFFER_RTT while (arguments.read("--fb")): renderImplementation = osg.CameraNode.FRAME_BUFFER while (arguments.read("--window")): renderImplementation = osg.CameraNode.SEPERATE_WINDOW useImage = False while (arguments.read("--image")): useImage = True useTextureRectangle = False while (arguments.read("--texture-rectangle")): useTextureRectangle = True useHDR = False while (arguments.read("--hdr")): useHDR = True # 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 if arguments.argc()<=1: arguments.getApplicationUsage().write(osg.ApplicationUsage.COMMAND_LINE_OPTION) return 1 # read the scene from the list of file specified commandline args. loadedModel = osgDB.readNodeFiles(arguments) # if no model has been successfully loaded report failure. if not loadedModel: print arguments.getApplicationName(), " No data loaded" return 1 # create a transform to spin the model. loadedModelTransform = osg.MatrixTransform() loadedModelTransform.addChild(loadedModel) nc = osg.AnimationPathCallback(loadedModelTransform.getBound().center(),osg.Vec3(0.0,0.0,1.0),osg.inDegrees(45.0)) loadedModelTransform.setUpdateCallback(nc) rootNode = osg.Group() # rootNode.addChild(loadedModelTransform) rootNode.addChild(createPreRenderSubGraph(loadedModelTransform,tex_width,tex_height,renderImplementation, useImage, useTextureRectangle, useHDR)) # set the scene to render viewer.setSceneData(rootNode) # create the windows and run the threads. viewer.realize() # viewer.realize(Producer.CameraGroup.SingleThreaded) 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__': # Import Psyco if available main(sys.argv) #profile.run('main(sys.argv)','mainprof') #poutput = pstats.Stats('mainprof') #poutput.strip_dirs().sort_stats('cumulative').print_stats(10) #poutput.strip_dirs().sort_stats('time').print_stats(10)