#!/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 __future__ import generators import sys import math from PyOSG import Producer from PyOSG import osg from PyOSG import osgDB from PyOSG import osgGA from PyOSG import osgUtil from PyOSG import osgProducer #import OpenGL.GLUT from math import * """ This demos uses the illustrates how to creates the various different types of geometry that the osg::Geometry class can represent. This demos uses the OpenGL red books diagram of different OpenGL Primitives as a template for all the equivilant OpenSceneGraph Primitives. The OpenSceneGraph wraps OpenGL very thinly so uses all the same enum and naming conventions. The coordinate data is also wrapped around OpenGL's vertex arrays and draw arrays/elements calls. Familarity with OpenGL will help understand the the osg::Geometry class which encapsulate all this, or if you havn't learnt OpenGL yet, learning osg::Geometry will help you understand how OpenGL works! The osg::Geometry class "is a" subclass of osg::Drawable base class, so is an object that provides a draw method for drawing objects in the scene. osg::Geometry contains all the vertex, normal color and texture coordate arrays required to specify the coordinates of your objects, and the primtives join these coordinates together as the points, lines or surfaces that you will see rendered on your screen. This demo is split into two functions, the createScene() function which creates the scene graph with the various primitives in it, and the main() which sets up a basic viewer window and adds to the it the scene generated by createScene(). """ # Simulate the vertex iterator, see LINE_STRIP implementation def iterator(lst): for item in lst: yield item class NormalPrint(osg.TriangleFunctor): def apply(self, v1, v2, v3): normal = (v2 - v1) ^ (v3 - v2) normal.normalize() print "\t(" + str(v1) + ") (" + str(v2) + ") (" + str(v3) + ") " + ") normal (" + str(normal) + ")" def printTriangles(name, drawable): """decompose Drawable primtives into triangles, print out these triangles and computed normals.""" print name tf = NormalPrint() drawable.accept(tf) print def createScene(): """create the Geode (Geometry Node) to contain all our osg::Geometry objects.""" geode = osg.Geode() # follows are seperate blocks for creating POINTS, LINES, LINE_STRIP, LINE_LOOP, POLYGON, QUADS, # QUAD_STRIP, TRIANGLES, TRIANGLE_STRIP and TRIANGLE_FAN primtives. A image of these primtives # are provided in the distribution : OpenSceneGraph-Data/Images/primtives.gif. # create POINTS if 1: # create Geometry object to store all the vetices and points primtive. pointsGeom = osg.Geometry() # create a Vec3Array and add to it all my coordinates. # Like all the *Array variants (see include/osg/Array) , Vec3Array is derivied from both osg::Array # and std::vector<>. osg::Array's are reference counted and hence sharable, # which std::vector<> provides all the convinience, flexibility and robustness # of the most popular of all STL containers. vertices = osg.Vec3Array() vertices.push_back(osg.Vec3(-1.02168, -2.15188e-09, 0.885735)) vertices.push_back(osg.Vec3(-0.976368, -2.15188e-09, 0.832179)) vertices.push_back(osg.Vec3(-0.873376, 9.18133e-09, 0.832179)) vertices.push_back(osg.Vec3(-0.836299, -2.15188e-09, 0.885735)) vertices.push_back(osg.Vec3(-0.790982, 9.18133e-09, 0.959889)) # pass the created vertex array to the points geometry object. pointsGeom.setVertexArray(vertices) # create the color of the geometry, one single for the whole geometry. # for consitency of design even one single color must added as an element # in a color array. colors = osg.Vec4Array() # add a white color, colors take the form r,g,b,a with 0.0 off, 1.0 full on. colors.push_back(osg.Vec4(1.0, 1.0, 0.0, 1.0)) # pass the color arry to points geometry, note the binding to tell the geometry # that only use one color for the whole object. pointsGeom.setColorArray(colors) pointsGeom.setColorBinding(osg.Geometry.BIND_OVERALL) # set the normal in the same way color. normals = osg.Vec3Array() normals.push_back(osg.Vec3(0.0,-1.0,0.0)); pointsGeom.setNormalArray(normals); pointsGeom.setNormalBinding(osg.Geometry.BIND_OVERALL) # create and add a DrawArray PrimitiveSet (see include/osg/Primtive). The first # paramter passed to the DrawArrays constructor is the Primtive::Mode which # in this case is POINTS (which has the same value GL_POINTS), the second # parameter is the index position into the vertex array of the first point # to draw, and the third parameter is the number of points to draw. pointsGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.POINTS,0,vertices.size())) # add the points geomtry to the geode. geode.addDrawable(pointsGeom) # create LINES if 1: # create Geometry object to store all the vetices and lines primtive. linesGeom = osg.Geometry(); # this time we'll prealloacte the vertex array to the size we # need and then simple set them as array elements, 8 points # makes 4 line segments. vertices = osg.Vec3Array(8); vertices[0].set(-1.13704, -2.15188e-09, 0.40373) vertices[1].set(-0.856897, -2.15188e-09, 0.531441) vertices[2].set(-0.889855, -2.15188e-09, 0.444927) vertices[3].set(-0.568518, -2.15188e-09, 0.40373) vertices[4].set(-1.00933, -2.15188e-09, 0.370773) vertices[5].set(-0.716827, -2.15188e-09, 0.292498) vertices[6].set(-1.07936, 9.18133e-09, 0.317217) vertices[7].set(-0.700348, 9.18133e-09, 0.362533) # pass the created vertex array to the points geometry object. linesGeom.setVertexArray(vertices) # set the colors as before, plus using the aobve colors = osg.Vec4Array() colors.push_back(osg.Vec4(1.0,1.0,0.0,1.0)) linesGeom.setColorArray(colors) linesGeom.setColorBinding(osg.Geometry.BIND_OVERALL) # set the normal in the same way color. normals = osg.Vec3Array() normals.push_back(osg.Vec3(0.0,-1.0,0.0)); linesGeom.setNormalArray(normals); linesGeom.setNormalBinding(osg.Geometry.BIND_OVERALL) # This time we simply use primitive, and hardwire the number of coords to use # since we know up front, linesGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.LINES,0,8)); # add the points geomtry to the geode. geode.addDrawable(linesGeom) # create LINE_STRIPS if 1: # create Geometry object to store all the vetices and lines primtive. linesGeom = osg.Geometry() # this time we'll prealloacte the vertex array to the size # and then use an iterator to fill in the values, a bit perverse # but does demonstrate that we have just a standard std::vector underneath. vertices = osg.Vec3Array(5) vitr = iterator(vertices) vitr.next().set(-0.0741545, -2.15188e-09, 0.416089) vitr.next().set(0.234823, -2.15188e-09, 0.259541) vitr.next().set(0.164788, -2.15188e-09, 0.366653) vitr.next().set(-0.0288379, -2.15188e-09, 0.333695) vitr.next().set(-0.0453167, -2.15188e-09, 0.280139) # pass the created vertex array to the points geometry object. linesGeom.setVertexArray(vertices) # set the colors as before, plus using the aobve colors = osg.Vec4Array() colors.push_back(osg.Vec4(1.0,1.0,0.0,1.0)) linesGeom.setColorArray(colors) linesGeom.setColorBinding(osg.Geometry.BIND_OVERALL) # set the normal in the same way color. normals = osg.Vec3Array() normals.push_back(osg.Vec3(0.0,-1.0,0.0)); linesGeom.setNormalArray(normals); linesGeom.setNormalBinding(osg.Geometry.BIND_OVERALL) # This time we simply use primitive, and hardwire the number of coords to use # since we know up front, linesGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.LINE_STRIP,0,5)) # add the points geomtry to the geode. geode.addDrawable(linesGeom) # create LINE_LOOP if 1: # create Geometry object to store all the vetices and lines primtive. linesGeom = osg.Geometry() # this time we'll a tuple to initilize the vertices. myCoords = ( (0.741546, -2.15188e-09, 0.453167), (0.840418, -2.15188e-09, 0.304858), (1.12468, -2.15188e-09, 0.300738), (1.03816, 9.18133e-09, 0.453167), (0.968129, -2.15188e-09, 0.337815), (0.869256, -2.15188e-09, 0.531441) ) # pass the created vertex array to the points geometry object. linesGeom.setVertexArray(myCoords) # set the colors as before, plus using the aobve colors = osg.Vec4Array() colors.push_back(osg.Vec4(1.0,1.0,0.0,1.0)) linesGeom.setColorArray(colors) linesGeom.setColorBinding(osg.Geometry.BIND_OVERALL) # set the normal in the same way color. normals = osg.Vec3Array() normals.push_back(osg.Vec3(0.0,-1.0,0.0)); linesGeom.setNormalArray(normals); linesGeom.setNormalBinding(osg.Geometry.BIND_OVERALL) # This time we simply use primitive, and hardwire the number of coords to use # since we know up front, linesGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.LINE_LOOP,0,len(myCoords))) # add the points geomtry to the geode. geode.addDrawable(linesGeom) # now we'll stop creating separate normal and color arrays # since we are using the same values all the time, we'll just # share the same ColorArray and NormalArrays.. # set the colors as before, use a ref_ptr rather than just # standard C pointer, as that in the case of it not being # assigned it will still be cleaned up automatically. shared_colors = osg.Vec4Array() shared_colors.push_back(osg.Vec4(1.0, 1.0, 0.0, 1.0)) # same trick for shared normal. shared_normals = osg.Vec3Array() shared_normals.push_back(osg.Vec3(0.0, -1.0, 0.0)) # Note on vertex ordering. # While the OpenGL diagram should vertices specified in a clockwise direction # in reality you need to specify coords for polygons into a anticlockwise direction # for their front face to be pointing towards your, get this wrong and you could # find back face culling removing the wrong faces of your models. The OpenGL diagram # is just plain wrong, but its nice diagram so we'll keep it for now! # create POLYGON if 1: # create Geometry object to store all the vetices and lines primtive. polyGeom = osg.Geometry() # this time we'll a C arrays to initilize the vertices. # note, anticlockwsie ordering. # note II, OpenGL polygons must be convex plan polygons, otherwise # undefined results will occur. If you have concave polygons or ones # that cross over themselves then use the osgUtil::Tesselator to fix # the polygons into a set of valid polygons. myCoords = ( (-1.0464, 0.0, -0.193626), (-1.0258, 0.0, -0.26778), (-0.807461, 0.0, -0.181267), (-0.766264, 0.0, -0.0576758), (-0.980488, 0.0, -0.094753) ) # pass the created vertex array to the points geometry object. polyGeom.setVertexArray(myCoords) # use the shared color array. polyGeom.setColorArray(shared_colors) polyGeom.setColorBinding(osg.Geometry.BIND_OVERALL) # use the shared normal array. polyGeom.setNormalArray(shared_normals) polyGeom.setNormalBinding(osg.Geometry.BIND_OVERALL) # This time we simply use primitive, and hardwire the number of coords to use # since we know up front, polyGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.POLYGON, 0, len(myCoords))) printTriangles("Polygon", polyGeom) # add the points geomtry to the geode. geode.addDrawable(polyGeom) # create QUADS if 1: # create Geometry object to store all the vetices and lines primtive. polyGeom = osg.Geometry() # note, anticlockwsie ordering. myCoords = ( (0.0247182, 0.0, -0.156548), (0.0247182, 0.0, -0.00823939), (-0.160668, 0.0, -0.0453167), (-0.222464, 0.0, -0.13183), (0.238942, 0.0, -0.251302), (0.333696, 0.0, 0.0329576), (0.164788, 0.0, -0.0453167), (0.13595, 0.0, -0.255421) ) # pass the created vertex array to the points geometry object. polyGeom.setVertexArray(myCoords) # use the shared color array. polyGeom.setColorArray(shared_colors) polyGeom.setColorBinding(osg.Geometry.BIND_OVERALL) # use the shared normal array. polyGeom.setNormalArray(shared_normals) polyGeom.setNormalBinding(osg.Geometry.BIND_OVERALL) # This time we simply use primitive, and hardwire the number of coords to use # since we know up front, polyGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.QUADS, 0, len(myCoords))) printTriangles("Quads", polyGeom) # add the points geomtry to the geode. geode.addDrawable(polyGeom) # create QUAD_STRIP if 1: # create Geometry object to store all the vetices and lines primtive. polyGeom = osg.Geometry() # note, first coord at top, second at bottom, reverse to that buggy OpenGL image.. myCoords = ( (0.733306, -2.15188e-09, -0.0741545), (0.758024, -2.15188e-09, -0.205985), (0.885735, -2.15188e-09, -0.0576757), (0.885735, -2.15188e-09, -0.214224), (0.964009, 9.18133e-09, -0.0370773), (1.0464, 9.18133e-09, -0.173027), (1.11232, -2.15188e-09, 0.0123591), (1.12468, 9.18133e-09, -0.164788) ) # pass the created vertex array to the points geometry object. polyGeom.setVertexArray(myCoords) # use the shared color array. polyGeom.setColorArray(shared_colors) polyGeom.setColorBinding(osg.Geometry.BIND_OVERALL) # use the shared normal array. polyGeom.setNormalArray(shared_normals) polyGeom.setNormalBinding(osg.Geometry.BIND_OVERALL) # This time we simply use primitive, and hardwire the number of coords to use # since we know up front, polyGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.QUAD_STRIP, 0, len(myCoords))) printTriangles("Quads strip", polyGeom) # add the points geomtry to the geode. geode.addDrawable(polyGeom) # create TRIANGLES, TRIANGLE_STRIP and TRIANGLE_FAN all in one Geometry/ if 1: # create Geometry object to store all the vetices and lines primtive. polyGeom = osg.Geometry() # note, first coord at top, second at bottom, reverse to that buggy OpenGL image.. myCoords = ( # TRIANGLES 6 vertices, v0..v5 # note in aniclockwise order. (-1.12056, -2.15188e-09, -0.840418), (-0.95165, -2.15188e-09, -0.840418), (-1.11644, 9.18133e-09, -0.716827), # note in aniclockwise order. (-0.840418, 9.18133e-09, -0.778623), (-0.622074, 9.18133e-09, -0.613835), (-1.067, 9.18133e-09, -0.609715), # TRIANGLE STRIP 6 vertices, v6..v11 # note defined top point first, # then anticlockwise for the next two points, # then alternating to bottom there after. (-0.160668, -2.15188e-09, -0.531441), (-0.160668, -2.15188e-09, -0.749785), (0.0617955, 9.18133e-09, -0.531441), (0.168908, -2.15188e-09, -0.753905), (0.238942, -2.15188e-09, -0.531441), (0.280139, -2.15188e-09, -0.823939), # TRIANGLE FAN 5 vertices, v12..v16 # note defined in anticlockwsie order. (0.844538, 9.18133e-09, -0.712708), (1.0258, 9.18133e-09, -0.799221), (1.03816, -2.15188e-09, -0.692109), (0.988727, 9.18133e-09, -0.568518), (0.840418, -2.15188e-09, -0.506723), ) # pass the created vertex array to the points geometry object. polyGeom.setVertexArray(myCoords) # use the shared color array. polyGeom.setColorArray(shared_colors) polyGeom.setColorBinding(osg.Geometry.BIND_OVERALL) # use the shared normal array. polyGeom.setNormalArray(shared_normals) polyGeom.setNormalBinding(osg.Geometry.BIND_OVERALL) # This time we simply use primitive, and hardwire the number of coords to use # since we know up front, polyGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.TRIANGLES, 0, 6)) polyGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.TRIANGLE_STRIP, 6, 6)) polyGeom.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.TRIANGLE_FAN, 12, 5)) # polygon stipple stateSet = osg.StateSet() polyGeom.setStateSet(stateSet) polygonStipple = osg.PolygonStipple() stateSet.setAttributeAndModes(polygonStipple,osg.StateAttribute.OVERRIDE|osg.StateAttribute.ON) printTriangles("Triangles/Strip/Fan",polyGeom); # add the points geomtry to the geode. geode.addDrawable(polyGeom) return geode class MyTransformCallback(osg.NodeCallback): """define a node callback to animation a transform as a cycle along the y axis, between 0 and 2.0.""" def __init__(self, angularVelocity): # make sure that you initialize the base class! osg.NodeCallback.__init__(self) self.angular_velocity = angularVelocity def apply(self, node, nv): transform = node.asMatrixTransform() if nv and transform and nv.getFrameStamp(): time = nv.getFrameStamp().getReferenceTime() transform.setMatrix(osg.Matrix.translate(0.0, 1.0 + cos(time * self.angular_velocity), 0.0)) # must continue subgraph traversal. self.traverse(node, nv) def createBackground(): """ we'll create a texture mapped quad to sit behind the Geometry """ image = osgDB.readImageFile("Images/primitives.gif") if not image: return None # create Geometry object to store all the vetices and lines primtive. polyGeom = osg.Geometry() # note, anticlockwsie ordering. # pass the created vertex array to the points geometry object. polyGeom.setVertexArray(( (-1.22908,0.0,1.0), (-1.22908,0.0,-1.0), (1.22908,0.0,-1.0), (1.22908,0.0,1.0) )) 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) # set the normal in the same way color. normals = osg.Vec3Array() normals.push_back(osg.Vec3(0.0,-1.0,0.0)); polyGeom.setNormalArray(normals); polyGeom.setNormalBinding(osg.Geometry.BIND_OVERALL); # pass the created tex coord array to the points geometry object, # and use it to set texture unit 0. polyGeom.setTexCoordArray(0, ((0,1), (0,0), (1,0), (1,1)) ); # Theere are three variants of the DrawElements osg::PrimitiveSet, UByteDrawElements which # contains unsigned char indicies, UShortDrawElements which contains unsigned short indices, # and UIntDrawElements whcih contains ... unsigned int indices. # The first parameter to DrawElements is p = osg.DrawElementsUShort(osg.PrimitiveSet.QUADS, (0,1,2,3)) polyGeom.addPrimitiveSet(p) # new we need to add the texture to the Drawable, we do so by creating a # StateSet to contain the Texture2D StateAttribute. stateset = osg.StateSet(); # set up the texture. texture = osg.Texture2D() texture.setImage(image) stateset.setTextureAttributeAndModes(0, texture, osg.StateAttribute.ON) polyGeom.setStateSet(stateset) # create the Geode (Geometry Node) to contain all our osg::Geometry objects. geode = osg.Geode() # add the points geomtry to the geode. geode.addDrawable(polyGeom) # create a tranform to move the background back and forward with. transform = osg.MatrixTransform() transform.setUpdateCallback(MyTransformCallback(1.0)) transform.addChild(geode) return transform 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 how to create osg::Geometry.") 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. root = osg.Group() root.addChild(createScene()) root.addChild(createBackground()) # add model to viewer viewer.setSceneData(root) # 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)