#!/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 osg from PyOSG import osgGA import math YAW_AUTOMATICALLY_WHEN_BANKED = 0 NO_AUTOMATIC_YAW = 1 class GliderManipulator(osgGA.MatrixManipulator): def __init__(self): osgGA.MatrixManipulator.__init__(self) self._ga_t1 = None self._ga_t0 = None self._node = None self._modelScale = 0.01 self._velocity = 0.0 self._yawMode = YAW_AUTOMATICALLY_WHEN_BANKED self._eye = osg.Vec3() self._rotation = osg.Quat() self._distance = 1.0 def __del__(self): pass def className(self): return "Glider" def setByMatrix(self, matrix): self._eye = matrix.getTrans() self._rotation.set(matrix) self._distance = 1.0 def setByInverseMatrix(self, matrix): self.setByMatrix(osg.Matrix.inverse(matrix)) def getMatrix(self): return osg.Matrix.rotate(self._rotation) * osg.Matrix.translate(self._eye) def getInverseMatrix(self): return osg.Matrix.translate(-self._eye) * osg.Matrix.rotate(self._rotation.inverse()); def setNode(self, node): """ Attach a node to the manipulator. Automatically detaches previously attached node. setNode(NULL) detaches previously nodes. Is ignored by manipulators which do not require a reference model. """ self._node = node if self._node: boundingSphere=self._node.getBound() self._modelScale = boundingSphere.radius() def getNode(self): """ Return node if attached.""" return self._node def home(self, ea, us): """ Move the camera to the default position. May be ignored by manipulators if home functionality is not appropriate.""" if self._node : boundingSphere=self._node.getBound() eye = boundingSphere.center()+osg.Vec3(-boundingSphere.radius()*0.25,-boundingSphere.radius()*0.25,-boundingSphere.radius()*0.03) self.computePosition(eye, osg.Vec3(1.0,1.0,-0.1), osg.Vec3(0.0,0.0,1.0)) self._velocity = boundingSphere.radius()*0.01 us.requestRedraw() us.requestWarpPointer((ea.getXmin()+ea.getXmax())/2.0,(ea.getYmin()+ea.getYmax())/2.0) self.flushMouseEventStack() def init(self, ea, us): """ Start/restart the manipulator.""" self.flushMouseEventStack() us.requestContinuousUpdate(0) self._velocity = 0.0 if ea.getEventType != osgGA.GUIEventAdapter.RESIZE: us.requestWarpPointer((ea.getXmin()+ea.getXmax())/2.0,(ea.getYmin()+ea.getYmax())/2.0) def handle(self, ea, us): """ handle events, return true if handled, false otherwise.""" eType = ea.getEventType() if eType == osgGA.GUIEventAdapter.PUSH: self.addMouseEvent(ea) us.requestContinuousUpdate(1) if (self.calcMovement()): us.requestRedraw() return 1 elif eType == osgGA.GUIEventAdapter.RELEASE: self.addMouseEvent(ea) us.requestContinuousUpdate(1) if (self.calcMovement()): us.requestRedraw() return 1 elif eType == osgGA.GUIEventAdapter.DRAG: self.addMouseEvent(ea) us.requestContinuousUpdate(1) if (self.calcMovement()): us.requestRedraw() return 1 elif eType == osgGA.GUIEventAdapter.MOVE: self.addMouseEvent(ea) us.requestContinuousUpdate(1) if (self.calcMovement()): us.requestRedraw() return 1 elif eType == osgGA.GUIEventAdapter.KEYDOWN: if ea.getKey()==ord(' '): self.flushMouseEventStack() self.home(ea,us) us.requestRedraw() us.requestContinuousUpdate(0) return 1 elif ea.getKey()==ord('q'): self._yawMode = YAW_AUTOMATICALLY_WHEN_BANKED return 1 elif ea.getKey()==ord('a'): self._yawMode = NO_AUTOMATIC_YAW return 1 return 0 elif eType == osgGA.GUIEventAdapter.FRAME: self.addMouseEvent(ea) if (self.calcMovement()): us.requestRedraw() return 1 elif eType == osgGA.GUIEventAdapter.RESIZE: self.init(ea,us) us.requestRedraw() return 1 else: return 0 def getUsage(self, usage): usage.addKeyboardMouseBinding("Flight: Space","Reset the viewing position to home") usage.addKeyboardMouseBinding("Flight: q","Automatically yaw when banked (default)") usage.addKeyboardMouseBinding("Flight: a","No yaw when banked") def setYawControlMode(self, ycm): """ Configure the Yaw control for the Glider model.""" self._yawMode = ycm def flushMouseEventStack(self): """Reset the internal GUIEvent stack.""" # self._ga_t1 = None # self._ga_t0 = None # Messy, have to take care of the reference count explicitly # since we don't have the ref_ptr functionality in python. # Need to find a way to make it more transparent. # When we don't ref / unref, the application crashes major! if (self._ga_t1): self._ga_t1.unref() self._ga_t1 = None if (self._ga_t0): self._ga_t0.unref() self._ga_t0 = None def addMouseEvent(self, ea): """Add the current mouse GUIEvent to internal stack.""" self._ga_t1 = self._ga_t0 self._ga_t0 = ea self._ga_t0.ref() def computePosition(self, eye, lv, up): f = osg.Vec3(lv) f.normalize() s = osg.Vec3(f ^ up) s.normalize() u = osg.Vec3(s ^ f) u.normalize() rotation_matrix = osg.Matrix() rotation_matrix.set(( s[0], u[0], -f[0], 0.0, s[1], u[1], -f[1], 0.0, s[2], u[2], -f[2], 0.0, 0.0, 0.0, 0.0, 1.0)) self._eye = eye self._distance = lv.length() self._rotation.set(rotation_matrix) self._rotation = self._rotation.inverse() def calcMovement(self): """For the give mouse movement calculate the movement of the camera. Return 1 if camera has moved and a redraw is required.""" # return if less then two events have been added. if self._ga_t0 == None or self._ga_t1 ==None: return 0 dt = self._ga_t0.time()- self._ga_t1.time() if dt<0.0: print "warning dt = ", dt dt = 0.0 buttonMask = self._ga_t1.getButtonMask() if buttonMask==osgGA.GUIEventAdapter.LEFT_MOUSE_BUTTON: # pan model. self._velocity += dt*self._modelScale*0.05 elif buttonMask==osgGA.GUIEventAdapter.MIDDLE_MOUSE_BUTTON or \ buttonMask==(osgGA.GUIEventAdapter.LEFT_MOUSE_BUTTON|osgGA.GUIEventAdapter.RIGHT_MOUSE_BUTTON): self._velocity = 0.0 elif buttonMask==osgGA.GUIEventAdapter.RIGHT_MOUSE_BUTTON: self._velocity -= dt*self._modelScale*0.05 dx = self._ga_t0.getXnormalized() dy = self._ga_t0.getYnormalized() rotation_matrix = osg.Matrix() rotation_matrix.makeRotate(self._rotation) up = osg.Vec3(0.0,1.0,0.0) * rotation_matrix lv = osg.Vec3(0.0,0.0,-1.0) * rotation_matrix sv = lv^up sv.normalize() pitch = osg.inDegrees(-dy*70.0*dt) roll = osg.inDegrees(dx*60.0*dt) delta_rotate = osg.Quat() roll_rotate = osg.Quat() pitch_rotate = osg.Quat() pitch_rotate.makeRotate(pitch,sv.x(),sv.y(),sv.z()) roll_rotate.makeRotate(roll,lv.x(),lv.y(),lv.z()) delta_rotate = pitch_rotate*roll_rotate if self._yawMode==YAW_AUTOMATICALLY_WHEN_BANKED: bank = math.asin(sv._z) yaw = osg.inRadians(bank)*dt yaw_rotate = osg.Quat() yaw_rotate.makeRotate(yaw,0.0,0.0,1.0); delta_rotate = delta_rotate*yaw_rotate; lv *= (self._velocity*dt) self._eye += lv self._rotation = self._rotation * delta_rotate return 1 if __name__ == "__main__": import sys from PyOSG import * from PyOSG import osgDB from PyOSG import osgUtil from PyOSG import osgProducer viewer = osgProducer.Viewer() viewer.setUpViewer(osgProducer.Viewer.STANDARD_SETTINGS) rootnode = osgDB.readNodeFile("cow.osg") if rootnode == None: sys.exit(1) viewer.setSceneData(rootnode) pos = viewer.addCameraManipulator(GliderManipulator()) viewer.selectCameraManipulator(pos) viewer.realize() while not viewer.done(): viewer.sync() viewer.update() viewer.frame() viewer.sync() sys.exit(0)