#!/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. # from PyOSG import Producer from PyOSG import osg from PyOSG import osgGA from PyOSG import osgDB from PyOSG import osgUtil from PyOSG import osgProducer from PyOSG import osgText import OpenGL import math s_ProfessionalServices = False class MyBillboardTransform( osg.PositionAttitudeTransform): def __init__(self): osg.PositionAttitudeTransform.__init__(self) self._axis = osg.Vec3(0.0, 0.0, 1.0) self._normal = osg.Vec3(0.0, -1.0, 0.0) def computeLocalToWorldMatrix(self, matrix, nv): billboardRotation = osg.Quat() cullvisitor = osgUtil.asCullVisitor(nv) if cullvisitor: eyevector = cullvisitor.getEyeLocal() - self.getPosition() eyevector.normalize() side = self._axis^self._normal side.normalize() angle = math.atan2(eyevector*self._normal,eyevector*side) billboardRotation.makeRotate(osg.PI_2-angle,self._axis) matrix.preMult(osg.Matrix.translate(-self.getPivotPoint())* osg.Matrix.rotate(self.getAttitude())* osg.Matrix.rotate(billboardRotation)* osg.Matrix.translate(self.getPosition())) # XXX del cullvisitor return True def setAxis(self, axis): self._axis = axis def setNormal(self, normal): self._normal = normal def createWing(left, nose, right, chordRatio, color): geom = osg.Geometry() normal = (nose-right)^(left-nose) normal.normalize() left_to_right = right-left mid = (right+left)*0.5 mid_to_nose = (nose-mid)*chordRatio*0.5 vertices = osg.Vec3Array() vertices.push_back(left) #vertices.push_back(mid+mid_to_nose) noSteps = 40 for i in range(1, noSteps): ratio = float(i) / float(noSteps) vertices.push_back(left + left_to_right*ratio + mid_to_nose* (math.cos((ratio-0.5)*osg.PI*2.0)+1.0)) vertices.push_back(right) vertices.push_back(nose) geom.setVertexArray(vertices) normals = osg.Vec3Array() normals.push_back(normal) geom.setNormalArray(normals) geom.setNormalBinding(osg.Geometry.BIND_OVERALL) colors = osg.Vec4Array() colors.push_back(color) geom.setColorArray(colors) geom.setColorBinding(osg.Geometry.BIND_OVERALL) geom.addPrimitiveSet( osg.DrawArrays(OpenGL.GL.GL_POLYGON,0,vertices.getNumElements()) ) tesselator = osgUtil.Tesselator() tesselator.retesselatePolygons(geom) return geom def createTextBelow(bb): geode = osg.Geode() font = "fonts/arial.ttf" text = osgText.Text() text.setFont(font) text.setFontSize(64,64) text.setAlignment(osgText.Text.CENTER_CENTER) text.setAxisAlignment(osgText.Text.XZ_PLANE) text.setPosition(bb.center()-osg.Vec3(0.0,0.0,(bb.zMax()-bb.zMin()))) text.setColor(osg.Vec4(0.37,0.48,0.67,1.0)) text.setText("OpenSceneGraph") geode.addDrawable( text ) return geode def createTextLeft(bb): geode = osg.Geode() stateset = geode.getOrCreateStateSet() stateset.setMode(OpenGL.GL.GL_LIGHTING,osg.StateAttribute.OFF) #std.string font("fonts/times.ttf") font = "fonts/arial.ttf" text = osgText.Text() text.setFont(font) text.setFontResolution(110,120) text.setAlignment(osgText.Text.RIGHT_CENTER) text.setAxisAlignment(osgText.Text.XZ_PLANE) text.setCharacterSize((bb.zMax()-bb.zMin())*1.0) text.setPosition(bb.center()-osg.Vec3((bb.xMax()-bb.xMin()),-(bb.yMax()-bb.yMin())*0.5,(bb.zMax()-bb.zMin())*0.1)) # text.setColor(osg.Vec4(0.37,0.48,0.67,1.0)) # Neil's orignal OSG colour text.setColor(osg.Vec4(0.20,0.45,0.60,1.0)) # OGL logo colour text.setText("OpenSceneGraph") geode.addDrawable( text ) if s_ProfessionalServices: #subscript = osgText.Text(osgText.TextureFont(font,45)) subscript = osgText.Text() subscript.setFont(font) subscript.setText("Professional Services") subscript.setAlignment(osgText.Text.RIGHT_CENTER) subscript.setAxisAlignment(osgText.Text.XZ_PLANE) subscript.setPosition(bb.center()-osg.Vec3((bb.xMax()-bb.xMin())*4.3,-(bb.yMax()-bb.yMin())*0.5,(bb.zMax()-bb.zMin())*0.6)) subscript.setColor(osg.Vec4(0.0,0.0,0.0,1.0)); # black geode.addDrawable( subscript ) return geode def createGlobe(bb, ratio): xform = osg.MatrixTransform() xform.setUpdateCallback(osgUtil.TransformCallback(bb.center(),osg.Vec3(0.0,0.0,1.0),osg.inDegrees(30.0))) geode = osg.Geode() stateset = geode.getOrCreateStateSet() # image = osgDB.readImageFile("Images/land_shallow_topo_2048.jpg", False) image = osgDB.readImageFile("Images/land_shallow_topo_2048.jpg") if image: texture = osg.Texture2D() texture.setImage(image) texture.setMaxAnisotropy(8) stateset.setTextureAttributeAndModes(0,texture,osg.StateAttribute.ON) material = osg.Material() stateset.setAttribute(material); # the globe geode.addDrawable(osg.ShapeDrawable(osg.Sphere(bb.center(),bb.radius()*ratio))) xform.addChild(geode) return xform def createBox(bb, chordRatio): geode = osg.Geode() white = osg.Vec4(1.0,1.0,1.0,1.0) # front faces. geode.addDrawable(createWing(bb.corner(4),bb.corner(6),bb.corner(7),chordRatio,white)) geode.addDrawable(createWing(bb.corner(7),bb.corner(5),bb.corner(4),chordRatio,white)) geode.addDrawable(createWing(bb.corner(4),bb.corner(5),bb.corner(1),chordRatio,white)) geode.addDrawable(createWing(bb.corner(1),bb.corner(0),bb.corner(4),chordRatio,white)) geode.addDrawable(createWing(bb.corner(1),bb.corner(5),bb.corner(7),chordRatio,white)) geode.addDrawable(createWing(bb.corner(7),bb.corner(3),bb.corner(1),chordRatio,white)) # back faces geode.addDrawable(createWing(bb.corner(2),bb.corner(0),bb.corner(1),chordRatio,white)) geode.addDrawable(createWing(bb.corner(1),bb.corner(3),bb.corner(2),chordRatio,white)) geode.addDrawable(createWing(bb.corner(2),bb.corner(3),bb.corner(7),chordRatio,white)) geode.addDrawable(createWing(bb.corner(7),bb.corner(6),bb.corner(2),chordRatio,white)) geode.addDrawable(createWing(bb.corner(2),bb.corner(6),bb.corner(4),chordRatio,white)) geode.addDrawable(createWing(bb.corner(4),bb.corner(0),bb.corner(2),chordRatio,white)) return geode def createBoxNo5( bb, chordRatio): geode = osg.Geode() white = osg.Vec4(1.0,1.0,1.0,1.0) # front faces. geode.addDrawable(createWing(bb.corner(4),bb.corner(6),bb.corner(7),chordRatio,white)) geode.addDrawable(createWing(bb.corner(1),bb.corner(0),bb.corner(4),chordRatio,white)) geode.addDrawable(createWing(bb.corner(7),bb.corner(3),bb.corner(1),chordRatio,white)) # back faces geode.addDrawable(createWing(bb.corner(2),bb.corner(0),bb.corner(1),chordRatio,white)) geode.addDrawable(createWing(bb.corner(1),bb.corner(3),bb.corner(2),chordRatio,white)) geode.addDrawable(createWing(bb.corner(2),bb.corner(3),bb.corner(7),chordRatio,white)) geode.addDrawable(createWing(bb.corner(7),bb.corner(6),bb.corner(2),chordRatio,white)) geode.addDrawable(createWing(bb.corner(2),bb.corner(6),bb.corner(4),chordRatio,white)) geode.addDrawable(createWing(bb.corner(4),bb.corner(0),bb.corner(2),chordRatio,white)) return geode def createBoxNo5No2(bb, chordRatio): geode = osg.Geode() # red = osg.Vec4(1.0,0.0,0.0,1.0) # green = osg.Vec4(0.0,1.0,0.0,1.0) # blue = osg.Vec4(0.0,0.0,1.0,1.0) red = osg.Vec4(1.0,0.12,0.06,1.0) green = osg.Vec4(0.21,0.48,0.03,1.0) blue = osg.Vec4(0.20,0.45,0.60,1.0) # front faces. geode.addDrawable(createWing(bb.corner(4),bb.corner(6),bb.corner(7),chordRatio,red)) geode.addDrawable(createWing(bb.corner(1),bb.corner(0),bb.corner(4),chordRatio,green)) geode.addDrawable(createWing(bb.corner(7),bb.corner(3),bb.corner(1),chordRatio,blue)) return geode def createBackdrop(corner,top,right): geom = osg.Geometry() normal = (corner-top)^(right-corner) normal.normalize() vertices = osg.Vec3Array() vertices.push_back(top) vertices.push_back(corner) vertices.push_back(right) vertices.push_back(right+(top-corner)) geom.setVertexArray(vertices) normals = osg.Vec3Array() normals.push_back(normal) geom.setNormalArray(normals) geom.setNormalBinding(osg.Geometry.BIND_OVERALL) colors = osg.Vec4Array() colors.push_back(osg.Vec4(1.0,1.0,1.0,1.0)) geom.setColorArray(colors) geom.setColorBinding(osg.Geometry.BIND_OVERALL) geom.addPrimitiveSet(osg.DrawArrays(OpenGL.GL.GL_QUADS,0,vertices.getNumElements())) geode = osg.Geode() geode.addDrawable(geom) return geode def createLogo(): bb = osg.BoundingBox(osg.Vec3(0.0,0.0,0.0),osg.Vec3(100.0,100.0,100.0)) chordRatio = 0.5 sphereRatio = 0.6 # create a group to hold the whole model. logo_group = osg.Group() r1 = osg.Quat() r2 = osg.Quat() r1.makeRotate(-osg.inDegrees(45.0),0.0,0.0,1.0) r2.makeRotate(osg.inDegrees(45.0),1.0,0.0,0.0) xform = MyBillboardTransform() xform.setPivotPoint(bb.center()) xform.setPosition(bb.center()) xform.setAttitude(r1*r2) # create a transform to orientate the box and globe. # xform = osg.MatrixTransform() # xform.setDataVariance(osg.Object.STATIC) # xform.setMatrix(osg.Matrix.translate(-bb.center())* # osg.Matrix.rotate(-osg.inDegrees(45.0),0.0,0.0,1.0)* # osg.Matrix.rotate(osg.inDegrees(45.0),1.0,0.0,0.0)* # osg.Matrix.translate(bb.center())) # add the box and globe to it. #xform.addChild(createBox(bb,chordRatio)) #xform.addChild(createBoxNo5(bb,chordRatio)) xform.addChild(createBoxNo5No2(bb,chordRatio)) # add the transform to the group. logo_group.addChild(xform) # logo_group.addChild(createBoxNo5No2(bb, chordRatio)) logo_group.addChild(createGlobe(bb,sphereRatio)) # add the text to the group. #group.addChild(createTextBelow(bb)) logo_group.addChild(createTextLeft(bb)) # create the backdrop to render the shadow to. corner = osg.Vec3(-900.0,150.0,-100.0) top = osg.Vec3(0.0,0.0,300.0); top += corner right = osg.Vec3(1100.0,0.0,0.0); right += corner backdrop = osg.Group() backdrop.addChild(createBackdrop(corner,top,right)) backdrop = osg.ClearNode() backdrop.setClearColor(osg.Vec4(1.0,1.0,1.0,1.0)) #lightPosition = osg.Vec3(-500.0,-2500.0,500.0) #scene = createShadowedScene(logo_group,backdrop,lightPosition,0.0f,0) scene = osg.Group() stateset = scene.getOrCreateStateSet() stateset.setMode(OpenGL.GL.GL_LIGHTING,osg.StateAttribute.OVERRIDE|osg.StateAttribute.OFF) scene.addChild(logo_group) scene.addChild(backdrop) # XXX osgDB.writeNodeFile(scene,"test.osg") 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().setDescription(arguments.getApplicationName()+" is the example which demonstrates both text, animation and billboard via custom transform to create the OpenSceneGraph logo..") arguments.getApplicationUsage().setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...") arguments.getApplicationUsage().addCommandLineOption("-h or --help","Display this information") arguments.getApplicationUsage().addCommandLineOption("ps","Render the Professional Services logo") # 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 while (arguments.read("ps")): global s_ProfessionalServices s_ProfessionalServices = 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 node = createLogo() # add model to viewer. viewer.setSceneData( node ) # 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 sys main(sys.argv)