#!/usr/bin/env python from PyOSG import * from OpenGL.GL import * from math import sin, cos """ osgtesselator - this tesselator is an extension of the basic one - rather than tesselating individual polygons, we tesselate the entire geode with multiple contours. allows for overlapping contours etc. the tesselator has member fuinctions setTesselationType(osgUtil.Tesselator.TESS_TYPE_xxx) tscx.setBoundaryOnly(bool) tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_xxx) for winding rules: See the red book chap 13. """ class tesselateDemoGeometry(osg.Geometry_base, osgUtil.Tesselator_base): # We add the Tesselator to the geometry because we want to access the # tesselatable contours again; you can apply a tesselator to a Geometry # to produce exactly a required tesselation once only, and then # the contours could be discarded since the geometry does not need to be retesselated. def __init__(self): osg.Geometry_base.__init__(self) osgUtil.Tesselator.__init__(self) self._self = self self.value = 10 def makePolsTwo (): # an example of using current geometry contours to create next tesselation # this polygon disappears once the contour rules make no polygons. gtess= tesselateDemoGeometry() coords = osg.Vec3Array() nrms = osg.Vec3Array() tcs = osg.Vec2Array() nrm = osg.Vec3(0,-1,0) quadstrip = [ [ 1900.0, 1130.0, 0.0 ], [ 2100.0, 1130.0, 0.0 ], [ 1900.0, 1350.0, 0.0 ], [ 1950.0, 1350.0, 0.0 ], [ 1900.0, 1550.0, 0.0 ], [ 2000.0, 1550.0, 0.0 ], [ 1900.0, 1750.0, 0.0 ], [ 2400.0, 1750.0, 0.0 ] ] innerquadstrip = [ [ 2000.0, 1230.0, 0.0 ], [ 2050.0, 1230.0, 0.0 ], [ 1920.0, 1350.0, 0.0 ], [ 1940.0, 1350.0, 0.0 ], [ 1920.0, 1550.0, 0.0 ], [ 1980.0, 1550.0, 0.0 ], [ 2000.0, 1650.0, 0.0 ], [ 2400.0, 1650.0, 0.0 ] ] # add one large quadstrip for i in range(8): coords.push_back(osg.Vec3(quadstrip[i][0],quadstrip[i][2],quadstrip[i][1])) tcs.push_back(osg.Vec2(quadstrip[i][0],quadstrip[i][1])/200.0) nrms.push_back(nrm) for i in range(8): coords.push_back(osg.Vec3(innerquadstrip[i][0],innerquadstrip[i][2],innerquadstrip[i][1])) tcs.push_back(osg.Vec2(innerquadstrip[i][0],innerquadstrip[i][1])/200.0) nrms.push_back(nrm) gtess.setVertexArray(coords) gtess.setNormalArray(nrms) gtess.setNormalBinding(osg.Geometry.BIND_PER_VERTEX) gtess.setTexCoordArray(0,tcs) # demonstrate that the tesselator makes textured tesselations stateset = osg.StateSet() image = osgDB.readImageFile("Cubemap_snow/posy.jpg") if image: texture = osg.Texture2D() texture.setImage(image) stateset.setTextureAttributeAndModes(0,texture,osg.StateAttribute.ON) gtess.setStateSet( stateset ) nstart=0 # The derived class tesselateDemoGeometry retains the original contours for re-use. gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.TRIANGLE_STRIP,nstart,8));nstart+=8 gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.TRIANGLE_STRIP,nstart,8));nstart+=8 gtess.setTesselationType(osgUtil.Tesselator.TESS_TYPE_GEOMETRY) gtess.setBoundaryOnly(True) gtess.setWindingType( osgUtil.Tesselator.TESS_WINDING_ABS_GEQ_TWO) # so that first change in wind type makes the commonest tesselation - ODD. return gtess def makeSideWall (xpos): # demonstrate making a rectangular 'wall' with 2 holes in it. gtess= osg.Geometry() coords = osg.Vec3Array() nrms = osg.Vec3Array() tcs = osg.Vec2Array() nrm = osg.Vec3(-1,0,0) # front wall wall = [ [ 1130.0, 0.0 ], [ 1130.0, 300.0 ], [ 1340.0,300.0 ], [ 1340.0,0.0 ] ] gtess.setVertexArray(coords) gtess.setNormalArray(nrms) gtess.setNormalBinding(osg.Geometry.BIND_PER_VERTEX) gtess.setTexCoordArray(0,tcs) for i in range(4): coords.push_back(osg.Vec3(xpos,wall[i][1],wall[i][0])) tcs.push_back(osg.Vec2(wall[i][1],wall[i][0])/100.0) nrms.push_back(nrm) nstart=0 gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.QUADS,nstart,4));nstart+=4 for i in range(24): # make an ellipse hole y=150+50*cos(i*2*osg.PI/24.0) z=1300+30* sin(i*2*osg.PI/24.0) coords.push_back(osg.Vec3(xpos,y,z)) tcs.push_back(osg.Vec2(y,z)/100.0) nrms.push_back(nrm) gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.POLYGON,nstart,24));nstart+=24 for i in range(5): # make a pentagonal hole y=150+50*cos(i*2*osg.PI/5.0) z=1200+40* sin(i*2*osg.PI/5.0) coords.push_back(osg.Vec3(xpos,y,z)) tcs.push_back(osg.Vec2(y,z)/100.0) nrms.push_back(nrm) gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.POLYGON,nstart,5));nstart+=5 # demonstrate that the tesselator makes textured tesselations stateset = osg.StateSet() image = osgDB.readImageFile("Cubemap_snow/posx.jpg") if image: texture = osg.Texture2D() texture.setImage(image) stateset.setTextureAttributeAndModes(0,texture,osg.StateAttribute.ON) gtess.setStateSet( stateset ) tscx=osgUtil.Tesselator() # the v1.2 multi-contour tesselator. # we use the geometry primitives to describe the contours which are tesselated. # Winding odd means leave hole in surface where there are 2,4,6... contours circling the point. tscx.setTesselationType(osgUtil.Tesselator.TESS_TYPE_GEOMETRY) tscx.setBoundaryOnly(False) tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_ODD) # so that first change in wind type makes the commonest tesselation - ODD. tscx.retesselatePolygons(gtess) return gtess def makeFrontWall (zpos): # an example of using one tesselation to make a 'house' wall # describe the wall as a pentagon, then door & 4 windows are further contours # tesselate the set of contours to make a 'house wall' from the Boolean-like operations. nstart=0 # counts vertices used for the geometry primitives gtess= osg.Geometry() coords = osg.Vec3Array() nrms = osg.Vec3Array() tcs = osg.Vec2Array() nrm = osg.Vec3(0,-1,0) # front wall wall = [ [ 2200.0, 1130.0 ], [ 2600.0, 1130.0 ], [ 2600.0, 1340.0 ], [ 2400.0, 1440.0 ], [ 2200.0, 1340.0 ] ] door = [ [ 2360.0, 1130.0 ], [ 2440.0, 1130.0 ], [ 2440.0, 1230.0 ], [ 2360.0, 1230.0 ] ] windows = [ [ 2240.0, 1180.0 ], [ 2330.0, 1180.0 ], [ 2330.0, 1220.0 ], [ 2240.0, 1220.0 ], [ 2460.0, 1180.0 ], [ 2560.0, 1180.0 ], [ 2560.0, 1220.0 ], [ 2460.0, 1220.0 ], [ 2240.0, 1280.0 ], [ 2330.0, 1280.0 ], [ 2330.0, 1320.0 ], [ 2240.0, 1320.0 ], [ 2460.0, 1280.0 ], [ 2560.0, 1280.0 ], [ 2560.0, 1320.0 ], [ 2460.0, 1320.0 ] ] gtess.setVertexArray(coords) gtess.setNormalArray(nrms) gtess.setNormalBinding(osg.Geometry.BIND_PER_VERTEX) gtess.setTexCoordArray(0,tcs) # add one large pentagon -the wall for i in range(5): coords.push_back(osg.Vec3(wall[i][0],zpos,wall[i][1])) tcs.push_back(osg.Vec2(wall[i][0],wall[i][1])/100.0) nrms.push_back(nrm) gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.POLYGON,nstart,5));nstart+=5 # add first hole, a door for i in range(4): coords.push_back(osg.Vec3(door[i][0],zpos,door[i][1])) tcs.push_back(osg.Vec2(door[i][0],door[i][1])/100.0) nrms.push_back(nrm) # and windows for i in range(16): coords.push_back(osg.Vec3(windows[i][0],zpos,windows[i][1])) tcs.push_back(osg.Vec2(windows[i][0],windows[i][1])/100.0) nrms.push_back(nrm) gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.QUADS,nstart,20));nstart+=20 # demonstrate that the tesselator makes textured tesselations stateset = osg.StateSet() image = osgDB.readImageFile("Cubemap_snow/posy.jpg") if image: texture = osg.Texture2D() texture.setImage(image) stateset.setTextureAttributeAndModes(0,texture,osg.StateAttribute.ON) gtess.setStateSet( stateset ) # We use a Tesselator to produce the tesselation required once only # and the contours are discarded. tscx=osgUtil.Tesselator() # the v1.2 multi-contour tesselator. tscx.setTesselationType(osgUtil.Tesselator.TESS_TYPE_GEOMETRY) tscx.setBoundaryOnly(False) tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_ODD); # so that first change in wind type makes the commonest tesselation - ODD. tscx.retesselatePolygons(gtess) return gtess def makeHouse() : gd = osg.Geode() gd.addDrawable(makeFrontWall(0.0)) gd.addDrawable(makeFrontWall(300.0)) gd.addDrawable(makeSideWall(2200.0)) gd.addDrawable(makeSideWall(2600.0)) return gd def makePols(): gtess= tesselateDemoGeometry() coords = osg.Vec3Array() nrms = osg.Vec3Array() tcs = osg.Vec2Array() nrm = osg.Vec3(0,-1,0) # coordinates from red book code but shifted by 1000 & 2000 for alternate tesselatory things. rects = [ [ 50.0, 50.0, 0.0 ], [ 300.0, 50.0, 0.0 ], [ 300.0, 300.0, 0.0 ], [ 50.0, 300.0, 0.0 ], [ 100.0, 100.0, 0.0 ], [ 250.0, 100.0, 0.0 ], [ 250.0, 250.0, 0.0 ], [ 100.0, 250.0, 0.0 ], [ 150.0, 150.0, 0.0 ], [ 200.0, 150.0, 0.0 ], [ 200.0, 200.0, 0.0 ], [ 150.0, 200.0, 0.0 ] ] rectsMidanti = [ # the centre 2 contours are traversed opposite order to outer contour. [ 1050.0, 50.0, 0.0 ], [ 1300.0, 50.0, 0.0 ], [ 1300.0, 300.0, 0.0 ], [ 1050.0, 300.0, 0.0 ], [ 1250.0, 100.0, 0.0 ], [ 1100.0, 100.0, 0.0 ], [ 1100.0, 250.0, 0.0 ], [ 1250.0, 250.0, 0.0 ], [ 1200.0, 150.0, 0.0 ], [ 1150.0, 150.0, 0.0 ], [ 1150.0, 200.0, 0.0 ], [ 1200.0, 200.0, 0.0 ] ] spiral = [ # shift by 1000 nb the order of vertices is reversed from that of the red book [ 3400.0, 250.0, 0.0 ], [ 3400.0, 50.0, 0.0 ], [ 3050.0, 50.0, 0.0 ], [ 3050.0, 400.0, 0.0 ], [ 3350.0, 400.0, 0.0 ], [ 3350.0, 100.0, 0.0 ], [ 3100.0, 100.0, 0.0 ], [ 3100.0, 350.0, 0.0 ], [ 3300.0, 350.0, 0.0 ], [ 3300.0, 150.0, 0.0 ], [ 3150.0, 150.0, 0.0 ], [ 3150.0, 300.0, 0.0 ], [ 3250.0, 300.0, 0.0 ], [ 3250.0, 200.0, 0.0 ], [ 3200.0, 200.0, 0.0 ], [ 3200.0, 250.0, 0.0 ] ] quad1 = [ # shift by 2000 for next 3 things [ 2050.0, 150.0, 0.0 ], [ 2350.0, 150.0, 0.0 ], [ 2350.0, 200.0, 0.0 ], [ 2050.0, 200.0, 0.0 ] ] quad2 = [ [ 2100.0, 100.0, 0.0 ], [ 2300.0, 100.0, 0.0 ], [ 2300.0, 350.0, 0.0 ], [ 2100.0, 350.0, 0.0 ] ] tri = [ [ 2200.0, 50.0, 0.0 ], [ 2250.0, 300.0, 0.0 ], [ 2150.0, 300.0, 0.0 ] ] quad3 = [ [ 100.0, 1100.0, 0.0 ], [ 1300.0, 1100.0, 0.0 ], [ 1300.0, 2350.0, 0.0 ], [ 100.0, 2350.0, 0.0] ] quadstrip = [ [ 900.0, 1130.0, 0.0 ], [ 1100.0, 1130.0, 0.0 ], [ 900.0, 1350.0, 0.0 ], [ 950.0, 1350.0, 0.0 ], [ 900.0, 1550.0, 0.0 ], [ 1000.0, 1550.0, 0.0 ], [ 900.0, 1750.0, 0.0 ], [ 1400.0, 1750.0, 0.0 ] ] for i in range(12): coords.push_back(osg.Vec3(rects[i][0],rects[i][2],rects[i][1])) tcs.push_back(osg.Vec2(rects[i][0],rects[i][1])/200.0) nrms.push_back(nrm) for i in range(12): coords.push_back(osg.Vec3(rectsMidanti[i][0],rectsMidanti[i][2],rectsMidanti[i][1])) tcs.push_back(osg.Vec2(rectsMidanti[i][0],rectsMidanti[i][1])/200.0) nrms.push_back(nrm) for i in range(16): # and reverse spiral to make same as that of red book ch 11 coords.push_back(osg.Vec3(spiral[15-i][0],spiral[15-i][2],spiral[15-i][1])) tcs.push_back(osg.Vec2(spiral[15-i][0],spiral[15-i][1])/200.0) nrms.push_back(nrm) for i in range(4): coords.push_back(osg.Vec3(quad1[i][0],quad1[i][2],quad1[i][1])) tcs.push_back(osg.Vec2(quad1[i][0],quad1[i][1])/200.0) nrms.push_back(nrm) for i in range(4): coords.push_back(osg.Vec3(quad2[i][0],quad2[i][2],quad2[i][1])) tcs.push_back(osg.Vec2(quad2[i][0],quad2[i][1])/200.0) nrms.push_back(nrm) for i in range(3): coords.push_back(osg.Vec3(tri[i][0],tri[i][2],tri[i][1])) tcs.push_back(osg.Vec2(tri[i][0],tri[i][1])/200.0) nrms.push_back(nrm) # add one large quad with multiple holes for i in range(4): coords.push_back(osg.Vec3(quad3[i][0],quad3[i][2],quad3[i][1])) tcs.push_back(osg.Vec2(quad3[i][0],quad3[i][1])/200.0) nrms.push_back(nrm) if 1: centre = osg.Vec3(300,0,1500) for i in range(18): rim=centre+osg.Vec3(-cos(osg.DegreesToRadians(i*20.0)),0.0,sin(osg.DegreesToRadians(i*20.0)))*150.0 coords.push_back(rim) tcs.push_back(osg.Vec2(rim.x(),rim.z())/200.0) nrms.push_back(nrm) if 1: centre = osg.Vec3(400,0,1800) for i in range(18): rim=centre+osg.Vec3(-cos(osg.DegreesToRadians(i*15.0)),0.0,sin(osg.DegreesToRadians(i*15.0)))*250.0 coords.push_back(rim) tcs.push_back(osg.Vec2(rim.x(),rim.z())/200.0) nrms.push_back(nrm) if 1: centre = osg.Vec3(600,0,1400) for i in range(18): rim=centre+osg.Vec3(-cos(osg.DegreesToRadians(i*12.0)),0.0,sin(osg.DegreesToRadians(i*12.0)))*250.0 coords.push_back(rim) tcs.push_back(osg.Vec2(rim.x(),rim.z())/200.0) nrms.push_back(nrm) # add one large quadstrip for i in range(8): coords.push_back(osg.Vec3(quadstrip[i][0],quadstrip[i][2],quadstrip[i][1])) tcs.push_back(osg.Vec2(quadstrip[i][0],quadstrip[i][1])/200.0) nrms.push_back(nrm) gtess.setVertexArray(coords) gtess.setNormalArray(nrms) gtess.setNormalBinding(osg.Geometry.BIND_PER_VERTEX) gtess.setTexCoordArray(0,tcs) # demonstrate that the tesselator makes textured tesselations stateset = osg.StateSet() image = osgDB.readImageFile("Cubemap_snow/posz.jpg") if image: texture = osg.Texture2D() texture.setImage(image) stateset.setTextureAttributeAndModes(0,texture,osg.StateAttribute.ON) gtess.setStateSet( stateset ) nstart=0 # the contours accepoted are polygons; quads & tris. Trifans can bve added later. gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.QUADS,nstart,12));nstart+=12 gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.QUADS,nstart,12));nstart+=12 gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.POLYGON,nstart,16));nstart+=16 gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.QUADS,nstart,4));nstart+=4 gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.QUADS,nstart,4));nstart+=4 gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.TRIANGLES,nstart,3));nstart+=3 # A rectabngle with multiple holes gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.LINE_LOOP,nstart,4));nstart+=4 gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.TRIANGLE_FAN,nstart,18));nstart+=18 gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.POLYGON,nstart,18));nstart+=18 gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.POLYGON,nstart,18));nstart+=18 # test for quad strip gtess.addPrimitiveSet(osg.DrawArrays(osg.PrimitiveSet.TRIANGLE_STRIP,nstart,8));nstart+=8 # We need to access the tesselatable contours again to demonstrate all types of tesselation. # I could add the Tesselator to the geometry as userdata, but here # I use the derived tesselateDemoGeometry to hold both the drawable geode and the original contours. gtess.setTesselationType(osgUtil.Tesselator.TESS_TYPE_GEOMETRY) gtess.setBoundaryOnly(True) gtess.setWindingType( osgUtil.Tesselator.TESS_WINDING_ABS_GEQ_TWO); # so that first change in wind type makes the commonest tesselation - ODD. return gtess def createHUD(): # add a string reporting the type of winding rule tesselation applied geode = osg.Geode() timesFont = "fonts/arial.ttf" # turn lighting off for the text and disable depth test to ensure its always ontop. stateset = geode.getOrCreateStateSet() stateset.setMode(GL_LIGHTING,osg.StateAttribute.OFF) # Disable depth test, and make sure that the hud is drawn after everything # else so that it always appears ontop. stateset.setMode(GL_DEPTH_TEST,osg.StateAttribute.OFF) stateset.setRenderBinDetails(11,"RenderBin") position = osg.Vec3(150.0,900.0,0.0) delta = osg.Vec3(0.0,-30.0,0.0) if 1: text = osgText.Text() geode.addDrawable( text ) text.setFont(timesFont) text.setPosition(position) text.setText("Tesselation example - no tesselation (use 'W' wireframe to visualise)") text.setColor(osg.Vec4(1.0,1.0,0.8,1.0)) position += delta if 1: text = osgText.Text() geode.addDrawable( text ) text.setFont(timesFont) text.setPosition(position) text.setText("Press 'n' to use an alternative tesselation.") # create the hud. modelview_abs = osg.MatrixTransform() modelview_abs.setReferenceFrame(osg.Transform.ABSOLUTE_RF) modelview_abs.setMatrix(osg.Matrix.identity()) modelview_abs.addChild(geode) projection = osg.Projection() projection.setMatrix(osg.Matrix.ortho2D(0,1280,0,1024)) projection.addChild(modelview_abs) return projection def makeTesselateExample (): grp=osg.Group() gd=osg.Geode() # gd.addDrawable(makePols()) x = makePols() x.a = 10 print 'x.self = ', x.self() print "x = ", x, isinstance(x, tesselateDemoGeometry), isinstance(x, osgUtil.Tesselator), isinstance(x, osg.Geometry) print x.getBoundaryOnly() gd.addDrawable(x) y = gd.getDrawable(0) print "type of y = ", type(y), y y = gd.getDrawable(0).self() print "y.a = ", y.a print super(osgUtil.Tesselator, y) print "y = ", y, isinstance(y, tesselateDemoGeometry), isinstance(y, osgUtil.Tesselator), isinstance(y, osg.Geometry) print "y is of type: ", type(y) print "y isinstance of tess...", isinstance(y, tesselateDemoGeometry) print y.getBoundaryOnly() gd.addDrawable(makePolsTwo()) grp.addChild(gd) grp.addChild(makeHouse()) return grp class setTesselateVisitor(osg.NodeVisitor): # searches a loaded model tree for tesselatable geometries. # used with any database model which has a renderGroup (Geode) named 'tesselate' # or you can force a type of tess with special names or a sub-class of Geode could have extra information # of course you can use any name to detect what is to be tesselated! # all the polygons within the specific node are deemed to be contours, so # any tesselation can be requested. def __init__(self): osg.NodeVisitor.__init__(self, osg.NodeVisitor.TRAVERSE_ALL_CHILDREN) def apply(self, geode): if geode.getName()[:9] == "tesselate": for i in range(geode.getNumDrawables()): geom = geode.getDrawable(i).asGeometry() if geom: tscx=osgUtil.Tesselator() if tscx: tscx.setTesselationType(osgUtil.Tesselator.TESS_TYPE_GEOMETRY) if geode.getName() == "tesselate": # add a tesselator so that this geom is retesselated when N is pressed tscx.setBoundaryOnly(True) tscx.setWindingType(osgUtil.Tesselator.TESS_WINDING_ABS_GEQ_TWO); # so that first change in wind type makes the commonest tesselation - ODD. geom.setUserData(tscx) elif geode.getName()== "tesselate odd": # OR you can just apply the tesselator once only, using these different types tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_ODD); # commonest tesselation - ODD. tscx.retesselatePolygons(geom) elif geode.getName()== "tesselate odd bound": tscx.setBoundaryOnly(True) tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_ODD); # tesselation - ODD, only show boundary. tscx.retesselatePolygons(geom) elif geode.getName()== "tesselate positive": tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_POSITIVE); # tesselation - pos. tscx.retesselatePolygons(geom) elif geode.getName()== "tesselate positive bound": tscx.setBoundaryOnly(True) tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_POSITIVE) tscx.retesselatePolygons(geom) elif geode.getName()== "tesselate negative": tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_NEGATIVE) tscx.retesselatePolygons(geom) elif geode.getName()== "tesselate negative bound": tscx.setBoundaryOnly(True) tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_NEGATIVE) tscx.retesselatePolygons(geom) elif geode.getName()== "tesselate nonzero": tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_NONZERO) tscx.retesselatePolygons(geom) elif geode.getName()== "tesselate nonzero bound": tscx.setBoundaryOnly(True) tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_NONZERO) tscx.retesselatePolygons(geom) elif geode.getName()== "tesselate geq2": tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_ABS_GEQ_TWO) tscx.retesselatePolygons(geom) elif geode.getName()== "tesselate geq2 bound": tscx.setBoundaryOnly(True) tscx.setWindingType( osgUtil.Tesselator.TESS_WINDING_ABS_GEQ_TWO) tscx.retesselatePolygons(geom) class cxTesselateVisitor(osg.NodeVisitor): # special to this demo, traverses SG and finds nodes which have been tesselated # for test/demo purposes these nodes are of type tesselateDemoGeometry # but you could store the Tesselator as UserData or however you like. # the tesselator holds copies of the original contours used in the tesselation # In this visitor, I reuse the contours to make a different type of tesselation. def __init__(self): osg.NodeVisitor.__init__(self, osg.NodeVisitor.TRAVERSE_ALL_CHILDREN) self._str = "" def apply(self, node): geode = node.asGeode() if not geode: self.traverse(node) return for i in range(geode.getNumDrawables()): # geom = geode.getDrawable(i).asGeometry() geom = geode.getDrawable(i) print "geom = ", geom print "geom is of type: ", type(geom) print "geom isinstance of tess...", isinstance(geom, tesselateDemoGeometry) if isinstance(geom, osg.Geometry_base): print "getting the self of geom" s = geom.self() print "s = ", s print "s is of type: ", type(s) print "s isinstance of tess...", isinstance(s, tesselateDemoGeometry) # if isinstance(geom, tesselateDemoGeometry): if isinstance(geom, osg.Geometry_base): try: geom = geom.self() except: pass # print geom.value # geom = geom.self() print "geom.value = ", geom.value if not geom.getBoundaryOnly(): # turn on bounds only # NB this shows only the true boundary of the curves, no internal edges geom.setBoundaryOnly(True) else: # change to next type of tesselation... geom.setBoundaryOnly(False) wt = geom.getWindingType() if wt == osgUtil.Tesselator.TESS_WINDING_ODD: geom.setWindingType(osgUtil.Tesselator.TESS_WINDING_NONZERO) elif wt == osgUtil.Tesselator.TESS_WINDING_NONZERO: geom.setWindingType( osgUtil.Tesselator.TESS_WINDING_POSITIVE) elif wt == osgUtil.Tesselator.TESS_WINDING_POSITIVE: geom.setWindingType( osgUtil.Tesselator.TESS_WINDING_NEGATIVE) elif wt == osgUtil.Tesselator.TESS_WINDING_NEGATIVE: geom.setWindingType( osgUtil.Tesselator.TESS_WINDING_ABS_GEQ_TWO) elif wt == osgUtil.Tesselator.TESS_WINDING_ABS_GEQ_TWO: geom.setWindingType( osgUtil.Tesselator.TESS_WINDING_ODD) wt = geom.getWindingType() if wt == osgUtil.Tesselator.TESS_WINDING_ODD: self._str="TESS_WINDING_ODD" elif wt == osgUtil.Tesselator.TESS_WINDING_NONZERO: self._str="TESS_WINDING_NONZERO" elif wt == osgUtil.Tesselator.TESS_WINDING_POSITIVE: self._str="TESS_WINDING_POSITIVE" elif wt == osgUtil.Tesselator.TESS_WINDING_NEGATIVE: self._str="TESS_WINDING_NEGATIVE" elif wt == osgUtil.Tesselator.TESS_WINDING_ABS_GEQ_TWO: self._str="TESS_WINDING_ABS_GEQ_TWO" if geom.getBoundaryOnly(): self._str += " Boundary" geom.retesselatePolygons(geom) else: print "geom is not an instance of tesselateDemoGeometry:", geom txt = osgText.asText(geode.getDrawable(i)) if txt: ct=txt.getColor(); # pick the text to be changed by its color if ct.z()<0.9: txt.setText(self._str) self.traverse(geode) class KeyboardEventHandler(osgGA.GUIEventHandler): def __init__(self, nd): osgGA.GUIEventHandler.__init__(self) self._scene = nd self._self = self def handle(self, ea, aa): et = ea.getEventType() if et == osgGA.GUIEventAdapter.KEYDOWN: if self._scene and ea.getKey()==ord('n'): # re-tesselate the scene graph. # the same contours are re-tesselated using a method. Old contours # & tesselation type are held internally in the derived Geode class tesselateDemoGeometry. tsv = cxTesselateVisitor() self._scene.accept(tsv) return True return False def accept(self, v): v.visit(self) 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 standard OpenSceneGraph example which loads and visualises 3d models.") 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() # report any errors if they have occured when parsing the program aguments. if arguments.errors(): arguments.writeErrorMessages() return 1 timer = osg.Timer() start_tick = timer.tick() # 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: loadedModel=makeTesselateExample() else: # if there is a loaded model: # tesselate by searching for geode called tesselate & tesselate it tsv = setTesselateVisitor() loadedModel.accept(tsv) # create the hud. gload= loadedModel.asGroup() gload.addChild(createHUD()) # 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() end_tick = timer.tick() print "Time to load = ", timer.delta_s(start_tick,end_tick) optimizer = osgUtil.Optimizer() optimizer.optimize(loadedModel) # set the scene to render viewer.setSceneData(loadedModel) # add event handler for keyboard 'n' to retesselate viewer.getEventHandlerList().push_front(KeyboardEventHandler(loadedModel)) # 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__": import sys main(sys.argv)