from PyOSG import * from OpenGL.GL import * class Rect: def __init__(self, x0 = 0, y0 = 0, x1 = 0, y1 = 0): self.x0 = x0 self.y0 = y0 self.x1 = x1 self.y1 = y1 def width(self): return self.x1 - self.x0 def heigh(self): return self.y1 - self.y0 class Frame(osg.Geode): def __init__(self): osg.Geode.__init__(self) self.bgcolor_ = osg.Vec4(0.5, 0.5, 0.5, 1.0) self.rect_ = Rect(0, 0, 100, 100) self.caption_ = "Frame" def getCaption(self): return self.caption_ def setCaption(self, caption): self.caption_ = caption def getBackgroundColor(self): return self.bgcolor_ def setBackgroundColor(self, bgcolor): self.bgcolor_ = bgcolor def getRect(self): return self.rect_ def setRect(self, rect): self.rect_ = rect def build_quad(self, rect, color, shadow = True, z = 0): shadow_space = 8 shadow_size = 10 geo = osg.Geometry() vx = osg.Vec3Array() vx.push_back(osg.Vec3(rect.x0, rect.y0, z)) vx.push_back(osg.Vec3(rect.x1, rect.y0, z)) vx.push_back(osg.Vec3(rect.x1, rect.y1, z)) vx.push_back(osg.Vec3(rect.x0, rect.y1, z)) if shadow: vx.push_back(osg.Vec3(rect.x0+shadow_space, rect.y0-shadow_size, z)) vx.push_back(osg.Vec3(rect.x1+shadow_size, rect.y0-shadow_size, z)) vx.push_back(osg.Vec3(rect.x1, rect.y0, z)) vx.push_back(osg.Vec3(rect.x0+shadow_space, rect.y0, z)) vx.push_back(osg.Vec3(rect.x1, rect.y1-shadow_space, z)) vx.push_back(osg.Vec3(rect.x1, rect.y0, z)) vx.push_back(osg.Vec3(rect.x1+shadow_size, rect.y0-shadow_size, z)) vx.push_back(osg.Vec3(rect.x1+shadow_size, rect.y1-shadow_space, z)) geo.setVertexArray(vx) clr = osg.Vec4Array() clr.push_back(color) clr.push_back(color) clr.push_back(color) clr.push_back(color) if shadow: alpha = color.w() * 0.5 black = osg.Vec3(0, 0, 0) clr.push_back(osg.Vec4(black, 0)) clr.push_back(osg.Vec4(black, 0)) clr.push_back(osg.Vec4(black, alpha)) clr.push_back(osg.Vec4(black, alpha)) clr.push_back(osg.Vec4(black, alpha)) clr.push_back(osg.Vec4(black, alpha)) clr.push_back(osg.Vec4(black, 0)) clr.push_back(osg.Vec4(black, 0)) geo.setColorArray(clr) geo.setColorBinding(osg.Geometry.BIND_PER_VERTEX) geo.addPrimitiveSet(osg.DrawArrays(GL_QUADS, 0, (shadow and 12 or 4))) return geo def rebuild(self): zPos = -0.1 self.removeDrawable(0, self.getNumDrawables()) self.addDrawable(self.build_quad(self.rect_, self.bgcolor_)) self.addDrawable(self.build_quad(Rect(self.rect_.x0 + 4, self.rect_.y1 - 24, self.rect_.x1 - 4, self.rect_.y1 - 4), osg.Vec4(0, 0, 0, self.bgcolor_.w()), False, zPos)) caption_text = osgText.Text() caption_text.setText(self.caption_) caption_text.setColor(osg.Vec4(1, 1, 1, 1)) caption_text.setAlignment(osgText.Text.CENTER_CENTER) caption_text.setFont("fonts/arial.ttf") caption_text.setCharacterSize(16) caption_text.setFontResolution(16, 16) caption_text.setPosition(osg.Vec3((self.rect_.x0 + self.rect_.x1) / 2, self.rect_.y1 - 15, zPos*2.0)) self.addDrawable(caption_text) self.rebuild_client_area(Rect(self.rect_.x0 + 4, self.rect_.y0 + 4, self.rect_.x1 - 4, self.rect_.y1 - 28)) def rebuild_client_area(self, rect = None): pass class RotateCallback(osg.NodeCallback): def __init__(self): osg.NodeCallback.__init__(self) self.enabled_ = True def apply(self, node, nv): xform = node.asMatrixTransform() if xform and self.enabled_: t = nv.getFrameStamp().getReferenceTime() xform.setMatrix(osg.Matrix.rotate(t, osg.Vec3(0, 0, 1))) self.traverse(node, nv) # yes, I know global variables are not good things in C++ # but in this case it is useful... :-P rotate_cb = None # XXX further implementation class KeyboardHandler(osgGA.GUIEventHandler): def __init__(self, ep): osgGA.GUIEventHandler.__init__(self) self.ep_ = ep self.self_ = self # Create Circular reference so we don't get deleted def handle(self, ea, aa): if ea.getEventType() == osgGA.GUIEventAdapter.KEYDOWN: if ea.getKey() == osgGA.GUIEventAdapter.KEY_Right: self.ep_.setEffectIndex(self.ep_.getEffectIndex()+1) return True if ea.getKey() == osgGA.GUIEventAdapter.KEY_Left: self.ep_.setEffectIndex(self.ep_.getEffectIndex()-1) return True if ea.getKey() == osgGA.GUIEventAdapter.KEY_Return: self.ep_.setNodeMask(not self.ep_.getNodeMask()) return True if ea.getKey() == osgGA.GUIEventAdapter.KEY_Delete or \ ea.getKey() == osgGA.GUIEventAdapter.KEY_BackSpace: self.ep_.setEffectsEnabled(not self.ep_.getEffectsEnabled()) return True if ea.getKey() == ord('x'): osgDB.writeNodeFile(self.ep_.getRoot(), "osgfx_model.osg") print 'written nodes to "osgfx_model.osg"\n' return True if ea.getKey() == ord('r'): rotate_cb.enabled_ = not rotate_cb.enabled_ return True return False class EffectPanel(Frame): def __init__(self): Frame.__init__(self) self.selected_fx_ = -1 self.effects_ = [] self.fxen_ = True self.root_ = osg.Group() self.scene_ = None self.hints_color_ = osg.Vec4(0.75, 0.75, 0.75, 1.0) self.name_color_ = osg.Vec4(1, 1, 1, 1) self.desc_color_ = osg.Vec4(1, 1, 0.7, 1) self.setBackgroundColor(osg.Vec4(0.3, 0.1, 0.15, 0.75)) print "INFO: available osgFX effects:" inst = osgFX.Registry.instance() emap = inst.getEffectMap() # emap = osgFX.Registry.instance().getEffectMap() for key, value in emap.iteritems(): print "INFO: \t" + key effect = value.cloneType() self.effects_.append(effect) print "INFO: " + str(len(emap)) + " effect(s) ready." if len(self.effects_) is not 0: self.selected_fx_ = 0 def getRoot(self): return self.root_ def setRoot(self, node): self.root_ def getScene(self): return self.scene_ def setScene(self, node): self.scene_ = node def getEffectsEnabled(self): return self.fxen_ def setEffectsEnabled(self, v): self.fxen_ = v if self.getSelectedEffect(): self.getSelectedEffect().setEnabled(self.fxen_) def getEffectIndex(self): return self.selected_fx_ def setEffectIndex(self, i): if i >= len(self.effects_): i = 0 if i < 0: i = len(self.effects_)-1 self.selected_fx_ = i self.rebuild() def getSelectedEffect(self): if self.selected_fx_ >= 0 and self.selected_fx_ < len(self.effects_): return self.effects_[self.selected_fx_] return None def rebuild_client_area(self, client_rect): zPos = -0.1 # note from Robert, was 0.1, but now must be -0.1 to keep text visible??#!? due # to some other change in the OSG not tracked down yet... arial = osgText.readFontFile("fonts/arial.ttf") hints = osgText.Text() hints.setFont(arial) hints.setColor(self.hints_color_) hints.setAlignment(osgText.Text.CENTER_BOTTOM) hints.setCharacterSize(13) hints.setFontResolution(13, 13) hints.setPosition(osg.Vec3((client_rect.x0+client_rect.x1)/2, client_rect.y0 + 4, zPos)) hints.setText(" show/hide this panel previous effect next effect enable/disable effects 'x' save to file 'r' rotate/stop") self.addDrawable(hints) effect_name = "No Effect Selected" effect_description = "" if self.selected_fx_ >= 0 and self.selected_fx_ < len(self.effects_): effect_name = self.effects_[self.selected_fx_].effectName() author_name = self.effects_[self.selected_fx_].effectAuthor() if len(author_name): effect_description = author_name = "AUTHOR: " + self.effects_[self.selected_fx_].effectAuthor() + "\n\n" effect_description += "DESCRIPTION:\n" + self.effects_[self.selected_fx_].effectDescription() if self.scene_ and self.root_: self.root_.removeChild(0, self.root_.getNumChildren()) effect = self.effects_[self.selected_fx_] effect.setEnabled(self.fxen_) effect.removeChild(0, effect.getNumChildren()) effect.addChild(self.scene_) effect.setUpDemo() self.root_.addChild(effect) ename = osgText.Text() ename.setFont(arial) ename.setColor(self.name_color_) ename.setAlignment(osgText.Text.CENTER_TOP) ename.setCharacterSize(32) ename.setFontResolution(32, 32) ename.setPosition(osg.Vec3((client_rect.x0 + client_rect.x1) / 2, client_rect.y1 - 22, zPos)) ename.setText(effect_name) self.addDrawable(ename) edesc = osgText.Text() edesc.setMaximumWidth(client_rect.width() - 16) edesc.setFont(arial) edesc.setColor(self.desc_color_) edesc.setAlignment(osgText.Text.LEFT_TOP) edesc.setCharacterSize(16) edesc.setFontResolution(16, 16) edesc.setPosition(osg.Vec3(client_rect.x0 + 8, client_rect.y1 - 60, zPos)) edesc.setText(effect_description) self.addDrawable(edesc) def build_hud_base(root): proj = osg.Projection(osg.Matrix.ortho2D(0, 1024, 0, 768)) proj.setCullingActive(False) root.addChild(proj) xform = osg.MatrixTransform(osg.Matrix.identity()) xform.setReferenceFrame(osg.Transform.ABSOLUTE_RF) proj.addChild(xform) ss = xform.getOrCreateStateSet() ss.setRenderBinDetails(100, "RenderBin") ss.setMode(GL_LIGHTING, osg.StateAttribute.OFF) ss.setMode(GL_DEPTH_TEST, osg.StateAttribute.OFF) bf = osg.BlendFunc() ss.setAttributeAndModes(bf) # return xform.take() return xform def build_gui(root): hud = build_hud_base(root) effect_panel = EffectPanel() effect_panel.setCaption("osgFX Effect Browser") effect_panel.setRect(Rect(20, 20, 1000, 280)) hud.addChild(effect_panel) # return effect_panel.take() return effect_panel def build_world(root, scene, viewer): effect_panel = build_gui(root) effect_panel.setScene(scene) effect_panel.rebuild() viewer.getEventHandlerList().push_front(KeyboardHandler(effect_panel)) root.addChild(effect_panel.getRoot()) 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 a simple browser that allows you to apply osgFX effects to models interactively.") arguments.getApplicationUsage().setCommandLineUsage(arguments.getApplicationName() + " [options] filename ...") arguments.getApplicationUsage().addCommandLineOption("-h or --help", "Display this information") arguments.getApplicationUsage().addKeyboardMouseBinding("Left", "Apply previous effect") arguments.getApplicationUsage().addKeyboardMouseBinding("Right", "Apply next effect") arguments.getApplicationUsage().addKeyboardMouseBinding("Del", "Enable or disable osgFX") arguments.getApplicationUsage().addKeyboardMouseBinding("Return", "Show or hide the effect information panel") arguments.getApplicationUsage().addKeyboardMouseBinding("x", "Save the scene graph with current effect applied") # 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 if arguments.argc() <= 1: arguments.getApplicationUsage().write(osg.ApplicationUsage.COMMAND_LINE_OPTION) 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: print arguments.getApplicationName() + ": No data loaded" return 1 end_tick = timer.tick() print "Time to load = " + str(timer.delta_s(start_tick,end_tick)) # optimize the scene graph, remove rendundent nodes and state etc. optimizer = osgUtil.Optimizer() optimizer.optimize(loadedModel) # set up a transform to rotate the model xform = osg.MatrixTransform() global rotate_cb rotate_cb = RotateCallback() xform.setUpdateCallback(rotate_cb) xform.addChild(loadedModel) light = osg.Light() light.setLightNum(0) light.setDiffuse(osg.Vec4(1, 1, 1, 1)) light.setSpecular(osg.Vec4(1, 1, 0.8, 1)) light.setAmbient(osg.Vec4(0.2, 0.2, 0.2, 0.2)) light.setPosition(osg.Vec4(1, -1, 1, 0)) root = osg.LightSource() root.setLight(light) root.setLocalStateSetModes() build_world(root, xform, viewer) # set the scene to render 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__": import sys main(sys.argv)