#!/usr/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. # import sys import math from PyOSG import * from OpenGL.GL import * # callback to make the loaded model oscilate up and down. class ModelTransformCallback(osg.NodeCallback): """callback to make the loaded model oscilate up and down.""" def __init__(self, bs): osg.NodeCallback.__init__(self) # XXX Hack to keep ourselfs alive self._self = self self._firstTime = 0 self._period = 4.0 self._range = bs.radius() * 0.5 def apply(self, node, nv): pat = node.asPositionAttitudeTransform() frameStamp = nv.getFrameStamp() if pat and frameStamp: if self._firstTime == 0: self._firstTime = frameStamp.getReferenceTime() phase = (frameStamp.getReferenceTime() - self._firstTime) / self._period phase -= math.floor(phase) phase *= (2.0 * osg.PI) rotation = osg.Quat() rotation.makeRotate(phase, 1.0, 1.0, 1.0) pat.setAttitude(rotation) pat.setPosition(osg.Vec3(0.0, 0.0, math.sin(phase)) * self._range) # must traverse self.traverse(node,nv) def createLights(bb,rootStateSet): lightGroup = osg.Group() modelSize = bb.radius() # create a spot light. myLight1 = osg.Light() myLight1.setLightNum(0) myLight1.setPosition(osg.Vec4(bb.corner(4),1.0)) myLight1.setAmbient(osg.Vec4(1.0,0.0,0.0,1.0)) myLight1.setDiffuse(osg.Vec4(1.0,0.0,0.0,1.0)) myLight1.setSpotCutoff(20.0) myLight1.setSpotExponent(50.0) myLight1.setDirection(osg.Vec3(1.0,1.0,-1.0)) lightS1 = osg.LightSource() lightS1.setLight(myLight1) lightS1.setLocalStateSetModes(osg.StateAttribute.ON); lightS1.setStateSetModes(rootStateSet,osg.StateAttribute.ON) lightGroup.addChild(lightS1) # create a local light. myLight2 = osg.Light() myLight2.setLightNum(1) myLight2.setPosition(osg.Vec4(0.0,0.0,0.0,1.0)) myLight2.setAmbient(osg.Vec4(0.0,1.0,1.0,1.0)) myLight2.setDiffuse(osg.Vec4(0.0,1.0,1.0,1.0)) myLight2.setConstantAttenuation(1.0) myLight2.setLinearAttenuation(2.0/modelSize) myLight2.setQuadraticAttenuation(2.0/osg.square(modelSize)) lightS2 = osg.LightSource() lightS2.setLight(myLight2) lightS2.setLocalStateSetModes(osg.StateAttribute.ON); lightS2.setStateSetModes(rootStateSet,osg.StateAttribute.ON) mt = osg.MatrixTransform() if 1: # set up the animation path animationPath = osg.AnimationPath() animationPath.insert(0.0,osg.AnimationPath.ControlPoint(bb.corner(0))) animationPath.insert(1.0,osg.AnimationPath.ControlPoint(bb.corner(1))) animationPath.insert(2.0,osg.AnimationPath.ControlPoint(bb.corner(2))) animationPath.insert(3.0,osg.AnimationPath.ControlPoint(bb.corner(3))) animationPath.insert(4.0,osg.AnimationPath.ControlPoint(bb.corner(4))) animationPath.insert(5.0,osg.AnimationPath.ControlPoint(bb.corner(5))) animationPath.insert(6.0,osg.AnimationPath.ControlPoint(bb.corner(6))) animationPath.insert(7.0,osg.AnimationPath.ControlPoint(bb.corner(7))) animationPath.insert(8.0,osg.AnimationPath.ControlPoint(bb.corner(0))) animationPath.setLoopMode(osg.AnimationPath.SWING) mt.setUpdateCallback(osg.AnimationPathCallback(animationPath)) # create marker for point light. marker = osg.Geometry() vertices = osg.Vec3Array() vertices.push_back(osg.Vec3(0.0,0.0,0.0)) marker.setVertexArray(vertices) marker.addPrimitiveSet(osg.DrawArrays(GL_POINTS,0,1)) stateset = osg.StateSet() point = osg.Point() point.setSize(4.0) stateset.setAttribute(point) marker.setStateSet(stateset) markerGeode = osg.Geode() markerGeode.addDrawable(marker) mt.addChild(lightS2) mt.addChild(markerGeode) lightGroup.addChild(mt) return lightGroup def createWall(v1, v2, v3, stateset): # create a drawable for occluder. geom = osg.Geometry() geom.setStateSet(stateset) noXSteps = 100 noYSteps = 100 coords = osg.Vec3Array() coords.reserve(noXSteps*noYSteps) dx = (v2-v1)/(noXSteps-1.0) dy = (v3-v1)/(noYSteps-1.0) vRowStart = v1 for row in range(noYSteps): # XXX Make a copy of vRowStart, v = vRowStart just makes a reference v = osg.Vec3(vRowStart[0], vRowStart[1], vRowStart[2]) for col in range(noXSteps): coords.push_back(v) v += dx vRowStart+=dy geom.setVertexArray(coords) colors = osg.Vec4Array(1) colors[0].set(1.0,1.0,1.0,1.0) geom.setColorArray(colors) geom.setColorBinding(osg.Geometry.BIND_OVERALL) for row in range(noYSteps -1): quadstrip = osg.DrawElementsUShort(osg.PrimitiveSet.QUAD_STRIP) quadstrip.reserve(noXSteps*2) for col in range(noXSteps): quadstrip.push_back((row+1)*noXSteps+col) quadstrip.push_back(row*noXSteps+col) geom.addPrimitiveSet(quadstrip) # create the normals. osgUtil.SmoothingVisitor.smooth(geom) return geom def createRoom(loadedModel): # default scale for this model. bs = osg.BoundingSphere(osg.Vec3(0.0,0.0,0.0),1.0) root = osg.Group() if (loadedModel): loaded_bs = loadedModel.getBound() pat = osg.PositionAttitudeTransform() pat.setPivotPoint(loaded_bs.center()) pat.setUpdateCallback(ModelTransformCallback(loaded_bs)) pat.addChild(loadedModel) bs = pat.getBound() root.addChild(pat) bs.radius(bs.radius() * 1.5 ) # create a bounding box, which we'll use to size the room. bb = osg.BoundingBox() bb.expandBy(bs) # create statesets. rootStateSet = osg.StateSet() root.setStateSet(rootStateSet) wall = osg.StateSet() wall.setMode(GL_CULL_FACE,osg.StateAttribute.ON) floor = osg.StateSet() floor.setMode(GL_CULL_FACE,osg.StateAttribute.ON) roof = osg.StateSet() roof.setMode(GL_CULL_FACE,osg.StateAttribute.ON) geode = osg.Geode() # create front side. geode.addDrawable(createWall(bb.corner(0), bb.corner(4), bb.corner(1), wall)) # right side geode.addDrawable(createWall(bb.corner(1), bb.corner(5), bb.corner(3), wall)) # left side geode.addDrawable(createWall(bb.corner(2), bb.corner(6), bb.corner(0), wall)) # back side geode.addDrawable(createWall(bb.corner(3), bb.corner(7), bb.corner(2), wall)) # floor geode.addDrawable(createWall(bb.corner(0), bb.corner(1), bb.corner(2), floor)) # roof geode.addDrawable(createWall(bb.corner(6), bb.corner(7), bb.corner(4), roof)) root.addChild(geode) root.addChild(createLights(bb,rootStateSet)) return root 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 use of OpenGL vertex lighting.") arguments.getApplicationUsage().setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...") arguments.getApplicationUsage().addCommandLineOption("-h or --help","Display this information") # 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 # load the nodes from the commandline arguments. loadedModel = osgDB.readNodeFiles(arguments) # create a room made of foor walls, a floor, a roof, and swinging light fitting. rootnode = createRoom(loadedModel) # run optimization over the scene graph optimzer = osgUtil.Optimizer() optimzer.optimize(rootnode) # add a viewport to the viewer and attach the scene graph. viewer.setSceneData( rootnode ) # create the windows and run the threads. viewer.realize() # set all the sceneview's up so that their left and right add cull masks are set up. for scene_handler in viewer.getSceneHandlerList(): # switch off small feature culling to prevent the light points from being culled. sceneview = scene_handler.getSceneView() sceneview.setCullingMode( sceneview.getCullingMode() & ~osg.CullStack.SMALL_FEATURE_CULLING) 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)