From 8618f4d3f89700ce1fa2a40a1067e110472b7944 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 6 Aug 2004 22:22:50 +0000 Subject: [PATCH] more files from CMU --- direct/src/showbase/Audio3DManager.py | 80 + doc/.project | 11 + doc/SceneEditor/.project | 11 + doc/SceneEditor/AlignTool.py | 188 ++ doc/SceneEditor/Dirlight.egg | 575 +++++ doc/SceneEditor/MetadataPanel.py | 59 + doc/SceneEditor/Pointlight.egg | 1175 +++++++++++ doc/SceneEditor/SeSession.py | 991 +++++++++ doc/SceneEditor/SideWindow.py | 459 ++++ doc/SceneEditor/Spotlight.egg | 870 ++++++++ doc/SceneEditor/collisionWindow.py | 302 +++ doc/SceneEditor/controllerWindow.py | 697 +++++++ doc/SceneEditor/dataHolder.py | 1071 ++++++++++ doc/SceneEditor/duplicateWindow.py | 172 ++ doc/SceneEditor/lightingPanel.py | 496 +++++ doc/SceneEditor/propertyWindow.py | 1454 +++++++++++++ doc/SceneEditor/quad.py | 674 ++++++ doc/SceneEditor/sceneEditor.py | 1679 +++++++++++++++ doc/SceneEditor/seAnimPanel.py | 608 ++++++ doc/SceneEditor/seBlendAnimPanel.py | 666 ++++++ doc/SceneEditor/seCameraControl.py | 670 ++++++ doc/SceneEditor/seColorEntry.py | 51 + doc/SceneEditor/seFileSaver.py | 913 ++++++++ doc/SceneEditor/seForceGroup.py | 139 ++ doc/SceneEditor/seGeometry.py | 257 +++ doc/SceneEditor/seGrid.py | 170 ++ doc/SceneEditor/seLights.py | 632 ++++++ doc/SceneEditor/seManipulation.py | 956 +++++++++ doc/SceneEditor/seMopathRecorder.py | 2073 +++++++++++++++++++ doc/SceneEditor/seParticleEffect.py | 267 +++ doc/SceneEditor/seParticlePanel.py | 1976 ++++++++++++++++++ doc/SceneEditor/seParticles.py | 449 ++++ doc/SceneEditor/sePlacer.py | 799 +++++++ doc/SceneEditor/seSceneGraphExplorer.py | 208 ++ doc/SceneEditor/seSelection.py | 716 +++++++ doc/SceneEditor/seTree.py | 421 ++++ dtool/src/parser-inc/MainHelix.h | 9 + dtool/src/parser-inc/dllpath.h | 6 + dtool/src/parser-inc/hxcom.h | 6 + dtool/src/parser-inc/hxcomm.h | 6 + dtool/src/parser-inc/hxcore.h | 6 + dtool/src/parser-inc/hxengin.h | 6 + dtool/src/parser-inc/hxerror.h | 6 + dtool/src/parser-inc/hxfiles.h | 6 + dtool/src/parser-inc/hxtbuf.h | 6 + dtool/src/parser-inc/hxtbuff.h | 6 + dtool/src/parser-inc/hxwin.h | 6 + panda/src/helix/HelixClient.cxx | 801 +++++++ panda/src/helix/HelixClient.h | 123 ++ panda/src/helix/HelixDefs.h | 421 ++++ panda/src/helix/HxAdviseSink.cxx | 653 ++++++ panda/src/helix/HxAdviseSink.h | 97 + panda/src/helix/HxAuthenticationManager.cxx | 210 ++ panda/src/helix/HxAuthenticationManager.h | 67 + panda/src/helix/HxClientContext.cxx | 327 +++ panda/src/helix/HxClientContext.h | 88 + panda/src/helix/HxErrorSink.cxx | 229 ++ panda/src/helix/HxErrorSink.h | 77 + panda/src/helix/HxSiteSupplier.cxx | 322 +++ panda/src/helix/HxSiteSupplier.h | 67 + panda/src/helix/MainHelix.h | 74 + panda/src/helix/Sources.pp | 28 + panda/src/helix/config_helix.cxx | 36 + panda/src/helix/config_helix.h | 28 + panda/src/helix/fivemmap.cxx | 175 ++ panda/src/helix/fivemmap.h | 83 + panda/src/helix/iids.cxx | 52 + panda/src/helix/main.cpp | 62 + panda/src/helix/print.cxx | 80 + panda/src/helix/print.h | 62 + 70 files changed, 27166 insertions(+) create mode 100644 direct/src/showbase/Audio3DManager.py create mode 100644 doc/.project create mode 100644 doc/SceneEditor/.project create mode 100644 doc/SceneEditor/AlignTool.py create mode 100644 doc/SceneEditor/Dirlight.egg create mode 100644 doc/SceneEditor/MetadataPanel.py create mode 100644 doc/SceneEditor/Pointlight.egg create mode 100644 doc/SceneEditor/SeSession.py create mode 100644 doc/SceneEditor/SideWindow.py create mode 100644 doc/SceneEditor/Spotlight.egg create mode 100644 doc/SceneEditor/collisionWindow.py create mode 100644 doc/SceneEditor/controllerWindow.py create mode 100644 doc/SceneEditor/dataHolder.py create mode 100644 doc/SceneEditor/duplicateWindow.py create mode 100644 doc/SceneEditor/lightingPanel.py create mode 100644 doc/SceneEditor/propertyWindow.py create mode 100644 doc/SceneEditor/quad.py create mode 100644 doc/SceneEditor/sceneEditor.py create mode 100644 doc/SceneEditor/seAnimPanel.py create mode 100644 doc/SceneEditor/seBlendAnimPanel.py create mode 100644 doc/SceneEditor/seCameraControl.py create mode 100644 doc/SceneEditor/seColorEntry.py create mode 100644 doc/SceneEditor/seFileSaver.py create mode 100644 doc/SceneEditor/seForceGroup.py create mode 100644 doc/SceneEditor/seGeometry.py create mode 100644 doc/SceneEditor/seGrid.py create mode 100644 doc/SceneEditor/seLights.py create mode 100644 doc/SceneEditor/seManipulation.py create mode 100644 doc/SceneEditor/seMopathRecorder.py create mode 100644 doc/SceneEditor/seParticleEffect.py create mode 100644 doc/SceneEditor/seParticlePanel.py create mode 100644 doc/SceneEditor/seParticles.py create mode 100644 doc/SceneEditor/sePlacer.py create mode 100644 doc/SceneEditor/seSceneGraphExplorer.py create mode 100644 doc/SceneEditor/seSelection.py create mode 100644 doc/SceneEditor/seTree.py create mode 100644 panda/src/helix/HelixClient.cxx create mode 100644 panda/src/helix/HelixClient.h create mode 100644 panda/src/helix/HelixDefs.h create mode 100644 panda/src/helix/HxAdviseSink.cxx create mode 100644 panda/src/helix/HxAdviseSink.h create mode 100644 panda/src/helix/HxAuthenticationManager.cxx create mode 100644 panda/src/helix/HxAuthenticationManager.h create mode 100644 panda/src/helix/HxClientContext.cxx create mode 100644 panda/src/helix/HxClientContext.h create mode 100644 panda/src/helix/HxErrorSink.cxx create mode 100644 panda/src/helix/HxErrorSink.h create mode 100644 panda/src/helix/HxSiteSupplier.cxx create mode 100644 panda/src/helix/HxSiteSupplier.h create mode 100644 panda/src/helix/MainHelix.h create mode 100644 panda/src/helix/Sources.pp create mode 100644 panda/src/helix/config_helix.cxx create mode 100644 panda/src/helix/config_helix.h create mode 100644 panda/src/helix/fivemmap.cxx create mode 100644 panda/src/helix/fivemmap.h create mode 100644 panda/src/helix/iids.cxx create mode 100644 panda/src/helix/main.cpp create mode 100644 panda/src/helix/print.cxx create mode 100644 panda/src/helix/print.h diff --git a/direct/src/showbase/Audio3DManager.py b/direct/src/showbase/Audio3DManager.py new file mode 100644 index 0000000000..6f0df0011a --- /dev/null +++ b/direct/src/showbase/Audio3DManager.py @@ -0,0 +1,80 @@ +import pandac.AudioSound +import pandac.AudioManager + +class Audio3DManager: + + def __init__(self, audio_manager, listener_target = None): + self.audio_manager = audio_manager + self.listener_target = listener_target + + self.sound_dict = {} + + + def attachSoundToObject(self, sound, object): + # sound is an AudioSound + # object is any Panda object with coordinates + for known_object in self.sound_dict.keys(): + if sound_dict[known_object].count(sound): + # This sound is already attached to something + return 0 + + if not self.sound_dict.has_key(object): + self.sound_dict[object] = [] + + self.sound_dict[object].append(sound) + return 1 + + + def detachSound(self, sound): + for known_object in self.sound_dict.keys(): + if sound_dict[known_object].count(sound): + self.sound_dict[known_object].remove() + if len(sound_dict[known_object]) == 0: + # if there are no other sounds, don't track + # the object any more + del self.sound_dict[known_object] + return 1 + return 0 + + + def getSoundsOnObject(self, object): + if not self.sound_dict.has_key(object): + return [] + sound_list = [] + sound_list.extend(self.sound_dict[object]) + return sound_list + + + def attachListener(self, object): + self.listener_target = object + return 1 + + + def detachListener(self): + self.listener_target = None + return 1 + + + def update(self): + # Update the positions of all sounds based on the objects + # to which they are attached + for known_object in self.sound_dict.keys(): + tracked_sound = 0 + while tracked_sound < len(self.sound_dict[known_object]): + sound = self.sound_dict[known_object][tracked_sound] + if self.listener_target: + pos = known_object.getPos(self.listener_target) + else: + pos = known_object.getPos() + sound.set3dAttributes(pos[0], pos[1], pos[2], 0,0,0) + tracked_sound += 1 + + # Update the position of the listener based on the object + # to which it is attached + if self.listener_target: + pos = self.listener_target.getPos() + self.audio_manager.audio3dSetListenerAttributes(pos[0], pos[1], pos[2], 0,0,0, 0,1,0, 0,0,1) + else: + self.audio_manager.audio3dSetListenerAttributes(0,0,0, 0,0,0, 0,1,0, 0,0,1) + self.audio_manager.audio3dUpdate() + return 1 diff --git a/doc/.project b/doc/.project new file mode 100644 index 0000000000..29141a9be1 --- /dev/null +++ b/doc/.project @@ -0,0 +1,11 @@ + + + panda + + + + + + + + diff --git a/doc/SceneEditor/.project b/doc/SceneEditor/.project new file mode 100644 index 0000000000..390de7af63 --- /dev/null +++ b/doc/SceneEditor/.project @@ -0,0 +1,11 @@ + + + SceneEditor + + + + + + + + diff --git a/doc/SceneEditor/AlignTool.py b/doc/SceneEditor/AlignTool.py new file mode 100644 index 0000000000..84968fed0b --- /dev/null +++ b/doc/SceneEditor/AlignTool.py @@ -0,0 +1,188 @@ +################################################################# +# AlignTool.py +# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +################################################################# +from AppShell import * +from TkGlobal import * + + +class AlignTool(AppShell): + ################################################################# + # AlignTool(AppShell) + ################################################################# + appversion = '1.0' + appname = 'Align Tool' + frameWidth = 220 + frameHeight = 330 + frameIniPosX = 250 + frameIniPosY = 250 + padx = 0 + pady = 0 + + + def __init__(self, list = [], parent = None, nodePath = None, **kw): + # Keep nodePath Data + self.nodePath = nodePath + self.targetList = list + self.targetName = None + # Rename App + self.appname += (' '+self.nodePath.getName()) + # Define the megawidget options. + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + if parent == None: + self.parent = Toplevel() + AppShell.__init__(self, self.parent) + self.parent.geometry('%dx%d+%d+%d' % (self.frameWidth, self.frameHeight,self.frameIniPosX,self.frameIniPosY)) + + self.initialiseoptions(AlignTool) + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + def appInit(self): + return + + def createInterface(self): + # The interior of the toplevel panel + interior = self.interior() + mainFrame = Frame(interior) + frame = Frame(mainFrame) + self.nameBox = self.createcomponent( + 'Align Target', (), None, + Pmw.ComboBox, (frame,), + labelpos = W, label_text='Target Node:', entry_width = 20, entry_state = DISABLED, + selectioncommand = self.setTargetNode, + scrolledlist_items = self.targetList) + self.nameBox.pack(side=LEFT) + frame.pack(side=TOP, fill = X, expand = 1,pady=5) + group = Pmw.Group(mainFrame, tag_text = 'Setting') + group.pack(side=TOP, fill = 'both', expand = 1,pady=5) + groupFrame = group.interior() + # X and H checkbox + frame = Frame(groupFrame) + self.alignXVar = IntVar() + self.alignXVar.set(False) + self.alignXButton = Checkbutton( + frame, + text = ': Align X', + variable = self.alignXVar) + self.alignXButton.pack(side=LEFT, expand=False) + self.alignHVar = IntVar() + self.alignHVar.set(False) + self.alignHButton = Checkbutton( + frame, + text = ': Align H', + variable = self.alignHVar) + self.alignHButton.pack(side=RIGHT, expand=False) + frame.pack(side=TOP, fill = X, expand = 1,pady=5) + + groupFrame.pack(side=TOP, fill = 'both', expand = 1,padx=5,pady=5) + + frame = Frame(mainFrame) + Button(frame, text='Align', width = 13, command=self.Align_press).pack(side=LEFT) + Button(frame, text='OK', width = 13, command=self.ok_press).pack(side=RIGHT) + frame.pack(side=BOTTOM, fill = X, expand = 1,pady=5) + + # Y and P checkbox + frame = Frame(groupFrame) + self.alignYVar = IntVar() + self.alignYVar.set(False) + self.alignYButton = Checkbutton( + frame, + text = ': Align Y', + variable = self.alignYVar) + self.alignYButton.pack(side=LEFT, expand=False) + self.alignPVar = IntVar() + self.alignPVar.set(False) + self.alignPButton = Checkbutton( + frame, + text = ': Align P', + variable = self.alignPVar) + self.alignPButton.pack(side=RIGHT, expand=False) + frame.pack(side=TOP, fill = X, expand = 1,pady=5) + + # Z and R checkbox + frame = Frame(groupFrame) + self.alignZVar = IntVar() + self.alignZVar.set(False) + self.alignZButton = Checkbutton( + frame, + text = ': Align Z', + variable = self.alignZVar) + self.alignZButton.pack(side=LEFT, expand=False) + self.alignRVar = IntVar() + self.alignRVar.set(False) + self.alignRButton = Checkbutton( + frame, + text = ': Align R', + variable = self.alignRVar) + self.alignRButton.pack(side=RIGHT, expand=False) + frame.pack(side=TOP, fill = X, expand = 1,pady=5) + + # Scale + frame = Frame(groupFrame) + Label(frame,text='Align Scale:').pack(side=LEFT) + frame.pack(side=TOP, fill = X, expand = 1,pady=5) + frame = Frame(groupFrame) + self.alignSXVar = IntVar() + self.alignSXVar.set(False) + self.alignSXButton = Checkbutton( + frame, + text = ': X', + variable = self.alignSXVar) + self.alignSXButton.pack(side=LEFT, expand=False) + self.alignSYVar = IntVar() + self.alignSYVar.set(False) + self.alignSYButton = Checkbutton( + frame, + text = ': Y', + variable = self.alignSYVar) + self.alignSYButton.pack(side=LEFT, expand=False) + frame.pack(side=TOP, fill = X, expand = 1,pady=5) + self.alignSZVar = IntVar() + self.alignSZVar.set(False) + self.alignSZButton = Checkbutton( + frame, + text = ': Z', + variable = self.alignSZVar) + self.alignSZButton.pack(side=LEFT, expand=False) + frame.pack(side=TOP, fill = X, expand = 1,pady=5) + + + mainFrame.pack(fill = 'both', expand = 1,padx=7,pady=7) + + + def createMenuBar(self): + self.menuBar.destroy() + + def onDestroy(self, event): + messenger.send('ALW_close', [self.nodePath.getName()]) + ''' + If you have open any thing, please rewrite here! + ''' + pass + + ############################### + + def ok_press(self): + ################################################################# + # ok_press(self) + # Callback function + # This function will be called when user click on the "OK" button on the window. + ################################################################# + self.quit() + + def Align_press(self): + list = [self.alignXVar.get(), self.alignYVar.get(), self.alignZVar.get(), + self.alignHVar.get(), self.alignPVar.get(), self.alignRVar.get(), + self.alignSXVar.get(), self.alignSYVar.get(), self.alignSZVar.get()] + if self.targetName != None: + messenger.send('ALW_align', [self.nodePath, self.targetName, list]) + return + + def setTargetNode(self,name=None): + self.targetName = name + return + diff --git a/doc/SceneEditor/Dirlight.egg b/doc/SceneEditor/Dirlight.egg new file mode 100644 index 0000000000..5a9a500370 --- /dev/null +++ b/doc/SceneEditor/Dirlight.egg @@ -0,0 +1,575 @@ + { Z-Up } + + { + "MaxEggPlugin nul.max -o 'E:\sshadhan\Desktop\Dirlight.egg'" +} + Tex1 { + "DirLight.tif" + format { rgb } + wrapu { repeat } + wrapv { repeat } + minfilter { linear_mipmap_linear } + magfilter { linear } +} + Box03 { + { + { + 0.204526 0 0 0 + 0 3.98227e-008 0.204526 0 + 0 -0.204526 3.98227e-008 0 + 0 0 0 1 + } + } + Box03.verts { + 1 { + -1.02263 -0.00012118 -1.02263 + { 0 -0.204526 3.98227e-008 } + { 0.537617 0.269239 } + } + 2 { + 1.02263 -0.00012118 -1.02263 + { 0 -0.204526 3.98227e-008 } + { 0.769888 0.269239 } + } + 3 { + 1.02263 -0.000120782 1.02263 + { 0 -0.204526 3.98227e-008 } + { 0.769888 0.501509 } + } + 4 { + -1.02263 -0.000120782 1.02263 + { 0 -0.204526 3.98227e-008 } + { 0.537617 0.501509 } + } + 5 { + -1.02263 3.06777 -1.02263 + { 0 -3.98227e-008 -0.204526 } + { 0.516079 0.378406 } + } + 6 { + 1.02263 3.06777 -1.02263 + { 0 -3.98227e-008 -0.204526 } + { 0.283808 0.378406 } + } + 7 { + 1.02263 -0.00012118 -1.02263 + { 0 -3.98227e-008 -0.204526 } + { 0.283808 0.03 } + } + 8 { + -1.02263 -0.00012118 -1.02263 + { 0 -3.98227e-008 -0.204526 } + { 0.516079 0.03 } + } + 9 { + 1.02263 3.06777 -1.02263 + { 0.204526 0 0 } + { 0.516079 0.759119 } + } + 10 { + 1.02263 3.06777 1.02263 + { 0.204526 0 0 } + { 0.283808 0.759119 } + } + 11 { + 1.02263 -0.000120782 1.02263 + { 0.204526 0 0 } + { 0.283808 0.410713 } + } + 12 { + 1.02263 -0.00012118 -1.02263 + { 0.204526 0 0 } + { 0.516079 0.410713 } + } + 13 { + 1.02263 3.06777 1.02263 + { 0 3.98227e-008 0.204526 } + { 0.262271 0.759119 } + } + 14 { + -1.02263 3.06777 1.02263 + { 0 3.98227e-008 0.204526 } + { 0.03 0.759119 } + } + 15 { + -1.02263 -0.000120782 1.02263 + { 0 3.98227e-008 0.204526 } + { 0.03 0.410713 } + } + 16 { + 1.02263 -0.000120782 1.02263 + { 0 3.98227e-008 0.204526 } + { 0.262271 0.410713 } + } + 17 { + -1.02263 3.06777 1.02263 + { -0.204526 0 0 } + { 0.262271 0.378406 } + } + 18 { + -1.02263 3.06777 -1.02263 + { -0.204526 0 0 } + { 0.03 0.378406 } + } + 19 { + -1.02263 -0.00012118 -1.02263 + { -0.204526 0 0 } + { 0.03 0.03 } + } + 20 { + -1.02263 -0.000120782 1.02263 + { -0.204526 0 0 } + { 0.262271 0.03 } + } + 21 { + -1.02263 3.06777 -1.02263 + { 0 0.204526 -3.98227e-008 } + { 0.537617 0.262271 } + } + 22 { + -1.02263 3.06777 1.02263 + { 0 0.204526 -3.98227e-008 } + { 0.537617 0.03 } + } + 23 { + -0.0931723 3.06777 0.0931723 + { 0 0.204526 -3.98227e-008 } + { 0.643171 0.135554 } + } + 24 { + -0.0931723 3.06777 -0.0931723 + { 0 0.204526 -3.98227e-008 } + { 0.643171 0.156716 } + } + 25 { + 1.02263 3.06777 1.02263 + { 0 0.204526 -3.98227e-008 } + { 0.769888 0.03 } + } + 26 { + 0.0931723 3.06777 0.0931723 + { 0 0.204526 -3.98227e-008 } + { 0.664333 0.135554 } + } + 27 { + 1.02263 3.06777 -1.02263 + { 0 0.204526 -3.98227e-008 } + { 0.769888 0.262271 } + } + 28 { + 0.0931723 3.06777 -0.0931723 + { 0 0.204526 -3.98227e-008 } + { 0.664333 0.156716 } + } + 29 { + -0.0931723 3.06777 -0.0931723 + { -0.204526 0 0 } + { 0.700095 0.508478 } + } + 30 { + -0.0931723 3.06777 0.0931723 + { -0.204526 0 0 } + { 0.721257 0.508478 } + } + 31 { + -0.0931723 7.13171 0.0931715 + { -0.204526 0 0 } + { 0.721257 0.97 } + } + 32 { + -0.0931723 7.13171 -0.0931731 + { -0.204526 0 0 } + { 0.700095 0.97 } + } + 33 { + -0.0931723 3.06777 0.0931723 + { 0 3.98227e-008 0.204526 } + { 0.653846 0.508478 } + } + 34 { + 0.0931723 3.06777 0.0931723 + { 0 3.98227e-008 0.204526 } + { 0.675008 0.508478 } + } + 35 { + 0.0931723 7.13171 0.0931715 + { 0 3.98227e-008 0.204526 } + { 0.675008 0.97 } + } + 36 { + -0.0931723 7.13171 0.0931715 + { 0 3.98227e-008 0.204526 } + { 0.653846 0.97 } + } + 37 { + 0.0931723 3.06777 0.0931723 + { 0.204526 0 0 } + { 0.676971 0.508478 } + } + 38 { + 0.0931723 3.06777 -0.0931723 + { 0.204526 0 0 } + { 0.698133 0.508478 } + } + 39 { + 0.0931723 7.13171 -0.0931731 + { 0.204526 0 0 } + { 0.698133 0.97 } + } + 40 { + 0.0931723 7.13171 0.0931715 + { 0.204526 0 0 } + { 0.676971 0.97 } + } + 41 { + 0.0931723 3.06777 -0.0931723 + { 0 -3.98227e-008 -0.204526 } + { 0.72322 0.508478 } + } + 42 { + -0.0931723 3.06777 -0.0931723 + { 0 -3.98227e-008 -0.204526 } + { 0.744382 0.508478 } + } + 43 { + -0.0931723 7.13171 -0.0931731 + { 0 -3.98227e-008 -0.204526 } + { 0.744382 0.97 } + } + 44 { + 0.0931723 7.13171 -0.0931731 + { 0 -3.98227e-008 -0.204526 } + { 0.72322 0.97 } + } + 45 { + -0.0931723 7.13171 -0.0931731 + { -0.0336239 -0.201743 3.92809e-008 } + { 0.819457 0.311841 } + } + 46 { + -0.0931723 7.13171 0.0931715 + { -0.0336239 -0.201743 3.92809e-008 } + { 0.819457 0.333003 } + } + 47 { + -0.468305 7.19423 0.468304 + { -0.0336239 -0.201743 3.92809e-008 } + { 0.776855 0.375605 } + } + 48 { + -0.468305 7.19423 -0.468306 + { -0.0336239 -0.201743 3.92809e-008 } + { 0.776855 0.269239 } + } + 49 { + -0.0931723 7.13171 0.0931715 + { 0 -0.201743 0.033624 } + { 0.819457 0.333003 } + } + 50 { + 0.0931723 7.13171 0.0931715 + { 0 -0.201743 0.033624 } + { 0.84062 0.333003 } + } + 51 { + 0.468305 7.19423 0.468304 + { 0 -0.201743 0.033624 } + { 0.883222 0.375605 } + } + 52 { + -0.468305 7.19423 0.468304 + { 0 -0.201743 0.033624 } + { 0.776855 0.375605 } + } + 53 { + 0.0931723 7.13171 0.0931715 + { 0.0336239 -0.201743 3.92809e-008 } + { 0.84062 0.333003 } + } + 54 { + 0.0931723 7.13171 -0.0931731 + { 0.0336239 -0.201743 3.92809e-008 } + { 0.84062 0.311841 } + } + 55 { + 0.468305 7.19423 -0.468306 + { 0.0336239 -0.201743 3.92809e-008 } + { 0.883222 0.269239 } + } + 56 { + 0.468305 7.19423 0.468304 + { 0.0336239 -0.201743 3.92809e-008 } + { 0.883222 0.375605 } + } + 57 { + 0.0931723 7.13171 -0.0931731 + { 0 -0.201743 -0.0336239 } + { 0.84062 0.311841 } + } + 58 { + -0.0931723 7.13171 -0.0931731 + { 0 -0.201743 -0.0336239 } + { 0.819457 0.311841 } + } + 59 { + -0.468305 7.19423 -0.468306 + { 0 -0.201743 -0.0336239 } + { 0.776855 0.269239 } + } + 60 { + 0.468305 7.19423 -0.468306 + { 0 -0.201743 -0.0336239 } + { 0.883222 0.269239 } + } + 61 { + -0.468305 7.19423 -0.468306 + { -0.18883 0.0785766 -1.52994e-008 } + { 0.537617 0.508478 } + } + 62 { + -0.468305 7.19423 0.468304 + { -0.18883 0.0785766 -1.52994e-008 } + { 0.643983 0.508478 } + } + 63 { + 0 8.31963 -1.02257e-006 + { -0.18883 0.0785766 -1.52994e-008 } + { 0.5908 0.646908 } + } + 64 { + -0.468305 7.19423 0.468304 + { 0 0.0785766 0.18883 } + { 0.776855 0.03 } + } + 65 { + 0.468305 7.19423 0.468304 + { 0 0.0785766 0.18883 } + { 0.883222 0.03 } + } + 66 { + 0 8.31963 -1.02257e-006 + { 0 0.0785766 0.18883 } + { 0.830039 0.16843 } + } + 67 { + 0.468305 7.19423 0.468304 + { 0.18883 0.0785766 -1.52994e-008 } + { 0.03 0.791425 } + } + 68 { + 0.468305 7.19423 -0.468306 + { 0.18883 0.0785766 -1.52994e-008 } + { 0.136366 0.791425 } + } + 69 { + 0 8.31963 -1.02257e-006 + { 0.18883 0.0785766 -1.52994e-008 } + { 0.0831832 0.929855 } + } + 70 { + 0.468305 7.19423 -0.468306 + { 0 0.0785765 -0.18883 } + { 0.283808 0.791425 } + } + 71 { + -0.468305 7.19423 -0.468306 + { 0 0.0785765 -0.18883 } + { 0.390175 0.791425 } + } + 72 { + 0 8.31963 -1.02257e-006 + { 0 0.0785765 -0.18883 } + { 0.336992 0.929855 } + } + } + { + { 1 1 1 1 } + { Tex1 } + { 1 2 3 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 3 4 1 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 5 6 7 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 7 8 5 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 9 10 11 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 11 12 9 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 13 14 15 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 15 16 13 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 17 18 19 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 19 20 17 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 21 22 23 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 23 24 21 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 22 25 26 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 26 23 22 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 25 27 28 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 28 26 25 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 27 21 24 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 24 28 27 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 29 30 31 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 31 32 29 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 33 34 35 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 35 36 33 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 37 38 39 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 39 40 37 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 41 42 43 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 43 44 41 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 45 46 47 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 47 48 45 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 49 50 51 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 51 52 49 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 53 54 55 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 55 56 53 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 57 58 59 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 59 60 57 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 61 62 63 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 64 65 66 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 67 68 69 { Box03.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 70 71 72 { Box03.verts } } + } +} diff --git a/doc/SceneEditor/MetadataPanel.py b/doc/SceneEditor/MetadataPanel.py new file mode 100644 index 0000000000..0995d2f726 --- /dev/null +++ b/doc/SceneEditor/MetadataPanel.py @@ -0,0 +1,59 @@ +from AppShell import * +from TkGlobal import * + + +class MetadataPanel(AppShell,Pmw.MegaWidget): + appversion = '1.0' + appname = 'Metadata Panel' + frameWidth = 400 + frameHeight = 400 + padx = 0 + pady = 0 + usecommandarea = 0 + usestatusarea = 0 + Metatag="" + Metanode=None + tag_text=None + def __init__(self,nodePath,parent=None,**kw): + + # Initialise superclass + Pmw.MegaWidget.__init__(self, parent) + + # Define the megawidget options. + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + self.Metanode=nodePath + if(nodePath.hasTag("Metadata")): + self.Metatag=self.Metanode.getTag("Metadata") + + if parent == None: + self.parent = Toplevel() + AppShell.__init__(self, self.parent) + + self.parent.resizable(False,False) + + + def appInit(self): + print "Metadata Panel" + + def createInterface(self): + interior = self.interior() + mainFrame = Frame(interior) + tag_label=Label (mainFrame,text="Enter Metadata",font=('MSSansSerif', 15), + relief = RIDGE, borderwidth=5) + tag_label.pack() + source=StringVar() + source.set(self.Metatag) + self.tag_text=Entry(mainFrame, width=10,textvariable=source) + self.tag_text.pack() + set_button=Button(mainFrame, text='Set Metadata',font=('MSSansSerif', 15), + relief = RIDGE, borderwidth=5, command= lambda:self.SetIt()) + set_button.pack() + mainFrame.pack(fill = 'both', expand = 1) + + def SetIt(self): + self.Metanode.setTag("Metadata",self.tag_text.get()) + + diff --git a/doc/SceneEditor/Pointlight.egg b/doc/SceneEditor/Pointlight.egg new file mode 100644 index 0000000000..42f5da319f --- /dev/null +++ b/doc/SceneEditor/Pointlight.egg @@ -0,0 +1,1175 @@ + { Z-Up } + + { + "MaxEggPlugin nul.max -o 'E:\sshadhan\Desktop\Pointlight.egg'" +} + Tex1 { + "PointLight.tif" + format { rgb } + wrapu { repeat } + wrapv { repeat } + minfilter { linear_mipmap_linear } + magfilter { linear } +} + Sphere01 { + { + { + 0.0831012 0 0 0 + 0 1.61804e-008 0.0831012 0 + 0 -0.0831012 1.61804e-008 0 + 0 1.62628e-009 0 1 + } + } + Sphere01.verts { + 1 { + 0 -0.203189 3.95624e-008 + { 2.81819e-009 -0.0831012 1.60721e-008 } + { 0.849859 0.904272 } + } + 2 { + -6.2803e-009 -0.143677 0.143677 + { -2.96629e-009 -0.0550964 0.0622109 } + { 0.816025 0.9179 } + } + 3 { + -0.101595 -0.143677 0.101595 + { -0.0439898 -0.0550964 0.0439897 } + { 0.816298 0.889984 } + } + 4 { + -0.143677 -0.143677 1.54143e-008 + { -0.0622109 -0.0550964 3.94371e-009 } + { 0.836231 0.870438 } + } + 5 { + -0.101595 -0.143677 -0.101595 + { -0.0439897 -0.0550964 -0.0439897 } + { 0.864147 0.870712 } + } + 6 { + 1.88409e-008 -0.143677 -0.143677 + { 1.03993e-008 -0.0550965 -0.0622109 } + { 0.883693 0.890645 } + } + 7 { + 0.101595 -0.143677 -0.101595 + { 0.0439898 -0.0550965 -0.0439897 } + { 0.883419 0.91856 } + } + 8 { + 0.143677 -0.143677 5.30961e-008 + { 0.0622109 -0.0550964 2.31734e-008 } + { 0.863486 0.938106 } + } + 9 { + 0.101595 -0.143677 0.101595 + { 0.0439897 -0.0550964 0.0439898 } + { 0.835571 0.937832 } + } + 10 { + -6.2803e-009 -0.143677 0.143677 + { -2.96629e-009 -0.0550964 0.0622109 } + { 0.806869 0.906913 } + } + 11 { + -8.88169e-009 5.00704e-008 0.203189 + { -5.53626e-009 2.08603e-008 0.0831012 } + { 0.770394 0.906913 } + } + 12 { + -0.143677 3.84828e-008 0.143677 + { -0.0587614 1.72823e-008 0.0587614 } + { 0.770394 0.870438 } + } + 13 { + -0.101595 -0.143677 0.101595 + { -0.0439898 -0.0550964 0.0439897 } + { 0.806869 0.881122 } + } + 14 { + -0.101595 -0.143677 0.101595 + { -0.0439898 -0.0550964 0.0439897 } + { 0.724763 0.932705 } + } + 15 { + -0.143677 3.84828e-008 0.143677 + { -0.0587614 1.72823e-008 0.0587614 } + { 0.688288 0.943389 } + } + 16 { + -0.203189 1.0508e-008 -1.77634e-008 + { -0.0831012 3.12346e-009 -7.51694e-009 } + { 0.688288 0.906913 } + } + 17 { + -0.143677 -0.143677 1.54143e-008 + { -0.0622109 -0.0550964 3.94371e-009 } + { 0.724763 0.906913 } + } + 18 { + -0.143677 -1.74669e-008 -0.143677 + { -0.0587614 -7.46716e-009 -0.0587614 } + { 0.688288 0.870438 } + } + 19 { + -0.101595 -0.143677 -0.101595 + { -0.0439897 -0.0550964 -0.0439897 } + { 0.724763 0.881122 } + } + 20 { + -0.101595 -0.143677 -0.101595 + { -0.0439897 -0.0550964 -0.0439897 } + { 0.56055 0.932705 } + } + 21 { + -0.143677 -1.74669e-008 -0.143677 + { -0.0587614 -7.46716e-009 -0.0587614 } + { 0.524075 0.943389 } + } + 22 { + 2.66451e-008 -2.90545e-008 -0.203189 + { 1.36271e-008 -1.28522e-008 -0.0831012 } + { 0.524075 0.906913 } + } + 23 { + 1.88409e-008 -0.143677 -0.143677 + { 1.03993e-008 -0.0550965 -0.0622109 } + { 0.56055 0.906913 } + } + 24 { + 0.143677 -1.74669e-008 -0.143677 + { 0.0587615 -4.44334e-009 -0.0587614 } + { 0.524075 0.870438 } + } + 25 { + 0.101595 -0.143677 -0.101595 + { 0.0439898 -0.0550965 -0.0439897 } + { 0.56055 0.881122 } + } + 26 { + 0.101595 -0.143677 -0.101595 + { 0.0439898 -0.0550965 -0.0439897 } + { 0.642657 0.932705 } + } + 27 { + 0.143677 -1.74669e-008 -0.143677 + { 0.0587615 -4.44334e-009 -0.0587614 } + { 0.606181 0.943389 } + } + 28 { + 0.203189 1.0508e-008 3.55268e-008 + { 0.0831012 4.47838e-009 1.50679e-008 } + { 0.606181 0.906913 } + } + 29 { + 0.143677 -0.143677 5.30961e-008 + { 0.0622109 -0.0550964 2.31734e-008 } + { 0.642657 0.906913 } + } + 30 { + 0.143677 3.84828e-008 0.143677 + { 0.0587614 1.49249e-008 0.0587615 } + { 0.606181 0.870438 } + } + 31 { + 0.101595 -0.143677 0.101595 + { 0.0439897 -0.0550964 0.0439898 } + { 0.642657 0.881122 } + } + 32 { + 0.101595 -0.143677 0.101595 + { 0.0439897 -0.0550964 0.0439898 } + { 0.806869 0.932705 } + } + 33 { + 0.143677 3.84828e-008 0.143677 + { 0.0587614 1.49249e-008 0.0587615 } + { 0.770394 0.943389 } + } + 34 { + -6.2803e-009 0.143677 0.143677 + { -5.53642e-009 0.0550965 0.0622109 } + { 0.733919 0.906913 } + } + 35 { + -0.101595 0.143677 0.101595 + { -0.0439897 0.0550965 0.0439897 } + { 0.733919 0.881122 } + } + 36 { + -0.101595 0.143677 0.101595 + { -0.0439897 0.0550965 0.0439897 } + { 0.651812 0.932705 } + } + 37 { + -0.143677 0.143677 -4.05355e-008 + { -0.0622109 0.0550964 -1.48349e-008 } + { 0.651812 0.906913 } + } + 38 { + -0.101595 0.143677 -0.101595 + { -0.0439897 0.0550964 -0.0439898 } + { 0.651812 0.881122 } + } + 39 { + -0.101595 0.143677 -0.101595 + { -0.0439897 0.0550964 -0.0439898 } + { 0.4876 0.932705 } + } + 40 { + 1.88409e-008 0.143677 -0.143677 + { 1.0111e-008 0.0550964 -0.0622109 } + { 0.4876 0.906913 } + } + 41 { + 0.101595 0.143677 -0.101595 + { 0.0439897 0.0550964 -0.0439897 } + { 0.4876 0.881122 } + } + 42 { + 0.101595 0.143677 -0.101595 + { 0.0439897 0.0550964 -0.0439897 } + { 0.569706 0.932705 } + } + 43 { + 0.143677 0.143677 -2.85366e-009 + { 0.0622109 0.0550964 -8.05606e-010 } + { 0.569706 0.906913 } + } + 44 { + 0.101595 0.143677 0.101595 + { 0.0439897 0.0550965 0.0439897 } + { 0.569706 0.881122 } + } + 45 { + 0.101595 0.143677 0.101595 + { 0.0439897 0.0550965 0.0439897 } + { 0.733919 0.932705 } + } + 46 { + 0 0.203189 -3.95624e-008 + { 3.13616e-009 0.0831012 -1.65717e-008 } + { 0.919557 0.904272 } + } + 47 { + -0.101595 0.143677 0.101595 + { -0.0439897 0.0550965 0.0439897 } + { 0.933845 0.870712 } + } + 48 { + -6.2803e-009 0.143677 0.143677 + { -5.53642e-009 0.0550965 0.0622109 } + { 0.95339 0.890645 } + } + 49 { + -0.143677 0.143677 -4.05355e-008 + { -0.0622109 0.0550964 -1.48349e-008 } + { 0.905929 0.870438 } + } + 50 { + -0.101595 0.143677 -0.101595 + { -0.0439897 0.0550964 -0.0439898 } + { 0.885996 0.889984 } + } + 51 { + 1.88409e-008 0.143677 -0.143677 + { 1.0111e-008 0.0550964 -0.0622109 } + { 0.885723 0.9179 } + } + 52 { + 0.101595 0.143677 -0.101595 + { 0.0439897 0.0550964 -0.0439897 } + { 0.905268 0.937832 } + } + 53 { + 0.143677 0.143677 -2.85366e-009 + { 0.0622109 0.0550964 -8.05606e-010 } + { 0.933184 0.938106 } + } + 54 { + 0.101595 0.143677 0.101595 + { 0.0439897 0.0550965 0.0439897 } + { 0.953117 0.91856 } + } + 55 { + -0.448066 0.540474 -0.0243913 + { 0.0622339 0.0550705 2.82555e-008 } + { 0.239384 0.332059 } + } + 56 { + -0.600247 0.580497 -0.0243914 + { -0.0590326 0.058489 -2.9106e-008 } + { 0.239385 0.370693 } + } + 57 { + -0.0143542 -2.11169e-008 0.800379 + { -2.36973e-008 -3.31737e-009 0.0831012 } + { 0.03 0.221953 } + } + 58 { + 0.0410214 0.0558902 0.664106 + { 0.0583631 0.0589056 -0.00544895 } + { 0.0645957 0.207895 } + } + 59 { + -0.558817 0.428693 -0.0243914 + { -0.0544923 -0.0627407 -3.37662e-008 } + { 0.737089 0.769437 } + } + 60 { + -0.448066 0.540474 -0.0243913 + { 0.0622339 0.0550705 2.82555e-008 } + { 0.734092 0.809272 } + } + 61 { + 0.0410214 0.0558902 0.664106 + { 0.0583631 0.0589056 -0.00544895 } + { 0.4876 0.790733 } + } + 62 { + -0.0697296 -0.0558903 0.664106 + { -0.0583631 -0.0589056 -0.00544897 } + { 0.490596 0.750898 } + } + 63 { + -0.600247 0.580497 -0.0243914 + { -0.0590326 0.058489 -2.9106e-008 } + { 0.239384 0.735917 } + } + 64 { + -0.558817 0.428693 -0.0243914 + { -0.0544923 -0.0627407 -3.37662e-008 } + { 0.239384 0.697379 } + } + 65 { + -0.0697296 -0.0558903 0.664106 + { -0.0583631 -0.0589056 -0.00544897 } + { 0.414173 0.574357 } + } + 66 { + -0.0143542 -2.11169e-008 0.800379 + { -2.36973e-008 -3.31737e-009 0.0831012 } + { 0.448769 0.588546 } + } + 67 { + 0.0410218 0.0558902 -0.712888 + { 0.0583631 0.0589056 0.00544898 } + { 0.414173 0.207895 } + } + 68 { + -0.0143537 -4.24682e-008 -0.849162 + { 3.17156e-008 -7.4369e-009 -0.0831012 } + { 0.448769 0.221953 } + } + 69 { + -0.0697292 -0.0558903 -0.712888 + { -0.0583631 -0.0589056 0.00544892 } + { 0.7686 0.614293 } + } + 70 { + 0.0410218 0.0558902 -0.712888 + { 0.0583631 0.0589056 0.00544898 } + { 0.769568 0.574357 } + } + 71 { + -0.448066 0.540474 -0.0243913 + { 0.0622339 0.0550705 2.82555e-008 } + { 0.944305 0.578593 } + } + 72 { + -0.558817 0.428693 -0.0243914 + { -0.0544923 -0.0627407 -3.37662e-008 } + { 0.943337 0.618529 } + } + 73 { + -0.0143537 -4.24682e-008 -0.849162 + { 3.17156e-008 -7.4369e-009 -0.0831012 } + { 0.03 0.588546 } + } + 74 { + -0.0697292 -0.0558903 -0.712888 + { -0.0583631 -0.0589056 0.00544892 } + { 0.0645957 0.574357 } + } + 75 { + 0.53011 -0.428693 -0.024391 + { 0.0544923 0.0627407 -4.04613e-009 } + { 0.239384 0.789436 } + } + 76 { + 0.571539 -0.580497 -0.024391 + { 0.0590326 -0.058489 2.08687e-008 } + { 0.239384 0.750898 } + } + 77 { + -0.0143537 -4.24682e-008 -0.849162 + { 3.17156e-008 -7.4369e-009 -0.0831012 } + { 0.448769 0.898269 } + } + 78 { + 0.0410218 0.0558902 -0.712888 + { 0.0583631 0.0589056 0.00544898 } + { 0.414173 0.912457 } + } + 79 { + 0.419359 -0.540474 -0.024391 + { -0.0622339 -0.0550705 6.40126e-010 } + { 0.73463 0.642446 } + } + 80 { + 0.53011 -0.428693 -0.024391 + { 0.0544923 0.0627407 -4.04613e-009 } + { 0.736062 0.682369 } + } + 81 { + 0.0410218 0.0558902 -0.712888 + { 0.0583631 0.0589056 0.00544898 } + { 0.489032 0.69123 } + } + 82 { + -0.0697292 -0.0558903 -0.712888 + { -0.0583631 -0.0589056 0.00544892 } + { 0.4876 0.651308 } + } + 83 { + 0.571539 -0.580497 -0.024391 + { 0.0590326 -0.058489 2.08687e-008 } + { 0.239385 0.391126 } + } + 84 { + 0.419359 -0.540474 -0.024391 + { -0.0622339 -0.0550705 6.40126e-010 } + { 0.239384 0.42976 } + } + 85 { + -0.0697292 -0.0558903 -0.712888 + { -0.0583631 -0.0589056 0.00544892 } + { 0.0645957 0.553925 } + } + 86 { + -0.0143537 -4.24682e-008 -0.849162 + { 3.17156e-008 -7.4369e-009 -0.0831012 } + { 0.03 0.539866 } + } + 87 { + 0.0410214 0.0558902 0.664106 + { 0.0583631 0.0589056 -0.00544895 } + { 0.0645957 0.912457 } + } + 88 { + -0.0143542 -2.11169e-008 0.800379 + { -2.36973e-008 -3.31737e-009 0.0831012 } + { 0.03 0.898268 } + } + 89 { + -0.0697296 -0.0558903 0.664106 + { -0.0583631 -0.0589056 -0.00544897 } + { 0.03 0.967382 } + } + 90 { + 0.0410214 0.0558902 0.664106 + { 0.0583631 0.0589056 -0.00544895 } + { 0.0305984 0.927438 } + } + 91 { + 0.53011 -0.428693 -0.024391 + { 0.0544923 0.0627407 -4.04613e-009 } + { 0.205368 0.930057 } + } + 92 { + 0.419359 -0.540474 -0.024391 + { -0.0622339 -0.0550705 6.40126e-010 } + { 0.204769 0.97 } + } + 93 { + -0.0143542 -2.11169e-008 0.800379 + { -2.36973e-008 -3.31737e-009 0.0831012 } + { 0.448769 0.539867 } + } + 94 { + -0.0697296 -0.0558903 0.664106 + { -0.0583631 -0.0589056 -0.00544897 } + { 0.414173 0.553925 } + } + 95 { + 0.52612 0.433713 -0.0243912 + { 0.0550705 -0.0622339 3.21479e-008 } + { 0.239384 0.154165 } + } + 96 { + 0.566143 0.585893 -0.0243912 + { 0.058489 0.0590326 3.83966e-009 } + { 0.239384 0.192799 } + } + 97 { + -0.0143541 8.09984e-008 0.80038 + { -9.81896e-009 4.70957e-009 0.0831012 } + { 0.03 0.0440582 } + } + 98 { + 0.0415362 -0.0553754 0.664106 + { 0.0589056 -0.0583631 -0.00544895 } + { 0.0645958 0.03 } + } + 99 { + 0.414339 0.544464 -0.0243912 + { -0.0627407 0.0544923 -2.89671e-008 } + { 0.662968 0.698372 } + } + 100 { + 0.52612 0.433713 -0.0243912 + { 0.0550705 -0.0622339 3.21479e-008 } + { 0.662369 0.738315 } + } + 101 { + 0.0415362 -0.0553754 0.664106 + { 0.0589056 -0.0583631 -0.00544895 } + { 0.4876 0.735697 } + } + 102 { + -0.0702443 0.0553756 0.664106 + { -0.0589056 0.0583631 -0.00544896 } + { 0.488198 0.695754 } + } + 103 { + 0.566143 0.585893 -0.0243912 + { 0.058489 0.0590326 3.83966e-009 } + { 0.710712 0.207895 } + } + 104 { + 0.414339 0.544464 -0.0243912 + { -0.0627407 0.0544923 -2.89671e-008 } + { 0.710712 0.246433 } + } + 105 { + -0.0702443 0.0553756 0.664106 + { -0.0589056 0.0583631 -0.00544896 } + { 0.535924 0.369454 } + } + 106 { + -0.0143541 8.09984e-008 0.80038 + { -9.81896e-009 4.70957e-009 0.0831012 } + { 0.501328 0.355265 } + } + 107 { + 0.0415364 -0.0553756 -0.712888 + { 0.0589056 -0.0583631 0.00544902 } + { 0.414173 0.03 } + } + 108 { + -0.0143539 -1.45055e-007 -0.849162 + { 1.44852e-008 -4.85542e-008 -0.0831012 } + { 0.448769 0.0440582 } + } + 109 { + -0.0702441 0.0553754 -0.712888 + { -0.0589056 0.0583631 0.00544893 } + { 0.4876 0.614165 } + } + 110 { + 0.0415364 -0.0553756 -0.712888 + { 0.0589056 -0.0583631 0.00544902 } + { 0.490943 0.574357 } + } + 111 { + 0.52612 0.433713 -0.0243912 + { 0.0550705 -0.0622339 3.21479e-008 } + { 0.737265 0.595046 } + } + 112 { + 0.414339 0.544464 -0.0243912 + { -0.0627407 0.0544923 -2.89671e-008 } + { 0.733921 0.634854 } + } + 113 { + -0.0143539 -1.45055e-007 -0.849162 + { 1.44852e-008 -4.85542e-008 -0.0831012 } + { 0.920097 0.355265 } + } + 114 { + -0.0702441 0.0553754 -0.712888 + { -0.0589056 0.0583631 0.00544893 } + { 0.885501 0.369454 } + } + 115 { + -0.443047 -0.544464 -0.0243911 + { 0.0627407 -0.0544923 -7.42057e-009 } + { 0.710712 0.514147 } + } + 116 { + -0.594851 -0.585893 -0.0243911 + { -0.058489 -0.0590327 2.64795e-008 } + { 0.710712 0.552686 } + } + 117 { + -0.0143539 -1.45055e-007 -0.849162 + { 1.44852e-008 -4.85542e-008 -0.0831012 } + { 0.501328 0.405315 } + } + 118 { + 0.0415364 -0.0553756 -0.712888 + { 0.0589056 -0.0583631 0.00544902 } + { 0.535924 0.391126 } + } + 119 { + -0.554828 -0.433713 -0.0243912 + { -0.0550705 0.0622339 4.14658e-010 } + { 0.944107 0.755133 } + } + 120 { + -0.443047 -0.544464 -0.0243911 + { 0.0627407 -0.0544923 -7.42057e-009 } + { 0.943139 0.795069 } + } + 121 { + 0.0415364 -0.0553756 -0.712888 + { 0.0589056 -0.0583631 0.00544902 } + { 0.768401 0.790834 } + } + 122 { + -0.0702441 0.0553754 -0.712888 + { -0.0589056 0.0583631 0.00544893 } + { 0.769369 0.750898 } + } + 123 { + -0.594851 -0.585893 -0.0243911 + { -0.058489 -0.0590327 2.64795e-008 } + { 0.696985 0.03 } + } + 124 { + -0.554828 -0.433713 -0.0243912 + { -0.0550705 0.0622339 4.14658e-010 } + { 0.696985 0.068634 } + } + 125 { + -0.0702441 0.0553754 -0.712888 + { -0.0589056 0.0583631 0.00544893 } + { 0.522196 0.192799 } + } + 126 { + -0.0143539 -1.45055e-007 -0.849162 + { 1.44852e-008 -4.85542e-008 -0.0831012 } + { 0.4876 0.178741 } + } + 127 { + 0.0415362 -0.0553754 0.664106 + { 0.0589056 -0.0583631 -0.00544895 } + { 0.885501 0.391126 } + } + 128 { + -0.0143541 8.09984e-008 0.80038 + { -9.81896e-009 4.70957e-009 0.0831012 } + { 0.920097 0.405315 } + } + 129 { + -0.0702443 0.0553756 0.664106 + { -0.0589056 0.0583631 -0.00544896 } + { 0.4876 0.856526 } + } + 130 { + 0.0415362 -0.0553754 0.664106 + { 0.0589056 -0.0583631 -0.00544895 } + { 0.488878 0.816599 } + } + 131 { + -0.443047 -0.544464 -0.0243911 + { 0.0627407 -0.0544923 -7.42057e-009 } + { 0.73594 0.824507 } + } + 132 { + -0.554828 -0.433713 -0.0243912 + { -0.0550705 0.0622339 4.14658e-010 } + { 0.734662 0.864435 } + } + 133 { + -0.0143541 8.09984e-008 0.80038 + { -9.81896e-009 4.70957e-009 0.0831012 } + { 0.906369 0.178741 } + } + 134 { + -0.0702443 0.0553756 0.664106 + { -0.0589056 0.0583631 -0.00544896 } + { 0.871773 0.192799 } + } + } + { + { 1 1 1 1 } + { Tex1 } + { 1 2 3 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 1 3 4 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 1 4 5 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 1 5 6 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 1 6 7 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 1 7 8 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 1 8 9 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 1 9 2 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 10 11 12 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 12 13 10 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 14 15 16 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 16 17 14 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 17 16 18 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 18 19 17 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 20 21 22 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 22 23 20 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 23 22 24 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 24 25 23 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 26 27 28 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 28 29 26 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 29 28 30 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 30 31 29 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 32 33 11 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 11 10 32 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 11 34 35 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 35 12 11 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 15 36 37 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 37 16 15 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 16 37 38 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 38 18 16 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 21 39 40 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 40 22 21 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 22 40 41 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 41 24 22 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 27 42 43 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 43 28 27 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 28 43 44 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 44 30 28 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 33 45 34 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 34 11 33 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 46 47 48 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 46 49 47 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 46 50 49 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 46 51 50 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 46 52 51 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 46 53 52 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 46 54 53 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 46 48 54 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 55 56 57 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 57 58 55 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 59 60 61 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 61 62 59 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 63 64 65 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 65 66 63 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 67 68 56 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 56 55 67 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 69 70 71 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 71 72 69 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 73 74 64 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 64 63 73 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 75 76 77 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 77 78 75 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 79 80 81 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 81 82 79 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 83 84 85 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 85 86 83 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 87 88 76 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 76 75 87 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 89 90 91 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 91 92 89 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 93 94 84 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 84 83 93 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 95 96 97 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 97 98 95 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 99 100 101 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 101 102 99 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 103 104 105 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 105 106 103 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 107 108 96 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 96 95 107 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 109 110 111 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 111 112 109 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 113 114 104 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 104 103 113 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 115 116 117 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 117 118 115 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 119 120 121 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 121 122 119 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 123 124 125 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 125 126 123 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 127 128 116 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 116 115 127 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 129 130 131 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 131 132 129 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 133 134 124 { Sphere01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 124 123 133 { Sphere01.verts } } + } +} diff --git a/doc/SceneEditor/SeSession.py b/doc/SceneEditor/SeSession.py new file mode 100644 index 0000000000..5a27e86bae --- /dev/null +++ b/doc/SceneEditor/SeSession.py @@ -0,0 +1,991 @@ +################################################################# +# seSession.py +# Originally from DirectSession.py +# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +# +# We took out a lot of stuff we don't need in the sceneeditor. +# This is also the main reason we didn't just inherite the original directSession. +# Also, the way of selecting, renaming and some hot-key controls are changed. +# +################################################################# +from PandaObject import * +from DirectGlobals import * +from DirectUtil import* +from intervalGlobal import * +from seCameraControl import * +from seManipulation import * +from seSelection import * +from seGrid import * +from seGeometry import * +import Placer +import Slider +import OnscreenText +import types +import string +import Loader + +class SeSession(PandaObject): ### Customized DirectSession + + def __init__(self): + # Establish a global pointer to the direct object early on + # so dependant classes can access it in their code + __builtins__["SEditor"] = self + # These come early since they are used later on + self.group = render.attachNewNode('SEditor') + self.font = TextNode.getDefaultFont() + self.fEnabled = 0 + self.drList = DisplayRegionList() + self.iRayList = map(lambda x: x.iRay, self.drList) + self.dr = self.drList[0] + self.camera = base.camera + self.trueCamera = self.camera + self.iRay = self.dr.iRay + self.coaMode = COA_ORIGIN + + self.enableAutoCamera = True + + self.cameraControl = DirectCameraControl() + self.manipulationControl = DirectManipulationControl() + self.useObjectHandles() + self.grid = DirectGrid() + self.grid.disable() + + # Initialize the collection of selected nodePaths + self.selected = SelectedNodePaths() + # Ancestry of currently selected object + self.ancestry = [] + self.ancestryIndex = 0 + self.activeParent = None + + self.selectedNPReadout = OnscreenText.OnscreenText( + pos = (-1.0, -0.9), bg=Vec4(1,1,1,1), + scale = 0.05, align = TextNode.ALeft, + mayChange = 1, font = self.font) + # Make sure readout is never lit or drawn in wireframe + useDirectRenderStyle(self.selectedNPReadout) + self.selectedNPReadout.reparentTo(hidden) + + self.activeParentReadout = OnscreenText.OnscreenText( + pos = (-1.0, -0.975), bg=Vec4(1,1,1,1), + scale = 0.05, align = TextNode.ALeft, + mayChange = 1, font = self.font) + # Make sure readout is never lit or drawn in wireframe + useDirectRenderStyle(self.activeParentReadout) + self.activeParentReadout.reparentTo(hidden) + + self.directMessageReadout = OnscreenText.OnscreenText( + pos = (-1.0, 0.9), bg=Vec4(1,1,1,1), + scale = 0.05, align = TextNode.ALeft, + mayChange = 1, font = self.font) + # Make sure readout is never lit or drawn in wireframe + useDirectRenderStyle(self.directMessageReadout) + self.directMessageReadout.reparentTo(hidden) + + self.fControl = 0 + self.fAlt = 0 + self.fShift = 0 + + self.pos = VBase3() + self.hpr = VBase3() + self.scale = VBase3() + + self.hitPt = Point3(0.0) + + # Lists for managing undo/redo operations + self.undoList = [] + self.redoList = [] + + # One run through the context task to init everything + self.drList.updateContext() + for dr in self.drList: + dr.camUpdate() + + + + self.modifierEvents = ['control', 'control-up', + 'shift', 'shift-up', + 'alt', 'alt-up', + ] + self.keyEvents = ['escape', 'delete', 'page_up', 'page_down', + '[', '{', ']', '}', + 'shift-a', 'b', 'control-f', + 'l', 'shift-l', 'o', 'p', 'r', + 'shift-r', 's', 't', 'v', 'w'] + self.mouseEvents = ['mouse1', 'mouse1-up', + 'shift-mouse1', 'shift-mouse1-up', + 'control-mouse1', 'control-mouse1-up', + 'alt-mouse1', 'alt-mouse1-up', + 'mouse2', 'mouse2-up', + 'shift-mouse2', 'shift-mouse2-up', + 'control-mouse2', 'control-mouse2-up', + 'alt-mouse2', 'alt-mouse2-up', + 'mouse3', 'mouse3-up', + 'shift-mouse3', 'shift-mouse3-up', + 'control-mouse3', 'control-mouse3-up', + 'alt-mouse3', 'alt-mouse3-up', + ] + + def enable(self): + if self.fEnabled: + return + # Make sure old tasks are shut down + self.disable() + # Start all display region context tasks + self.drList.spawnContextTask() + # Turn on mouse Flying + self.cameraControl.enableMouseFly() + # Turn on object manipulation + self.manipulationControl.enableManipulation() + # Make sure list of selected items is reset + self.selected.reset() + # Accept appropriate hooks + self.enableKeyEvents() + self.enableModifierEvents() + self.enableMouseEvents() + # Set flag + self.fEnabled = 1 + + if self.enableAutoCamera: + self.accept('DH_LoadingComplete', self.autoCameraMove) + + def disable(self): + # Shut down all display region context tasks + self.drList.removeContextTask() + # Turn off camera fly + self.cameraControl.disableMouseFly() + # Turn off object manipulation + self.manipulationControl.disableManipulation() + self.disableKeyEvents() + self.disableModifierEvents() + self.disableMouseEvents() + self.ignore('DH_LoadingComplete') + # Kill tasks + taskMgr.remove('flashNodePath') + taskMgr.remove('hideDirectMessage') + taskMgr.remove('hideDirectMessageLater') + # Set flag + self.fEnabled = 0 + + def minimumConfiguration(self): + # Remove context task + self.drList.removeContextTask() + # Turn off camera fly + self.cameraControl.disableMouseFly() + # Ignore keyboard and action events + self.disableKeyEvents() + self.disableActionEvents() + # But let mouse events pass through + self.enableMouseEvents() + self.enableModifierEvents() + + def oobe(self): + # If oobeMode was never set, set it to false and create the + # structures we need to implement OOBE. + try: + self.oobeMode + except: + self.oobeMode = 0 + + self.oobeCamera = hidden.attachNewNode('oobeCamera') + + self.oobeVis = loader.loadModelOnce('models/misc/camera') + if self.oobeVis: + self.oobeVis.node().setFinal(1) + + if self.oobeMode: + # Position a target point to lerp the oobe camera to + self.cameraControl.camManipRef.iPosHpr(self.trueCamera) + t = self.oobeCamera.lerpPosHpr( + Point3(0), Vec3(0), 2.0, + other = self.cameraControl.camManipRef, + task = 'manipulateCamera', + blendType = 'easeInOut') + # When move is done, switch to oobe mode + t.uponDeath = self.endOOBE + else: + # Place camera marker at true camera location + self.oobeVis.reparentTo(self.trueCamera) + # Remove any transformation on the models arc + self.oobeVis.clearMat() + # Make oobeCamera be a sibling of wherever camera is now. + cameraParent = self.camera.getParent() + # Prepare oobe camera + self.oobeCamera.reparentTo(cameraParent) + self.oobeCamera.iPosHpr(self.trueCamera) + # Put camera under new oobe camera + base.cam.reparentTo(self.oobeCamera) + # Position a target point to lerp the oobe camera to + self.cameraControl.camManipRef.setPos( + self.trueCamera, Vec3(-2,-20, 5)) + self.cameraControl.camManipRef.lookAt(self.trueCamera) + t = self.oobeCamera.lerpPosHpr( + Point3(0), Vec3(0), 2.0, + other = self.cameraControl.camManipRef, + task = 'manipulateCamera', + blendType = 'easeInOut') + # When move is done, switch to oobe mode + t.uponDeath = self.beginOOBE + + def beginOOBE(self, state): + # Make sure we've reached our final destination + self.oobeCamera.iPosHpr(self.cameraControl.camManipRef) + self.camera = self.oobeCamera + self.oobeMode = 1 + + def endOOBE(self, state): + # Make sure we've reached our final destination + self.oobeCamera.iPosHpr(self.trueCamera) + # Disable OOBE mode. + base.cam.reparentTo(self.trueCamera) + self.camera = self.trueCamera + # Get rid of ancillary node paths + self.oobeVis.reparentTo(hidden) + self.oobeCamera.reparentTo(hidden) + self.oobeMode = 0 + + def destroy(self): + self.disable() + + def reset(self): + self.enable() + + # EVENT FUNCTIONS + def enableModifierEvents(self): + for event in self.modifierEvents: + self.accept(event, self.inputHandler, [event]) + + def enableKeyEvents(self): + for event in self.keyEvents: + self.accept(event, self.inputHandler, [event]) + + def enableMouseEvents(self): + for event in self.mouseEvents: + self.accept(event, self.inputHandler, [event]) + + def disableModifierEvents(self): + for event in self.modifierEvents: + self.ignore(event) + + def disableKeyEvents(self): + for event in self.keyEvents: + self.ignore(event) + + def disableMouseEvents(self): + for event in self.mouseEvents: + self.ignore(event) + + def inputHandler(self, input): + # Deal with keyboard and mouse input + if input == 'mouse1-up': + messenger.send('DIRECT-mouse1Up') + if SEditor.widget.fActive: + messenger.send('shift-f') + elif input.find('mouse1') != -1: + modifiers = self.getModifiers(input, 'mouse1') + messenger.send('DIRECT-mouse1', sentArgs = [modifiers]) + elif input == 'mouse2-up': + messenger.send('DIRECT-mouse2Up') + if SEditor.widget.fActive: + messenger.send('shift-f') + elif input.find('mouse2') != -1: + modifiers = self.getModifiers(input, 'mouse2') + messenger.send('DIRECT-mouse2', sentArgs = [modifiers]) + elif input == 'mouse3-up': + messenger.send('DIRECT-mouse3Up') + if SEditor.widget.fActive: + messenger.send('shift-f') + elif input.find('mouse3') != -1: + modifiers = self.getModifiers(input, 'mouse3') + messenger.send('DIRECT-mouse3', sentArgs = [modifiers]) + elif input == 'shift': + self.fShift = 1 + elif input == 'shift-up': + self.fShift = 0 + elif input == 'control': + self.fControl = 1 + elif input == 'control-up': + self.fControl = 0 + elif input == 'alt': + self.fAlt = 1 + elif input == 'alt-up': + self.fAlt = 0 + elif input == 'page_up': + self.upAncestry() + elif input == 'page_down': + self.downAncestry() + elif input == 'escape': + self.deselectAll() + elif input == 'delete': + taskMgr.remove('followSelectedNodePath') + #self.removeAllSelected() + messenger.send('SGE_Remove',[None]) + self.deselectAll() + elif input == 'v': + messenger.send('SEditor-ToggleWidgetVis') + self.toggleWidgetVis() + if SEditor.widget.fActive: + messenger.send('shift-f') + elif input == 'b': + messenger.send('SEditor-ToggleBackface') + base.toggleBackface() + #elif input == 'control-f': + # self.flash(last) + elif input == 'shift-l': + self.cameraControl.toggleCOALock() + elif input == 'o': + self.oobe() + elif input == 'p': + if self.selected.last: + self.setActiveParent(self.selected.last) + elif input == 'r': + # Do wrt reparent + if self.selected.last: + self.reparent(self.selected.last, fWrt = 1) + elif input == 'shift-r': + # Do regular reparent + if self.selected.last: + self.reparent(self.selected.last) + elif input == 's': + if self.selected.last: + self.select(self.selected.last) + elif input == 't': + messenger.send('SEditor-ToggleTexture') + base.toggleTexture() + elif input == 'shift-a': + self.selected.toggleVisAll() + elif input == 'w': + messenger.send('SEditor-ToggleWireframe') + base.toggleWireframe() + elif (input == '[') or (input == '{'): + self.undo() + elif (input == ']') or (input == '}'): + self.redo() + + + def getModifiers(self, input, base): + modifiers = DIRECT_NO_MOD + modifierString = input[: input.find(base)] + if modifierString.find('shift') != -1: + modifiers |= DIRECT_SHIFT_MOD + if modifierString.find('control') != -1: + modifiers |= DIRECT_CONTROL_MOD + if modifierString.find('alt') != -1: + modifiers |= DIRECT_ALT_MOD + return modifiers + + def gotShift(self, modifiers): + return modifiers & DIRECT_SHIFT_MOD + + def gotControl(self, modifiers): + return modifiers & DIRECT_CONTROL_MOD + + def gotAlt(self, modifiers): + return modifiers & DIRECT_ALT_MOD + + def select(self, nodePath, fMultiSelect = 0, fResetAncestry = 1, callback=False): + dnp = self.selected.select(nodePath, fMultiSelect) + if dnp: + messenger.send('DIRECT_preSelectNodePath', [dnp]) + if fResetAncestry: + # Update ancestry + self.ancestry = dnp.getAncestry() + self.ancestry.reverse() + self.ancestryIndex = 0 + # Update the selectedNPReadout + self.selectedNPReadout.reparentTo(aspect2d) + self.selectedNPReadout.setText( + 'Selected:' + dnp.getName()) + # Show the manipulation widget + self.widget.showWidget() + # Update camera controls coa to this point + # Coa2Camera = Coa2Dnp * Dnp2Camera + mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(self.camera) + row = mCoa2Camera.getRow(3) + coa = Vec3(row[0], row[1], row[2]) + self.cameraControl.updateCoa(coa) + # Adjust widgets size + # This uses the additional scaling factor used to grow and + # shrink the widget + self.widget.setScalingFactor(dnp.getRadius()) + # Spawn task to have object handles follow the selected object + taskMgr.remove('followSelectedNodePath') + t = Task.Task(self.followSelectedNodePathTask) + t.dnp = dnp + taskMgr.add(t, 'followSelectedNodePath') + # Send an message marking the event + messenger.send('DIRECT_selectedNodePath', [dnp]) + if callback: + messenger.send('se_selectedNodePath', [dnp, False]) + else: + messenger.send('se_selectedNodePath', [dnp]) + + self.upAncestry() + + if SEditor.widget.fActive: + messenger.send('shift-f') + + def followSelectedNodePathTask(self, state): + mCoa2Render = state.dnp.mCoa2Dnp * state.dnp.getMat(render) + decomposeMatrix(mCoa2Render, + self.scale,self.hpr,self.pos, + CSDefault) + self.widget.setPosHpr(self.pos,self.hpr) + return Task.cont + + def deselect(self, nodePath): + dnp = self.selected.deselect(nodePath) + if dnp: + # Hide the manipulation widget + self.widget.hideWidget() + self.selectedNPReadout.reparentTo(hidden) + self.selectedNPReadout.setText(' ') + taskMgr.remove('followSelectedNodePath') + self.ancestry = [] + # Send an message marking the event + messenger.send('DIRECT_deselectedNodePath', [dnp]) + + def deselectAll(self): + self.selected.deselectAll() + # Hide the manipulation widget + self.widget.hideWidget() + self.selectedNPReadout.reparentTo(hidden) + self.selectedNPReadout.setText(' ') + taskMgr.remove('followSelectedNodePath') + messenger.send('se_deselectedAll') + + def setActiveParent(self, nodePath = None): + # Record new parent + self.activeParent = nodePath + # Update the activeParentReadout + self.activeParentReadout.reparentTo(aspect2d) + self.activeParentReadout.setText( + 'Active Reparent Target:' + nodePath.getName()) + # Alert everyone else + self.activeParentReadout.show() + + def reparent(self, nodePath = None, fWrt = 0): + if (nodePath and self.activeParent and + self.isNotCycle(nodePath, self.activeParent)): + oldParent = nodePath.getParent() + if fWrt: + nodePath.wrtReparentTo(self.activeParent) + else: + nodePath.reparentTo(self.activeParent) + # Alert everyone else + messenger.send('DIRECT_reparent', + [nodePath, oldParent, self.activeParent]) + messenger.send('SGE_Update Explorer',[render]) + self.activeParentReadout.hide() + + + def isNotCycle(self, nodePath, parent): + if nodePath.id() == parent.id(): + print 'DIRECT.reparent: Invalid parent' + return 0 + elif parent.hasParent(): + return self.isNotCycle(nodePath, parent.getParent()) + else: + return 1 + + def fitOnNodePath(self, nodePath = 'None Given'): + if nodePath == 'None Given': + # If nothing specified, try selected node path + nodePath = self.selected.last + SEditor.select(nodePath) + def fitTask(state, self = self): + self.cameraControl.fitOnWidget() + return Task.done + taskMgr.doMethodLater(0.1, fitTask, 'manipulateCamera') + + def isolate(self, nodePath = 'None Given'): + """ Show a node path and hide its siblings """ + # First kill the flashing task to avoid complications + taskMgr.remove('flashNodePath') + # Use currently selected node path if node selected + if nodePath == 'None Given': + nodePath = self.selected.last + # Do we have a node path? + if nodePath: + # Yes, show everything in level + self.showAllDescendants(nodePath.getParent()) + # Now hide all of this node path's siblings + nodePath.hideSiblings() + + def toggleVis(self, nodePath = 'None Given'): + """ Toggle visibility of node path """ + # First kill the flashing task to avoid complications + taskMgr.remove('flashNodePath') + if nodePath == 'None Given': + # If nothing specified, try selected node path + nodePath = self.selected.last + if nodePath: + # Now toggle node path's visibility state + nodePath.toggleVis() + + def removeNodePath(self, nodePath = 'None Given'): + if nodePath == 'None Given': + # If nothing specified, try selected node path + nodePath = self.selected.last + if nodePath: + nodePath.remove() + + def removeAllSelected(self): + self.selected.removeAll() + + def showAllDescendants(self, nodePath = render): + """ Show the level and its descendants """ + nodePath.showAllDescendants() + nodePath.hideCS() + + def upAncestry(self): + if self.ancestry: + l = len(self.ancestry) + i = self.ancestryIndex + 1 + if i < l: + np = self.ancestry[i] + name = np.getName() + if i>0: + type = self.ancestry[i-1].node().getType().getName() + else: + type = self.ancestry[0].node().getType().getName() + + ntype = np.node().getType().getName() + if (name != 'render') and (name != 'renderTop')and(self.checkTypeNameForAncestry(type, ntype)): + self.ancestryIndex = i + self.select(np, 0, 0, True) + + def checkTypeNameForAncestry(self, type, nextType ): + if (type=='ModelRoot'): + if (nextType=='AmbientLight')or(nextType=='PointLight')or(nextType=='DirectionalLight')or(nextType=='Spotlight'): + return True + return False + elif (type=='ModelNode'): + if (nextType=='ModelNode'): + return True + return False + elif (type=='CollisionNode'): + return False + elif (type=='ActorNode'): + return False + elif (type=='AmbientLight')or(type=='PointLight')or(type=='DirectionalLight')or(type=='Spotlight'): + return False + else: + return True + + def downAncestry(self): + if self.ancestry: + l = len(self.ancestry) + i = self.ancestryIndex - 1 + if i >= 0: + np = self.ancestry[i] + name = np.getName() + if (name != 'render') and (name != 'renderTop'): + self.ancestryIndex = i + self.select(np, 0, 0, True) + + + def getAndSetName(self, nodePath): + """ Prompt user for new node path name """ + from tkSimpleDialog import askstring + newName = askstring('Node Path: ' + nodePath.getName(), + 'Enter new name:') + if newName: + nodePath.setName(newName) + messenger.send('DIRECT_nodePathSetName', [nodePath, newName]) + + # UNDO REDO FUNCTIONS + def pushUndo(self, nodePathList, fResetRedo = 1): + # Assemble group of changes + undoGroup = [] + for nodePath in nodePathList: + t = nodePath.getTransform() + undoGroup.append([nodePath, t]) + # Now record group + self.undoList.append(undoGroup) + # Truncate list + self.undoList = self.undoList[-25:] + # Alert anyone who cares + messenger.send('DIRECT_pushUndo') + if fResetRedo and (nodePathList != []): + self.redoList = [] + messenger.send('DIRECT_redoListEmpty') + + def popUndoGroup(self): + # Get last item + undoGroup = self.undoList[-1] + # Strip last item off of undo list + self.undoList = self.undoList[:-1] + # Update state of undo button + if not self.undoList: + messenger.send('DIRECT_undoListEmpty') + # Return last item + return undoGroup + + def pushRedo(self, nodePathList): + # Assemble group of changes + redoGroup = [] + for nodePath in nodePathList: + t = nodePath.getTransform() + redoGroup.append([nodePath, t]) + # Now record redo group + self.redoList.append(redoGroup) + # Truncate list + self.redoList = self.redoList[-25:] + # Alert anyone who cares + messenger.send('DIRECT_pushRedo') + + def popRedoGroup(self): + # Get last item + redoGroup = self.redoList[-1] + # Strip last item off of redo list + self.redoList = self.redoList[:-1] + # Update state of redo button + if not self.redoList: + messenger.send('DIRECT_redoListEmpty') + # Return last item + return redoGroup + + def undo(self): + if self.undoList: + # Get last item off of redo list + undoGroup = self.popUndoGroup() + # Record redo information + nodePathList = map(lambda x: x[0], undoGroup) + self.pushRedo(nodePathList) + # Now undo xform for group + for pose in undoGroup: + # Undo xform + pose[0].setTransform(pose[1]) + # Alert anyone who cares + messenger.send('DIRECT_undo') + + def redo(self): + if self.redoList: + # Get last item off of redo list + redoGroup = self.popRedoGroup() + # Record undo information + nodePathList = map(lambda x: x[0], redoGroup) + self.pushUndo(nodePathList, fResetRedo = 0) + # Redo xform + for pose in redoGroup: + pose[0].setTransform(pose[1]) + # Alert anyone who cares + messenger.send('DIRECT_redo') + + # UTILITY FUNCTIONS + def message(self, text): + taskMgr.remove('hideDirectMessage') + taskMgr.remove('hideDirectMessageLater') + self.directMessageReadout.reparentTo(aspect2d) + self.directMessageReadout.setText(text) + self.hideDirectMessageLater() + + def hideDirectMessageLater(self): + taskMgr.doMethodLater(3.0, self.hideDirectMessage, 'hideDirectMessage') + + def hideDirectMessage(self, state): + self.directMessageReadout.reparentTo(hidden) + return Task.done + + def useObjectHandles(self): + self.widget = self.manipulationControl.objectHandles + self.widget.reparentTo(SEditor.group) + + def hideSelectedNPReadout(self): + self.selectedNPReadout.reparentTo(hidden) + + def hideActiveParentReadout(self): + self.activeParentReadout.reparentTo(hidden) + + def toggleWidgetVis(self): + self.widget.toggleWidget() + + def setCOAMode(self, mode): + self.coaMode = mode + + def isEnabled(self): + return self.fEnabled + + def addUnpickable(self, item): + for iRay in self.iRayList: + iRay.addUnpickable(item) + + def removeUnpickable(self, item): + for iRay in self.iRayList: + iRay.removeUnpickable(item) + + def toggleAutoCamera(self): + self.enableAutoCamera = (self.enableAutoCamera+1)%2 + if self.enableAutoCamera==1: + self.accept('DH_LoadingComplete', self.autoCameraMove) + else: + self.ignore('DH_LoadingComplete') + return + + def autoCameraMove(self, nodePath): + time = 1 + node = DirectNodePath(nodePath) + radius = node.getRadius() + center = node.getCenter() + node.dehighlight() + posB = base.camera.getPos() + hprB = base.camera.getHpr() + posE = Point3((radius*-1.41)+center.getX(), (radius*-1.41)+center.getY(), (radius*1.41)+center.getZ()) + hprE = Point3(-45, -38, 0) + print posB, hprB + print posE, hprE + posInterval1 = base.camera.posInterval(time, posE, bakeInStart = 1) + posInterval2 = base.camera.posInterval(time, posB, bakeInStart = 1) + + hprInterval1 = base.camera.hprInterval(time, hprE, bakeInStart = 1) + hprInterval2 = base.camera.hprInterval(time, hprB, bakeInStart = 1) + + parallel1 = Parallel(posInterval1, hprInterval1) + parallel2 = Parallel(posInterval2, hprInterval2) + + Sequence(Wait(7), parallel1, Wait(1), parallel2).start() + + return + + +class DisplayRegionContext(PandaObject): + regionCount = 0 + def __init__(self, cam): + self.cam = cam + self.camNode = self.cam.node() + self.camLens = self.camNode.getLens() + # set lens change callback + changeEvent = 'dr%d-change-event' % DisplayRegionContext.regionCount + DisplayRegionContext.regionCount += 1 + self.camLens.setChangeEvent(changeEvent) + self.accept(changeEvent, self.camUpdate) + self.iRay = SelectionRay(self.cam) + self.nearVec = Vec3(0) + self.mouseX = 0.0 + self.mouseY = 0.0 + # A Camera node can have more than one display region + # associated with it. Here I assume that there is only + # one display region per camera, since we are defining a + # display region on a per-camera basis. See note in + # DisplayRegionList.__init__() + try: + self.dr = self.camNode.getDr(0) + except: + self.dr = self.camNode.getDisplayRegion(0) + left = self.dr.getLeft() + right = self.dr.getRight() + bottom = self.dr.getBottom() + top = self.dr.getTop() + self.originX = left+right-1 + self.originY = top+bottom-1 + self.scaleX = 1.0/(right-left) + self.scaleY = 1.0/(top-bottom) + self.setOrientation() + self.camUpdate() + + def __getitem__(self,key): + return self.__dict__[key] + + def setOrientation(self): + # MRM This assumes orientation is set on transform above cam + hpr = self.cam.getHpr() + if hpr[2] < 135 and hpr[2]>45 or hpr[2]>225 and hpr[2]<315: + self.isSideways = 1 + elif hpr[2] > -135 and hpr[2] < -45 or hpr[2] < -225 and hpr[2] > -315: + self.isSideways = 1 + else: + self.isSideways = 0 + + # The following take into consideration sideways displays + def getHfov(self): + if self.isSideways: + return self.camLens.getVfov() + else: + return self.camLens.getHfov() + + def getVfov(self): + if self.isSideways: + return self.camLens.getHfov() + else: + return self.camLens.getVfov() + + def setHfov(self,hfov): + if self.isSideways: + self.camLens.setFov(self.camLens.getHfov(), hfov) + else: + self.camLens.setFov(hfov, self.camLens.getVfov()) + + def setVfov(self,vfov): + if self.isSideways: + self.camLens.setFov(vfov, self.camLens.getVfov()) + else: + self.camLens.setFov(self.camLens.getHfov(), vfov) + + def setFov(self,hfov,vfov): + if self.isSideways: + self.camLens.setFov(vfov, hfov) + else: + self.camLens.setFov(hfov, vfov) + + def getWidth(self): + prop = base.win.getProperties() + if prop.hasSize(): + return prop.getXSize() + else: + return 640 + + def getHeight(self): + prop = base.win.getProperties() + if prop.hasSize(): + return prop.getYSize() + else: + return 480 + + def camUpdate(self, lens = None): + # Window Data + self.near = self.camLens.getNear() + self.far = self.camLens.getFar() + self.fovH = self.camLens.getHfov() + self.fovV = self.camLens.getVfov() + self.nearWidth = math.tan(deg2Rad(self.fovH * 0.5)) * self.near * 2.0 + self.nearHeight = math.tan(deg2Rad(self.fovV * 0.5)) * self.near * 2.0 + self.left = -self.nearWidth * 0.5 + self.right = self.nearWidth * 0.5 + self.top = self.nearHeight * 0.5 + self.bottom = -self.nearHeight * 0.5 + + def mouseUpdate(self): + # Mouse Data + # Last frame + self.mouseLastX = self.mouseX + self.mouseLastY = self.mouseY + # Values for this frame + # This ranges from -1 to 1 + if (base.mouseWatcherNode.hasMouse()): + self.mouseX = base.mouseWatcherNode.getMouseX() + self.mouseY = base.mouseWatcherNode.getMouseY() + self.mouseX = (self.mouseX-self.originX)*self.scaleX + self.mouseY = (self.mouseY-self.originY)*self.scaleY + # Delta percent of window the mouse moved + self.mouseDeltaX = self.mouseX - self.mouseLastX + self.mouseDeltaY = self.mouseY - self.mouseLastY + self.nearVec.set((self.nearWidth*0.5) * self.mouseX, + self.near, + (self.nearHeight*0.5) * self.mouseY) + +class DisplayRegionList(PandaObject): + def __init__(self): + self.displayRegionList = [] + i = 0 + # Things are funky if we are oobe + if (hasattr(base, 'oobeMode') and base.oobeMode): + # assume we only have one cam at this point + drc = DisplayRegionContext(base.cam) + self.displayRegionList.append(drc) + else: + # MRM: Doesn't properly handle multiple camera groups anymore + # Assumes everything is under main camera + + # This is following the old way of setting up + # display regions. A display region is set up for + # each camera node in the scene graph. This was done + # so that only display regions in the scene graph are + # considered. The right way to do this is to set up + # a display region for each real display region, and then + # keep track of which are currently active (e.g. use a flag) + # processing only them. + for camIndex in range(len(base.camList)): + cam = base.camList[camIndex] + if cam.getName()=='': + cam.setName('Camera%d' % camIndex) + drc = DisplayRegionContext(cam) + self.displayRegionList.append(drc) + + self.accept("DIRECT-mouse1",self.mouseUpdate) + self.accept("DIRECT-mouse2",self.mouseUpdate) + self.accept("DIRECT-mouse3",self.mouseUpdate) + self.accept("DIRECT-mouse1Up",self.mouseUpdate) + self.accept("DIRECT-mouse2Up",self.mouseUpdate) + self.accept("DIRECT-mouse3Up",self.mouseUpdate) + + def __getitem__(self, index): + return self.displayRegionList[index] + + def __len__(self): + return len(self.displayRegionList) + + def updateContext(self): + self.contextTask(None) + + def setNearFar(self, near, far): + for dr in self.displayRegionList: + dr.camLens.setNearFar(near, far) + + def setNear(self, near): + for dr in self.displayRegionList: + dr.camLens.setNear(near) + + def setFar(self, far): + for dr in self.displayRegionList: + dr.camLens.setFar(far) + + def setFov(self, hfov, vfov): + for dr in self.displayRegionList: + dr.setFov(hfov, vfov) + + def setHfov(self, fov): + for dr in self.displayRegionList: + dr.setHfov(fov) + + def setVfov(self, fov): + for dr in self.displayRegionList: + dr.setVfov(fov) + + def mouseUpdate(self, modifiers = DIRECT_NO_MOD): + for dr in self.displayRegionList: + dr.mouseUpdate() + SEditor.dr = self.getCurrentDr() + + def getCurrentDr(self): + for dr in self.displayRegionList: + if (dr.mouseX >= -1.0 and dr.mouseX <= 1.0 and + dr.mouseY >= -1.0 and dr.mouseY <= 1.0): + return dr + return self.displayRegionList[0] + + def start(self): + # First shutdown any existing task + self.stop() + # Start a new context task + self.spawnContextTask() + + def stop(self): + # Kill the existing context task + taskMgr.remove('DIRECTContextTask') + + def spawnContextTask(self): + taskMgr.add(self.contextTask, 'DIRECTContextTask') + + def removeContextTask(self): + taskMgr.remove('DIRECTContextTask') + + def contextTask(self, state): + # Window Data + self.mouseUpdate() + # hack to test movement + return Task.cont + +# Create one +#__builtins__['direct'] = base.direct = DirectSession() + + + + + + + + + + + + diff --git a/doc/SceneEditor/SideWindow.py b/doc/SceneEditor/SideWindow.py new file mode 100644 index 0000000000..a7736b2353 --- /dev/null +++ b/doc/SceneEditor/SideWindow.py @@ -0,0 +1,459 @@ +################################################################# +# sideWindow.py +# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +################################################################# +from AppShell import * +from TkGlobal import * + +import seSceneGraphExplorer +import Floater +import Slider +import VectorWidgets + +class sideWindow(AppShell): + ################################################################# + # sideWindow(AppShell) + # This class will open a side window wich contains a scene graph and + # a world setting page. + ################################################################# + appversion = '1.0' + appname = 'Navigation Window' + frameWidth = 325 + frameHeight = 580 + frameIniPosX = 0 + frameIniPosY = 110 + padx = 0 + pady = 0 + + lightEnable = 0 + ParticleEnable = 0 + basedriveEnable = 0 + collision = 0 + backface = 0 + texture = 1 + wireframe = 0 + + enableBaseUseDrive = 0 + + def __init__(self, worldColor,lightEnable,ParticleEnable, basedriveEnable,collision, + backface, texture, wireframe, grid, widgetVis, enableAutoCamera, parent = None, nodePath = render, **kw): + self.worldColor = worldColor + self.lightEnable = lightEnable + self.ParticleEnable = ParticleEnable + self.basedriveEnable = basedriveEnable + self.collision = collision + self.backface = backface + self.texture = texture + self.wireframe = wireframe + self.grid = grid + self.enableAutoCamera = enableAutoCamera + self.widgetVis = widgetVis + + # Define the megawidget options. + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + + if parent == None: + self.parent = Toplevel() + else: + self.parent = parent + + AppShell.__init__(self, self.parent) + self.parent.geometry('%dx%d+%d+%d' % (self.frameWidth, self.frameHeight,self.frameIniPosX,self.frameIniPosY)) + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + def appInit(self): + print '----SideWindow is Initialized!!' + + def createInterface(self): + # The interior of the toplevel panel + interior = self.interior() + mainFrame = Frame(interior) + ## Creat NoteBook + self.notebookFrame = Pmw.NoteBook(mainFrame) + self.notebookFrame.pack(fill=BOTH,expand=1) + sgePage = self.notebookFrame.add('Tree Graph') + envPage = self.notebookFrame.add('World Setting') + self.notebookFrame['raisecommand'] = self.updateInfo + + ## Tree Grapgh Page + self.SGE = seSceneGraphExplorer.seSceneGraphExplorer( + sgePage, nodePath = render, + scrolledCanvas_hull_width = 270, + scrolledCanvas_hull_height = 570) + self.SGE.pack(fill = BOTH, expand = 0) + + ## World Setting Page + envPage = Frame(envPage) + pageFrame = Frame(envPage) + self.LightingVar = IntVar() + self.LightingVar.set(self.lightEnable) + self.LightingButton = Checkbutton( + pageFrame, + text = 'Enable Lighting', + variable = self.LightingVar, + command = self.toggleLights) + self.LightingButton.pack(side=LEFT, expand=False) + pageFrame.pack(side=TOP, fill=X, expand=True) + + pageFrame = Frame(envPage) + self.CollisionVar = IntVar() + self.CollisionVar.set(self.collision) + self.CollisionButton = Checkbutton( + pageFrame, + text = 'Show Collision Object', + variable = self.CollisionVar, + command = self.showCollision) + self.CollisionButton.pack(side=LEFT, expand=False) + pageFrame.pack(side=TOP, fill=X, expand=True) + + pageFrame = Frame(envPage) + self.ParticleVar = IntVar() + self.ParticleVar.set(self.ParticleEnable) + self.ParticleButton = Checkbutton( + pageFrame, + text = 'Show Particle Dummy', + variable = self.ParticleVar, + command = self.enableParticle) + self.ParticleButton.pack(side=LEFT, expand=False) + pageFrame.pack(side=TOP, fill=X, expand=True) + + pageFrame = Frame(envPage) + self.baseUseDriveVar = IntVar() + self.baseUseDriveVar.set(self.basedriveEnable) + self.baseUseDriveButton = Checkbutton( + pageFrame, + text = 'Enable base.usedrive', + variable = self.baseUseDriveVar, + command = self.enablebaseUseDrive) + self.baseUseDriveButton.pack(side=LEFT, expand=False) + pageFrame.pack(side=TOP, fill=X, expand=True) + + pageFrame = Frame(envPage) + self.backfaceVar = IntVar() + self.backfaceVar.set(self.backface) + self.backfaceButton = Checkbutton( + pageFrame, + text = 'Enable BackFace', + variable = self.backfaceVar, + command = self.toggleBackface) + self.backfaceButton.pack(side=LEFT, expand=False) + pageFrame.pack(side=TOP, fill=X, expand=True) + + pageFrame = Frame(envPage) + self.textureVar = IntVar() + self.textureVar.set(self.texture) + self.textureButton = Checkbutton( + pageFrame, + text = 'Enable Texture', + variable = self.textureVar, + command = self.toggleTexture) + self.textureButton.pack(side=LEFT, expand=False) + pageFrame.pack(side=TOP, fill=X, expand=True) + + pageFrame = Frame(envPage) + self.wireframeVar = IntVar() + self.wireframeVar.set(self.wireframe) + self.wireframeButton = Checkbutton( + pageFrame, + text = 'Enable Wireframe', + variable = self.wireframeVar, + command = self.toggleWireframe) + self.wireframeButton.pack(side=LEFT, expand=False) + pageFrame.pack(side=TOP, fill=X, expand=True) + + pageFrame = Frame(envPage) + self.gridVar = IntVar() + self.gridVar.set(self.grid) + self.gridButton = Checkbutton( + pageFrame, + text = 'Enable Grid', + variable = self.gridVar, + command = self.toggleGrid) + self.gridButton.pack(side=LEFT, expand=False) + pageFrame.pack(side=TOP, fill=X, expand=True) + + pageFrame = Frame(envPage) + self.widgetVisVar = IntVar() + self.widgetVisVar.set(self.widgetVis) + self.widgetVisButton = Checkbutton( + pageFrame, + text = 'Enable WidgetVisible', + variable = self.widgetVisVar, + command = self.togglewidgetVis) + self.widgetVisButton.pack(side=LEFT, expand=False) + pageFrame.pack(side=TOP, fill=X, expand=True) + + pageFrame = Frame(envPage) + self.enableAutoCameraVar = IntVar() + self.enableAutoCameraVar.set(self.enableAutoCamera) + self.enableAutoCameraButton = Checkbutton( + pageFrame, + text = 'Enable Auto Camera Movement for Loading Objects', + variable = self.enableAutoCameraVar, + command = self.toggleAutoCamera) + self.enableAutoCameraButton.pack(side=LEFT, expand=False) + pageFrame.pack(side=TOP, fill=X, expand=True) + + pageFrame = Frame(envPage) + self.backgroundColor = VectorWidgets.ColorEntry( + pageFrame, text = 'BG Color', value=self.worldColor) + self.backgroundColor['command'] = self.setBackgroundColorVec + self.backgroundColor['resetValue'] = [0,0,0,0] + self.backgroundColor.pack(side=LEFT, expand=False) + self.bind(self.backgroundColor, 'Set background color') + pageFrame.pack(side=TOP, fill=X, expand=True) + + envPage.pack(expand=False) + + ## Set all stuff done + self.notebookFrame.setnaturalsize() + mainFrame.pack(fill = 'both', expand = 1) + + + def createMenuBar(self): + # We don't need menu bar here. + self.menuBar.destroy() + + def onDestroy(self, event): + ################################################################# + # onDestroy(self, event) + # This function will be called when user closed the side window. + # Here we will send out a message with whole data set we will need + # for the next time user open the side window. + ################################################################# + messenger.send('SW_close',[self.worldColor, + self.lightEnable, + self.ParticleEnable, + self.basedriveEnable, + self.collision, + self.backface, + self.texture, + self.wireframe, + self.grid, + self.widgetVis, + self.enableAutoCamera]) + ''' + If you have open any thing, please rewrite here! + ''' + pass + + ############################### + def updateInfo(self, page = 'Tree Graph'): + ################################################################# + # updateInfo(self, page = 'Tree Graph') + # This function will be called when each time user change the main + # page of the window. + # What it dose is to call right function to restore the data for current selected page. + ################################################################# + if page=='Tree Graph': + self.updateTreeGraph() + elif page == 'World Setting': + self.updateWorldSetting() + + def updateTreeGraph(self): + ################################################################# + # updateTreeGraph(self) + # When scene graoh page has been opend, call sceneGraphExplorer to + # updata the tree. + ################################################################# + self.SGE.update() + pass + + def updateWorldSetting(self): + ################################################################# + # updateWorldSetting(self) + # When world setting page has been selected, this function will + # reset those check box in the page to reflect the current world setting. + ################################################################# + self.LightingVar.set(self.lightEnable) + + self.CollisionVar.set(self.collision) + self.ParticleVar.set(self.ParticleEnable) + self.baseUseDriveVar.set(self.basedriveEnable) + self.backgroundColor.set(value = self.worldColor) + pass + + def toggleLights(self): + ################################################################# + # toggleLights(self) + # send out a message to let sceneEditor know we need to toggle the light. + # Then, sceneEditor will pass the message to dataHolder to disable/enable + # the lights. (lightManager is inside the dataHolder) + ################################################################# + self.lightEnable = (self.lightEnable+1)%2 + messenger.send('SW_lightToggle') + pass + + def showCollision(self): + ################################################################# + # showCollision(self) + # This function will send out a message to sceneEditor to toggle + # the visibility of collision objects. + ################################################################# + self.collision = (self.collision+1)%2 + messenger.send('SW_collisionToggle', [self.collision]) + pass + + def enableParticle(self): + ################################################################# + # enableParticle(self) + # This function will send out a message to sceneEditor to toggle + # the visibility of particle objects. + ################################################################# + self.ParticleEnable = (self.ParticleEnable+1)%2 + messenger.send('SW_particleToggle', [self.ParticleEnable]) + pass + + def enablebaseUseDrive(self): + ################################################################# + # enablebaseUseDrive(self) + # This function will toggle the usage of base.useDrive. + # Well, it may not usefull at all. + # + # We won't send out any message in this time to notice + # the sceneEditor this event happend. + # In the other hand, we will restore it back when + # the side window has been closed. + # + ################################################################# + if self.enableBaseUseDrive==0: + print 'Enabled' + base.useDrive() + self.enableBaseUseDrive = 1 + else: + print 'disabled' + #base.useTrackball() + base.disableMouse() + self.enableBaseUseDrive = 0 + self.basedriveEnable = (self.basedriveEnable+1)%2 + pass + + def toggleBackface(self): + ################################################################# + # toggleBackface(self) + # This function will toggle the back face setting. so it will + # render the polygon with two sides. + ################################################################# + base.toggleBackface() + self.backface = (self.backface+1)%2 + return + + def toggleBackfaceFromMainW(self): + ################################################################# + # toggleBackfaceFromMainW(self) + # This function is called by sceneEditor when user used hot key + # to toggle the back face setting in the main panda window. + # In here we will only reset the flag and reset the state of + # check box + ################################################################# + self.backface = (self.backface+1)%2 + self.backfaceButton.toggle() + return + + def toggleTexture(self): + ################################################################# + # toggleTexture(self) + # This function will toggle the txture using option for the whole scene. + ################################################################# + base.toggleTexture() + self.texture = (self.texture+1)%2 + return + + def toggleTextureFromMainW(self): + ################################################################# + # toggleTextureFromMainW(self) + # This function is called by sceneEditor when user used hot key + # to toggle the texture usage from the main panda window. + # In here we will only reset the flag and reset the state of + # check box + ################################################################# + self.texture = (self.texture+1)%2 + self.textureButton.toggle() + return + + def toggleWireframe(self): + ################################################################# + # toggleWireframe(self) + # This function will toggle the wire frame mode. + ################################################################# + base.toggleWireframe() + self.wireframe = (self.wireframe+1)%2 + return + + def toggleWireframeFromMainW(self): + ################################################################# + # toggleWireframeFromMainW(self) + # This function is called by sceneEditor when user used hot key + # to toggle the wire frame mode in the main panda window. + # In here we will only reset the flag and reset the state of + # check box + ################################################################# + self.wireframe = (self.wireframe+1)%2 + self.wireframeButton.toggle() + return + + def toggleGrid(self): + ################################################################# + # toggleGrid(self) + # This function will toggle the usage of the grid. + ################################################################# + self.grid = (self.grid+1)%2 + if self.grid==1: + SEditor.grid.enable() + else: + SEditor.grid.disable() + + def togglewidgetVis(self): + ################################################################# + # togglewidgetVis(self) + # This function will toggle the visibility of the widget of the grid. + ################################################################# + self.widgetVis = (self.widgetVis+1)%2 + SEditor.toggleWidgetVis() + if SEditor.widget.fActive: + messenger.send('shift-f') + return + + def toggleWidgetVisFromMainW(self): + ################################################################# + # toggleWidgetVisFromMainW(self) + # This function is called by sceneEditor when user used hot key + # to toggle the visibility of widgets ('v') from the main panda window. + # In here we will only reset the flag and reset the state of + # check box + ################################################################# + self.widgetVis = (self.widgetVis+1)%2 + self.widgetVisButton.toggle() + return + + def setBackgroundColorVec(self,color): + ################################################################# + # setBackgroundColorVec(self,color) + # Call back function + # This will be called from the colorEntry on the world setting page. + # The "color" here is a list containing three integer data, R, G and B. + ################################################################# + base.setBackgroundColor(color[0]/255.0, + color[1]/255.0, + color[2]/255.0) + self.worldColor = [color[0],color[1],color[2],0] + + def toggleAutoCamera(self): + ################################################################# + # toggleAutoCamera(self) + # This function will toggle the usage of the auto-camera movement + # when user loaded model or actor into the scene. + ################################################################# + self.enableAutoCamera = (self.enableAutoCamera+1)%2 + SEditor.toggleAutoCamera() + return + + def selectPage(self,page='Tree Graph'): + ################################################################# + ################################################################# + self.notebookFrame.selectpage(page) + diff --git a/doc/SceneEditor/Spotlight.egg b/doc/SceneEditor/Spotlight.egg new file mode 100644 index 0000000000..16413fdb70 --- /dev/null +++ b/doc/SceneEditor/Spotlight.egg @@ -0,0 +1,870 @@ + { Z-Up } + + { + "MaxEggPlugin nul.max -o 'E:\sshadhan\Desktop\Spotlight.egg'" +} + Tex1 { + "Spotlight.tif" + format { rgb } + wrapu { repeat } + wrapv { repeat } + minfilter { linear_mipmap_linear } + magfilter { linear } +} + Cone01 { + { + { + 0.0976066 0 0 0 + 0 1.90047e-008 0.0976066 0 + 0 -0.0976066 1.90047e-008 0 + 0 0 0 1 + } + } + Cone01.verts { + 1 { + 2.26078 5.50627 -1.06759e-013 + { 0.0849605 0.0480494 -7.35668e-009 } + { 0.701375 0.870481 } + } + 2 { + 1.82901 5.50627 1.32885 + { 0.0687345 0.0480495 0.0499385 } + { 0.592975 0.870481 } + } + 3 { + 0 -3.8396e-005 1.07212e-006 + { 0.0864707 -0.0355031 0.0280961 } + { 0.701375 0.421306 } + } + 4 { + 0 -3.8396e-005 1.07212e-006 + { 0.0864707 -0.0355031 -0.028096 } + { 0.701375 0.421306 } + } + 5 { + 1.82901 5.50627 1.32885 + { 0.0687345 0.0480495 0.0499385 } + { 0.328401 0.97 } + } + 6 { + 0.698618 5.50627 2.15013 + { 0.0262543 0.0480495 0.0808022 } + { 0.23619 0.97 } + } + 7 { + 0 -3.8396e-005 1.07212e-006 + { 0.0534418 -0.0355031 0.0735564 } + { 0.1792 0.520825 } + } + 8 { + 0 -3.8396e-005 1.07212e-006 + { 0.0864707 -0.0355031 0.0280961 } + { 0.1792 0.520825 } + } + 9 { + -0.698619 5.50627 2.15013 + { -0.0262543 0.0480495 0.0808023 } + { 0.122211 0.97 } + } + 10 { + 0 -3.8396e-005 1.07212e-006 + { 0 -0.0355031 0.0909207 } + { 0.1792 0.520825 } + } + 11 { + -1.82901 5.50627 1.32885 + { -0.0687345 0.0480495 0.0499385 } + { 0.03 0.97 } + } + 12 { + 0 -3.8396e-005 1.07212e-006 + { -0.0534418 -0.0355031 0.0735564 } + { 0.1792 0.520825 } + } + 13 { + -1.82901 5.50627 1.32885 + { -0.0687345 0.0480495 0.0499385 } + { 0.572872 0.870481 } + } + 14 { + -2.26078 5.50627 -1.97643e-007 + { -0.0849605 0.0480494 -2.11178e-008 } + { 0.464471 0.870481 } + } + 15 { + 0 -3.8396e-005 1.07212e-006 + { -0.0864707 -0.0355031 0.028096 } + { 0.464471 0.421306 } + } + 16 { + 0 -3.8396e-005 1.07212e-006 + { -0.0534418 -0.0355031 0.0735564 } + { 0.464471 0.421306 } + } + 17 { + -1.82901 5.50627 -1.32885 + { -0.0687345 0.0480494 -0.0499386 } + { 0.356071 0.870481 } + } + 18 { + 0 -3.8396e-005 1.07212e-006 + { -0.0864707 -0.0355031 -0.028096 } + { 0.464471 0.421306 } + } + 19 { + -1.82901 5.50627 -1.32885 + { -0.0687345 0.0480494 -0.0499386 } + { 0.328401 0.479175 } + } + 20 { + -0.698618 5.50627 -2.15013 + { -0.0262542 0.0480494 -0.0808023 } + { 0.23619 0.479175 } + } + 21 { + 0 -3.8396e-005 1.07212e-006 + { -0.0534418 -0.0355031 -0.0735564 } + { 0.1792 0.03 } + } + 22 { + 0 -3.8396e-005 1.07212e-006 + { -0.0864707 -0.0355031 -0.028096 } + { 0.1792 0.03 } + } + 23 { + 0.698619 5.50627 -2.15013 + { 0.0262543 0.0480494 -0.0808023 } + { 0.122211 0.479175 } + } + 24 { + 0 -3.8396e-005 1.07212e-006 + { 1.21144e-008 -0.0355031 -0.0909207 } + { 0.1792 0.03 } + } + 25 { + 1.82901 5.50627 -1.32885 + { 0.0687345 0.0480495 -0.0499385 } + { 0.03 0.479175 } + } + 26 { + 0 -3.8396e-005 1.07212e-006 + { 0.0534418 -0.0355031 -0.0735564 } + { 0.1792 0.03 } + } + 27 { + 1.82901 5.50627 -1.32885 + { 0.0687345 0.0480495 -0.0499385 } + { 0.809776 0.870481 } + } + 28 { + 0 -3.8396e-005 1.07212e-006 + { 0.0534418 -0.0355031 -0.0735564 } + { 0.701375 0.421306 } + } + 29 { + 0.0729454 8.46019 -0.224503 + { 0.023082 0.0628309 -0.071039 } + { 0.396217 0.36432 } + } + 30 { + -0.0729452 8.46019 -0.224503 + { -0.023082 0.062831 -0.0710389 } + { 0.408118 0.36432 } + } + 31 { + -0.190973 8.46019 -0.138751 + { -0.0604293 0.062831 -0.0439046 } + { 0.417746 0.371315 } + } + 32 { + -0.236056 8.46019 -4.5556e-007 + { -0.0746948 0.062831 -5.86065e-008 } + { 0.421423 0.382634 } + } + 33 { + -0.190973 8.46019 0.13875 + { -0.0604293 0.062831 0.0439045 } + { 0.417746 0.393952 } + } + 34 { + -0.0729453 8.46019 0.224502 + { -0.0230819 0.062831 0.0710389 } + { 0.408118 0.400947 } + } + 35 { + 0.0729452 8.46019 0.224502 + { 0.0230819 0.062831 0.0710389 } + { 0.396217 0.400947 } + } + 36 { + 0.190973 8.46019 0.13875 + { 0.0604293 0.062831 0.0439046 } + { 0.386589 0.393952 } + } + 37 { + 0.236056 8.46019 -6.38597e-007 + { 0.0746948 0.062831 2.14833e-008 } + { 0.382911 0.382634 } + } + 38 { + 0.190973 8.46019 -0.138751 + { 0.0604294 0.062831 -0.0439045 } + { 0.386588 0.371315 } + } + 39 { + 0 -3.8396e-005 1.07212e-006 + { 0.0534418 -0.0355031 0.0735564 } + { 0.701375 0.421306 } + } + 40 { + 0 -3.8396e-005 1.07212e-006 + { 0 -0.0355031 0.0909207 } + { 0.701375 0.421306 } + } + 41 { + 0 -3.8396e-005 1.07212e-006 + { -0.0534418 -0.0355031 0.0735564 } + { 0.701375 0.421306 } + } + 42 { + 0 -3.8396e-005 1.07212e-006 + { -0.0864707 -0.0355031 0.028096 } + { 0.701375 0.421306 } + } + 43 { + 0 -3.8396e-005 1.07212e-006 + { -0.0864707 -0.0355031 -0.028096 } + { 0.701375 0.421306 } + } + 44 { + 0 -3.8396e-005 1.07212e-006 + { -0.0534418 -0.0355031 -0.0735564 } + { 0.701375 0.421306 } + } + 45 { + 0 -3.8396e-005 1.07212e-006 + { 1.21144e-008 -0.0355031 -0.0909207 } + { 0.701375 0.421306 } + } + 46 { + 1.82901 5.50627 1.32885 + { 0.0687345 0.0480495 0.0499385 } + { 0.639867 0.363622 } + } + 47 { + 2.26078 5.50627 -1.06759e-013 + { 0.0849605 0.0480494 -7.35668e-009 } + { 0.531466 0.398844 } + } + 48 { + 0.236056 5.50627 -6.34468e-008 + { 0.0606261 0.0764952 -9.18613e-009 } + { 0.531466 0.233678 } + } + 49 { + 0.190973 5.50627 0.13875 + { 0.0490475 0.0764952 0.0356352 } + { 0.542785 0.23 } + } + 50 { + 1.82901 5.50627 -1.32885 + { 0.0687345 0.0480495 -0.0499385 } + { 0.423066 0.363622 } + } + 51 { + 0.190973 5.50627 -0.13875 + { 0.0490476 0.0764952 -0.0356351 } + { 0.520148 0.23 } + } + 52 { + 0.698619 5.50627 -2.15013 + { 0.0262543 0.0480494 -0.0808023 } + { 0.356071 0.271411 } + } + 53 { + 0.0729454 5.50627 -0.224503 + { 0.0187345 0.0764952 -0.0576588 } + { 0.513153 0.220372 } + } + 54 { + -0.698618 5.50627 -2.15013 + { -0.0262542 0.0480494 -0.0808023 } + { 0.356071 0.157432 } + } + 55 { + -0.0729452 5.50627 -0.224502 + { -0.0187345 0.0764952 -0.0576589 } + { 0.513153 0.208471 } + } + 56 { + -1.82901 5.50627 -1.32885 + { -0.0687345 0.0480494 -0.0499386 } + { 0.423066 0.0652215 } + } + 57 { + -0.190973 5.50627 -0.13875 + { -0.0490475 0.0764952 -0.0356352 } + { 0.520148 0.198843 } + } + 58 { + -2.26078 5.50627 -1.97643e-007 + { -0.0849605 0.0480494 -2.11178e-008 } + { 0.531466 0.03 } + } + 59 { + -0.236056 5.50627 1.1959e-007 + { -0.0606261 0.0764952 -3.15091e-008 } + { 0.531466 0.195166 } + } + 60 { + -1.82901 5.50627 1.32885 + { -0.0687345 0.0480495 0.0499385 } + { 0.639867 0.0652214 } + } + 61 { + -0.190973 5.50627 0.13875 + { -0.0490475 0.0764952 0.0356351 } + { 0.542785 0.198843 } + } + 62 { + -0.698619 5.50627 2.15013 + { -0.0262543 0.0480495 0.0808023 } + { 0.706862 0.157432 } + } + 63 { + -0.0729453 5.50627 0.224502 + { -0.0187344 0.0764952 0.0576589 } + { 0.54978 0.208471 } + } + 64 { + 0.698618 5.50627 2.15013 + { 0.0262543 0.0480495 0.0808022 } + { 0.706862 0.271411 } + } + 65 { + 0.0729452 5.50627 0.224502 + { 0.0187345 0.0764952 0.0576589 } + { 0.54978 0.220372 } + } + 66 { + 0.190973 5.50627 0.13875 + { 0.0490475 0.0764952 0.0356352 } + { 0.829879 0.684615 } + } + 67 { + 0.236056 5.50627 -6.34468e-008 + { 0.0606261 0.0764952 -9.18613e-009 } + { 0.841198 0.684615 } + } + 68 { + 0.236056 8.46019 -6.38597e-007 + { 0.0746948 0.062831 2.14833e-008 } + { 0.841198 0.92558 } + } + 69 { + 0.190973 8.46019 0.13875 + { 0.0604293 0.062831 0.0439046 } + { 0.829879 0.92558 } + } + 70 { + 0.190973 5.50627 -0.13875 + { 0.0490476 0.0764952 -0.0356351 } + { 0.852516 0.684615 } + } + 71 { + 0.190973 8.46019 -0.138751 + { 0.0604294 0.062831 -0.0439045 } + { 0.852516 0.92558 } + } + 72 { + 0.190973 5.50627 -0.13875 + { 0.0490476 0.0764952 -0.0356351 } + { 0.728225 0.03 } + } + 73 { + 0.0729454 5.50627 -0.224503 + { 0.0187345 0.0764952 -0.0576588 } + { 0.737853 0.03 } + } + 74 { + 0.0729454 8.46019 -0.224503 + { 0.023082 0.0628309 -0.071039 } + { 0.737853 0.270965 } + } + 75 { + 0.190973 8.46019 -0.138751 + { 0.0604294 0.062831 -0.0439045 } + { 0.728225 0.270965 } + } + 76 { + -0.0729452 5.50627 -0.224502 + { -0.0187345 0.0764952 -0.0576589 } + { 0.749754 0.03 } + } + 77 { + -0.0729452 8.46019 -0.224503 + { -0.023082 0.062831 -0.0710389 } + { 0.749754 0.270965 } + } + 78 { + -0.190973 5.50627 -0.13875 + { -0.0490475 0.0764952 -0.0356352 } + { 0.759382 0.03 } + } + 79 { + -0.190973 8.46019 -0.138751 + { -0.0604293 0.062831 -0.0439046 } + { 0.759382 0.270965 } + } + 80 { + -0.190973 5.50627 -0.13875 + { -0.0490475 0.0764952 -0.0356352 } + { 0.762271 0.03 } + } + 81 { + -0.236056 5.50627 1.1959e-007 + { -0.0606261 0.0764952 -3.15091e-008 } + { 0.77359 0.03 } + } + 82 { + -0.236056 8.46019 -4.5556e-007 + { -0.0746948 0.062831 -5.86065e-008 } + { 0.77359 0.270965 } + } + 83 { + -0.190973 8.46019 -0.138751 + { -0.0604293 0.062831 -0.0439046 } + { 0.762271 0.270965 } + } + 84 { + -0.190973 5.50627 0.13875 + { -0.0490475 0.0764952 0.0356351 } + { 0.784908 0.03 } + } + 85 { + -0.190973 8.46019 0.13875 + { -0.0604293 0.062831 0.0439045 } + { 0.784908 0.270965 } + } + 86 { + -0.190973 5.50627 0.13875 + { -0.0490475 0.0764952 0.0356351 } + { 0.829879 0.421306 } + } + 87 { + -0.0729453 5.50627 0.224502 + { -0.0187344 0.0764952 0.0576589 } + { 0.839507 0.421306 } + } + 88 { + -0.0729453 8.46019 0.224502 + { -0.0230819 0.062831 0.0710389 } + { 0.839507 0.662271 } + } + 89 { + -0.190973 8.46019 0.13875 + { -0.0604293 0.062831 0.0439045 } + { 0.829879 0.662271 } + } + 90 { + 0.0729452 5.50627 0.224502 + { 0.0187345 0.0764952 0.0576589 } + { 0.851408 0.421306 } + } + 91 { + 0.0729452 8.46019 0.224502 + { 0.0230819 0.062831 0.0710389 } + { 0.851408 0.662271 } + } + 92 { + 0.190973 5.50627 0.13875 + { 0.0490475 0.0764952 0.0356352 } + { 0.861036 0.421306 } + } + 93 { + 0.190973 8.46019 0.13875 + { 0.0604293 0.062831 0.0439046 } + { 0.861036 0.662271 } + } + } + { + { 1 1 1 1 } + { Tex1 } + { 1 2 3 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 3 4 1 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 5 6 7 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 7 8 5 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 6 9 10 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 10 7 6 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 9 11 12 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 12 10 9 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 13 14 15 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 15 16 13 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 14 17 18 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 18 15 14 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 19 20 21 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 21 22 19 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 20 23 24 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 24 21 20 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 23 25 26 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 26 24 23 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 27 1 4 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 4 28 27 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 29 30 31 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 31 32 33 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 33 34 35 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 35 36 37 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 33 35 37 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 31 33 37 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 29 31 37 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 38 29 37 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 3 39 40 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 40 41 42 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 42 43 44 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 44 45 28 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 42 44 28 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 40 42 28 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 3 40 28 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 4 3 28 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 46 47 48 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 48 49 46 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 47 50 51 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 51 48 47 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 50 52 53 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 53 51 50 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 52 54 55 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 55 53 52 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 54 56 57 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 57 55 54 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 56 58 59 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 59 57 56 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 58 60 61 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 61 59 58 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 60 62 63 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 63 61 60 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 62 64 65 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 65 63 62 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 64 46 49 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 49 65 64 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 66 67 68 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 68 69 66 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 67 70 71 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 71 68 67 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 72 73 74 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 74 75 72 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 73 76 77 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 77 74 73 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 76 78 79 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 79 77 76 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 80 81 82 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 82 83 80 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 81 84 85 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 85 82 81 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 86 87 88 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 88 89 86 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 87 90 91 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 91 88 87 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 90 92 93 { Cone01.verts } } + } + { + { 1 1 1 1 } + { Tex1 } + { 93 91 90 { Cone01.verts } } + } +} diff --git a/doc/SceneEditor/collisionWindow.py b/doc/SceneEditor/collisionWindow.py new file mode 100644 index 0000000000..c5e9acd228 --- /dev/null +++ b/doc/SceneEditor/collisionWindow.py @@ -0,0 +1,302 @@ +################################################################# +# collisionWindow.py +# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +################################################################# +# Import Tkinter, Pmw, and the floater code from this directory tree. +from AppShell import * +from TkGlobal import * +from seColorEntry import * +import VectorWidgets +import Floater +import Slider +import string +import math +import types + + +class collisionWindow(AppShell): + ################################################################# + # This will open a talk window for user to set the collision object + # In here, we won't finish the whole process to generate the + # collision object, half of process will be finished by dataHolder. + ################################################################# + # Override class variables + appname = 'Creating Collision Object' + frameWidth = 600 + frameHeight = 300 + + widgetsDict = {} + + # Define the types of collision we take care here + collisionType = ['collisionPolygon', + 'collisionSphere', + 'collisionSegment', + 'collisionRay'] + + def __init__(self, nodePath, parent = None, **kw): + + self.nodePath = nodePath + self.objType = 'collisionSphere' # set default type to Collision Sphere + + INITOPT = Pmw.INITOPT + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + + # Initialize the superclass + AppShell.__init__(self) + + # Execute option callbacks + self.initialiseoptions(collisionWindow) + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + def createInterface(self): + # Handle to the toplevels interior + interior = self.interior() + menuBar = self.menuBar + self.menuBar.destroy() + + # Create a frame to hold all stuff + mainFrame = Frame(interior) + + frame = Frame(mainFrame) + + self.collisionTypeEntry = self.createcomponent( + 'Collision Type', (), None, + Pmw.ComboBox, (frame,), + labelpos = W, label_text='Collision Object Type:', entry_width = 20, + selectioncommand = self.setObjectType, + scrolledlist_items = self.collisionType) + self.collisionTypeEntry.pack(side=LEFT, padx=3) + + label = Label(frame, text='Parent NodePath: '+ self.nodePath.getName(), font=('MSSansSerif', 12), + relief = RIDGE) + label.pack(side=LEFT,expand=0,fill=X, padx=20) + + frame.pack(side=TOP, fill=X, expand=True, padx=3) + self.collisionTypeEntry.selectitem('collisionSphere', setentry=True) + + self.inputZone = Pmw.Group(mainFrame, tag_pyclass = None) + self.inputZone.pack(fill='both',expand=1) + settingFrame = self.inputZone.interior() + + ############################################ + # Notebook pages for specific object setting + ############################################ + self.objNotebook = Pmw.NoteBook(settingFrame, tabpos = None, + borderwidth = 0) + PolygonPage = self.objNotebook.add('Polygon') + SpherePage = self.objNotebook.add('Sphere') + SegmentPage = self.objNotebook.add('Segment') + RayPage = self.objNotebook.add('Ray') + self.objNotebook.selectpage('Sphere') + # Put this here so it isn't called right away + self.objNotebook['raisecommand'] = self.updateObjInfo + + # Polygon object setting + + Interior = Frame(PolygonPage) + label = Label(Interior, text='Attention! All Coordinates Are Related To Its Parent Node!') + label.pack(side=LEFT,expand=0,fill=X, padx=1) + Interior.pack(side=TOP, expand=0,fill=X) + + self.createPosEntry(PolygonPage, catagory='Polygon', id='Point A') + self.createPosEntry(PolygonPage, catagory='Polygon', id='Point B') + self.createPosEntry(PolygonPage, catagory='Polygon', id='Point C') + + # Sphere object setting + + Interior = Frame(SpherePage) + label = Label(Interior, text='Attention! All Coordinates Are Related To Its Parent Node!') + label.pack(side=LEFT,expand=0,fill=X, padx=1) + Interior.pack(side=TOP, expand=0,fill=X) + + self.createPosEntry(SpherePage, catagory='Sphere', id='Center Point') + + self.createEntryField(SpherePage,catagory='Sphere', id='Size', + value = 1.0, + command = None, + initialState='normal', + side = 'top') + + # Segment object setting + + Interior = Frame(SegmentPage) + label = Label(Interior, text='Attention! All Coordinates Are Related To Its Parent Node!') + label.pack(side=LEFT,expand=0,fill=X, padx=1) + Interior.pack(side=TOP, expand=0,fill=X) + + self.createPosEntry(SegmentPage, catagory='Segment', id='Point A') + self.createPosEntry(SegmentPage, catagory='Segment', id='Point B') + + # Ray object setting + + Interior = Frame(RayPage) + label = Label(Interior, text='Attention! All Coordinates Are Related To Its Parent Node!') + label.pack(side=LEFT,expand=0,fill=X, padx=1) + Interior.pack(side=TOP, expand=0,fill=X) + + self.createPosEntry(RayPage, catagory='Ray', id='Origin') + self.createPosEntry(RayPage, catagory='Ray', id='Direction') + + + + self.objNotebook.setnaturalsize() + self.objNotebook.pack(expand = 1, fill = BOTH) + + self.okButton = Button(mainFrame, text="OK", command=self.okPress,width=10) + self.okButton.pack(fill=BOTH,expand=0,side=RIGHT) + + mainFrame.pack(expand=1, fill = BOTH) + + def onDestroy(self, event): + messenger.send('CW_close') + ''' + If you have open any thing, please rewrite here! + ''' + pass + + def setObjectType(self, typeName = 'collisionSphere'): + ################################################################# + # setObjectType(self, typeName = 'collisionSphere') + # Call back function + # This function will be called when user select target collision + # type on the combo box on the panel. + # Basically, this function's job is to switch the notebook page to right one. + ################################################################# + self.objType = typeName + if self.objType=='collisionPolygon': + self.objNotebook.selectpage('Polygon') + elif self.objType=='collisionSphere': + self.objNotebook.selectpage('Sphere') + elif self.objType=='collisionSegment': + self.objNotebook.selectpage('Segment') + elif self.objType=='collisionRay': + self.objNotebook.selectpage('Ray') + + return + + def updateObjInfo(self, page=None): + ################################################################# + # Nothing. Unlike in the lighting panel, we don't have to keep data + # once user switch the page. + ################################################################# + return + + def okPress(self): + ################################################################# + # okPress(self) + # This function will be called when user click on the Ok button. + # Then this function will collect all parameters that we need to create + # a collision Object from the panel and generate the colision Object. + # In the last, it will send the object out with a message to dataHolder to + # put the object into a CollisionNode and attach it to the target nodePath + ################################################################# + collisionObject = None + print self.objType + if self.objType=='collisionPolygon': + pointA = Point3(float(self.widgetDict['PolygonPoint A'][0]._entry.get()), + float(self.widgetDict['PolygonPoint A'][1]._entry.get()), + float(self.widgetDict['PolygonPoint A'][2]._entry.get())) + pointB = Point3(float(self.widgetDict['PolygonPoint B'][0]._entry.get()), + float(self.widgetDict['PolygonPoint B'][1]._entry.get()), + float(self.widgetDict['PolygonPoint B'][2]._entry.get())) + pointC = Point3(float(self.widgetDict['PolygonPoint C'][0]._entry.get()), + float(self.widgetDict['PolygonPoint C'][1]._entry.get()), + float(self.widgetDict['PolygonPoint C'][2]._entry.get())) + collisionObject = CollisionPolygon(pointA, pointB, pointC) + + elif self.objType=='collisionSphere': + collisionObject = CollisionSphere(float(self.widgetDict['SphereCenter Point'][0]._entry.get()), + float(self.widgetDict['SphereCenter Point'][1]._entry.get()), + float(self.widgetDict['SphereCenter Point'][2]._entry.get()), + float(self.widgetDict['SphereSize'].getvalue())) + + elif self.objType=='collisionSegment': + pointA = Point3(float(self.widgetDict['SegmentPoint A'][0]._entry.get()), + float(self.widgetDict['SegmentPoint A'][1]._entry.get()), + float(self.widgetDict['SegmentPoint A'][2]._entry.get())) + pointB = Point3(float(self.widgetDict['SegmentPoint B'][0]._entry.get()), + float(self.widgetDict['SegmentPoint B'][1]._entry.get()), + float(self.widgetDict['SegmentPoint B'][2]._entry.get())) + + collisionObject = CollisionSegment() + collisionObject.setPointA(pointA) + collisionObject.setFromLens(base.cam.node(), Point2( -1, 1 )) ## You must set up the camera lensNode before you set point B.... + collisionObject.setPointB(pointB) + + elif self.objType=='collisionRay': + point = Point3(float(self.widgetDict['RayOrigin'][0]._entry.get()), + float(self.widgetDict['RayOrigin'][1]._entry.get()), + float(self.widgetDict['RayOrigin'][2]._entry.get())) + + vector = Vec3(float(self.widgetDict['RayDirection'][0]._entry.get()), + float(self.widgetDict['RayDirection'][1]._entry.get()), + float(self.widgetDict['RayDirection'][2]._entry.get())) + + print vector, point + + collisionObject = CollisionRay() + collisionObject.setOrigin(point) + collisionObject.setDirection(vector) + #collisionObject.setFromLens(base.cam.node(), Point2( -1, 1 )) ## You must set up the camera lensNode before you set up others... + + if self.objType=='collisionPolygon': + messenger.send('CW_addCollisionObj', [collisionObject, self.nodePath, pointA, pointB, pointC]) + else: + messenger.send('CW_addCollisionObj', [collisionObject, self.nodePath]) + + self.quit() + + return + + def createPosEntry(self, contentFrame, catagory, id): + posInterior = Frame(contentFrame) + label = Label(posInterior, text=id+':') + label.pack(side=LEFT,expand=0,fill=X, padx=1) + self.posX = self.createcomponent('posX'+catagory+id, (), None, + Floater.Floater, (posInterior,), + text = 'X', relief = FLAT, + value = 0.0, + entry_width = 6) + + self.posX.pack(side=LEFT,expand=0,fill=X, padx=1) + + self.posY = self.createcomponent('posY'+catagory+id, (), None, + Floater.Floater, (posInterior,), + text = 'Y', relief = FLAT, + value = 0.0, + entry_width = 6) + self.posY.pack(side=LEFT, expand=0,fill=X, padx=1) + + self.posZ = self.createcomponent('posZ'+catagory+id, (), None, + Floater.Floater, (posInterior,), + text = 'Z', relief = FLAT, + value = 0.0, + entry_width = 6) + self.posZ.pack(side=LEFT, expand=0,fill=X, padx=1) + self.widgetDict[catagory+id]=[self.posX, self.posY, self.posZ] + posInterior.pack(side=TOP, expand=0,fill=X, padx=3, pady=3) + return + + def createEntryField(self, parent, catagory, id, value, + command, initialState, labelWidth = 6, + side = 'left', fill = X, expand = 0, + validate = None, + defaultButton = False, buttonText = 'Default',defaultFunction = None ): + frame = Frame(parent) + widget = Pmw.EntryField(frame, labelpos='w', label_text = id+':', + value = value, entry_font=('MSSansSerif', 10),label_font=('MSSansSerif', 10), + modifiedcommand=command, validate = validate, + label_width = labelWidth) + widget.configure(entry_state = initialState) + widget.pack(side=LEFT) + self.widgetDict[catagory+id] = widget + if defaultButton and (defaultFunction!=None): + widget = Button(frame, text=buttonText, font=('MSSansSerif', 10), command = defaultFunction) + widget.pack(side=LEFT, padx=3) + self.widgetDict[catagory+id+'-'+'DefaultButton']=widget + + frame.pack(side = side, fill = fill, expand = expand,pady=3) diff --git a/doc/SceneEditor/controllerWindow.py b/doc/SceneEditor/controllerWindow.py new file mode 100644 index 0000000000..1522bd731e --- /dev/null +++ b/doc/SceneEditor/controllerWindow.py @@ -0,0 +1,697 @@ +################################################################# +# controllerWindow.py +# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +################################################################# +# Import Tkinter, Pmw, and the floater code from this directory tree. +from AppShell import * +from TkGlobal import * +import VectorWidgets +import Floater +import string + +# Define the Category +KEYBORAD = 'Keyboard-' +TRACKER = 'Tarcker-' + +class controllerWindow(AppShell): + ################################################################# + # This will open a talk window for user to set the control mechanism + # In here, user can choose to control what object by keyboard or other inputs. + ################################################################# + + # Override class variables + appname = 'Controller Panel' + frameWidth = 500 + frameHeight = 500 + + widgetsDict = {} # category-id : widget obj + + # setup the type of controller we handle here. + controllerList = ['Keyboard', + 'Tracker'] + + # Default Keyboard setting + keyboardMapDict = {} + keyboardSpeedDict = {} + + def __init__(self, listOfObj, controlType , dataList, parent = None, **kw): + if controlType == 'Keyboard': + self.nodePath = dataList[0] # Default setting -> mainly used for Keyboard control now. + self.nameOfNode = self.nodePath.getName() + self.controllType = 'Keyboard' + self.keyboardMapDict.clear() + self.keyboardMapDict = dataList[1] + self.keyboardSpeedDict.clear() + self.keyboardSpeedDict = dataList[2] + + self.listOfObj = listOfObj + self.keepControl = False + + INITOPT = Pmw.INITOPT + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + + # Initialize the superclass + AppShell.__init__(self) + + # Execute option callbacks + self.initialiseoptions(controllerWindow) + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + def createInterface(self): + # Handle to the toplevels interior + interior = self.interior() + menuBar = self.menuBar + + # We don't need menu bar here + self.menuBar.destroy() + + # Create a frame to hold all stuff + mainFrame = Frame(interior) + + # A comboBox to switch the controller + frame = Frame(mainFrame) + self.cotrollerTypeEntry = self.createcomponent( + 'Controller Type', (), None, + Pmw.ComboBox, (frame,), + labelpos = W, label_text='Controller Type:', entry_width = 20,entry_state = DISABLED, + selectioncommand = self.setControllerType, + scrolledlist_items = self.controllerList) + self.cotrollerTypeEntry.pack(side=LEFT) + frame.pack(side=TOP, fill=X, expand=False, pady = 3) + self.cotrollerTypeEntry.selectitem('Keyboard', setentry=True) + + self.inputZone = Pmw.Group(mainFrame, tag_pyclass = None) + self.inputZone.pack(fill='both',expand=1) + settingFrame = self.inputZone.interior() + + ################################################### + # Notebook pages for specific controller setting # + ################################################### + self.contentWidge = self.createcomponent( + 'scrolledFrame', + (), None, + Pmw.ScrolledFrame, (settingFrame,), + hull_width = 200, hull_height = 300, + usehullsize = 1) + self.contentFrame = self.contentWidge.component('frame') + self.contentWidge.pack(fill = 'both', expand = 1,padx = 3, pady = 5) + self.objNotebook = Pmw.NoteBook(self.contentFrame, tabpos = None, + borderwidth = 0) + keyboardPage = self.objNotebook.add('Keyboard') + tarckerPage = self.objNotebook.add('Tracker') + self.objNotebook.selectpage('Keyboard') + self.objNotebook.pack(side = TOP, fill='both',expand=False) + # Put this here so it isn't called right away + self.objNotebook['raisecommand'] = self.updateControlInfo + + # Keyboard setting + assignFrame = Frame(keyboardPage) + + Interior = Frame(assignFrame) + widget = self.createcomponent( + 'Target Type', (), None, + Pmw.ComboBox, (Interior,), + labelpos = W, label_text='Target Object:', entry_width = 20, entry_state = DISABLED, + selectioncommand = self.setTargetObj, + scrolledlist_items = self.listOfObj) + widget.pack(side=LEFT, padx=3) + Interior.pack(side=TOP, fill=X, expand=True, pady = 5) + widget.selectitem(self.nameOfNode, setentry=True) + self.widgetsDict[KEYBORAD+'ObjList'] = widget + + inputZone = Pmw.Group(assignFrame, tag_pyclass = None) + inputZone.pack(fill='both',expand=1) + settingFrame = inputZone.interior() + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Assign a Key For:').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True,pady = 6 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Forward :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Forward key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyForward'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyForward'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Forward Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedForward'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedForward'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Backward :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Backward key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyBackward'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyBackward'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Backward Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedBackward'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedBackward'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Right :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Right key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyRight'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyRight'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Right Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedRight'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedRight'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Left :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Left key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyLeft'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyLeft'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Left Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedLeft'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedLeft'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Up :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Up key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyUp'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyUp'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Up Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedUp'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedUp'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Down :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Down key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyDown'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyDown'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Down Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedDown'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedDown'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Turn Right:', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Turn Right key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyTurnRight'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyTurnRight'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Turn Right Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedTurnRight'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedTurnRight'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Turn Left :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Turn Left key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyTurnLeft'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyTurnLeft'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Turn Left Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedTurnLeft'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedTurnLeft'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Turn UP :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Turn UP key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyTurnUp'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyTurnUp'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Turn UP Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedTurnUp'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedTurnUp'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Turn Down :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Turn Down key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyTurnDown'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyTurnDown'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Turn Down Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedTurnDown'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedTurnDown'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Roll Right:', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Roll Right key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyRollRight'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyRollRight'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Roll Right Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedRollRight'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedRollRight'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Roll Left :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Roll Left key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyRollLeft'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyRollLeft'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Roll Left Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedRollLeft'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedRollLeft'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Scale UP :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale UP key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyScaleUp'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyScaleUp'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale UP Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedScaleUp'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedScaleUp'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Scale Down:', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Down key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyScaleDown'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyScaleDown'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Down Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedScaleDown'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedScaleDown'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Scale X UP :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale X UP key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyScaleXUp'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyScaleXUp'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale X UP Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedScaleXUp'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedScaleXUp'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Scale X Down:', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale X Down key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyScaleXDown'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyScaleXDown'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Down X Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedScaleXDown'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedScaleXDown'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Scale Y UP :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Y UP key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyScaleYUp'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyScaleYUp'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Y UP Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedScaleYUp'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedScaleYUp'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Scale Y Down:', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Y Down key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyScaleYDown'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyScaleYDown'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Down XY Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedScaleYDown'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedScaleYDown'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Scale Z UP :', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Z UP key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyScaleZUp'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyScaleZUp'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Z UP Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedScaleZUp'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedScaleZUp'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + Interior = Frame(settingFrame) + widget = Label(Interior, text = 'Scale Z Down:', width = 20, anchor = W).pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Z Down key', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardMapDict['KeyScaleZDown'], + labelpos = W, label_text='Key :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'KeyScaleZDown'] = widget + widget = Label(Interior, text = ' ').pack(side=LEFT, expand = False) + widget = self.createcomponent( + 'Scale Down Z Speed', (), None, + Pmw.EntryField, (Interior,), + value = self.keyboardSpeedDict['SpeedScaleZDown'], + labelpos = W, label_text='Speed :', entry_width = 10) + widget.pack(side=LEFT, expand = False) + self.widgetsDict[KEYBORAD+'SpeedScaleZDown'] = widget + widget = Label(Interior, text = 'Per Seceond').pack(side=LEFT, expand = False) + Interior.pack(side=TOP, fill=X, expand=True, pady = 4 ) + + assignFrame.pack(side=TOP, expand=True, fill = X) + keyboardPage.pack(side=TOP, expand=True, fill = X) + + #################################################################### + #################################################################### + # End of Keyboard control page + #################################################################### + #################################################################### + # Pack the mainFrame + frame = Frame(mainFrame) + widget = Button(frame, text='OK', width = 13, command=self.ok_press).pack(side=RIGHT) + widget = Button(frame, text='Enable Control', width = 13, command=self.enableControl).pack(side=LEFT) + widget = Button(frame, text='Disable Control', width = 13, command=self.disableControl).pack(side=LEFT) + widget = Button(frame, text='Save & Keep', width = 13, command=self.saveKeepControl).pack(side=LEFT) + frame.pack(side = BOTTOM, expand=1, fill = X) + mainFrame.pack(expand=1, fill = BOTH) + + def onDestroy(self, event): + # Check if user wish to keep the control after the window closed. + if not self.keepControl: + messenger.send('ControlW_controlDisable',[self.controllType]) + messenger.send('ControlW_close') + ''' + If you have open any thing, please rewrite here! + ''' + pass + + def ok_press(self): + #################################################################### + # ok_press(self) + # After user clicked on "OK" button, this function will be called. + # This function will collect data from the panel and send it back to + # sceneEditor and close the window. It won't activate control at all. + #################################################################### + if self.controllType=='Keyboard': + for index in self.keyboardMapDict: + self.keyboardMapDict[index] = self.widgetsDict['Keyboard-'+index].getvalue() + for index in self.keyboardSpeedDict: + self.keyboardSpeedDict[index] = float(self.widgetsDict['Keyboard-'+index].getvalue()) + messenger.send('ControlW_controlSetting', ['Keyboard', [self.nodePath, self.keyboardMapDict, self.keyboardSpeedDict]]) + self.quit() + return + + def enableControl(self): + #################################################################### + # enableControl(self) + # Call back function. + # THis function will be called each time when user clicks on the + # "Enable Control" button. This function will do pretty much + # the same thing with ok_press function, except that this function + # will activate the control process in sceneEditor. + # However, if user didn't click on the button "Keep ANd Save," + # the control process will be terminated when user closed the panel. + #################################################################### + if self.controllType=='Keyboard': + for index in self.keyboardMapDict: + self.keyboardMapDict[index] = self.widgetsDict['Keyboard-'+index].getvalue() + for index in self.keyboardSpeedDict: + self.keyboardSpeedDict[index] = float(self.widgetsDict['Keyboard-'+index].getvalue()) + messenger.send('ControlW_controlEnable', ['Keyboard', [self.nodePath, self.keyboardMapDict, self.keyboardSpeedDict]]) + return + + def disableControl(self): + #################################################################### + # disableControl(self) + # This function will send out a message to sceneEditor to stop the + # control task. + #################################################################### + messenger.send('ControlW_controlDisable',[self.controllType]) + return + + def setControllerType(self, typeName = 'Keyboard'): + ################################################################# + # setControllerType(self, typeName = 'Keyboard') + # Call back function + # This function will be called when user select the type of + # controller they want on the combo box on the panel. + # Basically, this function's job is to switch the notebook page to right one. + ################################################################# + self.controllType = typeName + if self.controllType=='Keyboard': + self.objNotebook.selectpage('Keyboard') + elif self.controllType=='Tracker': + self.objNotebook.selectpage('Tracker') + + return + + def updateControlInfo(self, page=None): + ################################################################# + # Nothing. Unlike in the lighting panel, we don't have to keep data + # once user switch the page. + ################################################################# + return + + def setTargetObj(self, name = None, tracker = None): + ################################################################# + # setTargetObj(self, name = None) + # setup the target object we want to control + ################################################################# + if tracker == None: # Call from Keyboard page. + if name=='camera': + self.nodePath = camera + else: + messenger.send('ControlW_require',[name]) + return + + def resetNameList(self, list, name = None, nodePath = None): + #################################################################### + # resetNameList(self, list, name = None, nodePath = None) + # This function will be called be sceneEditor in order to reset the + # object list inside the panel. + # list here is a name list for all objects that can be set on control + # in the scene. If user has specify a name and a nodePath in, it will + # check if the name is equal to the name of current control target. + # If so, it will change the name showed on panel. + #################################################################### + self.widgetsDict['Keyboard-ObjList'].setlist(list) + if name != None: + if self.nameOfNode == name: + self.nameOfNode = self.nodePath.getName() + self.widgetsDict['Keyboard-ObjList'].selectitem(self.nameOfNode, setentry=True) + return + + def setNodePathIn(self, nodePath): + #################################################################### + # setNodePathIn(self, nodePath) + # THis function will be called by sceneEditor. + # After we send out a message to sceneEditor in setTargetObj function, + # This function will be called by sceneEditor after we get the reference + # of target object from dataHolder. + # This function will keep the reference. + #################################################################### + self.nodePath = nodePath + self.nameOfNode = self.nodePath.getName() + return + + def saveKeepControl(self): + ################################################################# + # saveKeepControl(self) + # When any time user has click on the "Save & Keep" button, + # This function will be called. + # This function will send out the message to sceneEditor to process + # the saving. Also, this function will change the "self.keepControl" flag. + # So, when user closes the window with control enabled, it will keep + # the control process runnning. otherwise program will disable the + # control automatically when panel has been closed. + # + # It doesn't mean that this function will automatically enable the + # control when user closed the window!! + # This flag will only decide that we will send out a "stopControl" + # message or not. + # + ################################################################# + self.keepControl = True + if self.controllType=='Keyboard': + for index in self.keyboardMapDict: + self.keyboardMapDict[index] = self.widgetsDict['Keyboard-'+index].getvalue() + for index in self.keyboardSpeedDict: + self.keyboardSpeedDict[index] = float(self.widgetsDict['Keyboard-'+index].getvalue()) + print self.nodePath + messenger.send('ControlW_saveSetting', ['Keyboard', [self.nodePath, self.keyboardMapDict, self.keyboardSpeedDict]]) + return + + diff --git a/doc/SceneEditor/dataHolder.py b/doc/SceneEditor/dataHolder.py new file mode 100644 index 0000000000..9033060ea4 --- /dev/null +++ b/doc/SceneEditor/dataHolder.py @@ -0,0 +1,1071 @@ +############################### +# TK and PMW INTERFACE MODULES# +############################### +from TkGlobal import* +from tkFileDialog import * +import Pmw +import tkFileDialog +import tkMessageBox +import Dial +import Floater + + +############################# +# Scene Editor Python Files # +############################# +from seLights import * # All the scene editor lighting +from seFileSaver import * # The actual File Saving Module which generates Python code + +################################ +#Panda Modules # +################################ +import Actor + +############################### +# Core Python Modules # +############################### +import os +import string +import sys + +import seParticleEffect +import seParticles + +###################################################################################################### +# Data Holder +# This class will actually hold all data in the form of dictionaries +# This is essentially the entire state of the scene +# When saving a scene, the dictionaries will be accessed and code generated for the loaded content +# Dictionaries are used to access the objects in the scene and their properties so code can be generated +# Write/Use Accessor Methods to interface with these dictionaries +###################################################################################################### + +class dataHolder: + + ModelDic = {} # Name: Model Nodepath; ModelRoot (whatever loader.loadModel() returns) + ModelRefDic = {} # Name:File Path; (whatever the Open File Dialog Returns) + ActorDic = {} # Name:Actor Actor; Nodepath, ModelRoot (whatever Actor.Actor() returns) + ActorRefDic = {} # Name:File Path; (whatever the Open File Dialog Returns) + curveDict = {} # Node Name: CurveCollection; The actual curve collection data + collisionDict = {} # Node Name: collisionNode; collisionNode in which contains a collision object + blendAnimDict = {} # Node Name: Dictionary of blended animation; User blened animation will be saved in here. + # the data structure in the inner dictionary is + # {"name of blended animation" : [Animation Name A, Animation Name B, Effect(Float)]} + collisionVisable = True # A flag used to record that collision objects are visable or not + dummyDict = {} # Node Name: Dummy Obj; All Object created as a dummy will be save here. + particleDict={} # "Effect Name": Effect Object + particleNodes={} # "Effect Name": Node which is a parent to the effect used to move it aruond easily + ModelNum = 0 # Count of number of models loaded + ActorNum = 0 # Count of number of animations loaded + theScene=None # Global variable to hold a loaded scene + CollisionHandler = CollisionHandlerEvent() # This object shows what happend when collision appeared + # Now, the default is CollisionHandlerEvent Type, which will just send out message when collision happend + + controlType = 'Keyboard' # default control input setting + # Default Control setting for keyboard. + controlTarget = camera + # This two dictionary set the basic setting for the keyboard contorl + # Those dictionaries will be passed into controller panel each time it has been opend + # Do NOT change anything about INDEX in the dictionary!! (But it's OK to change the values) + keyboardMapDict = {'KeyForward':'arrow_up', + 'KeyBackward':'arrow_down', + 'KeyLeft':'arrow_left', + 'KeyRight':'arrow_right', + 'KeyUp':'', + 'KeyDown':'', + 'KeyTurnRight':'', + 'KeyTurnLeft':'', + 'KeyTurnUp':'', + 'KeyTurnDown':'', + 'KeyRollRight':'', + 'KeyRollLeft':'', + 'KeyScaleUp':'', + 'KeyScaleDown':'', + 'KeyScaleXUp':'', + 'KeyScaleXDown':'', + 'KeyScaleYUp':'', + 'KeyScaleYDown':'', + 'KeyScaleZUp':'', + 'KeyScaleZDown':''} + keyboardSpeedDict = {'SpeedForward': 0, + 'SpeedBackward': 0, + 'SpeedLeft': 0, + 'SpeedRight': 0, + 'SpeedUp': 0, + 'SpeedDown': 0, + 'SpeedTurnRight': 0, + 'SpeedTurnLeft': 0, + 'SpeedTurnUp': 0, + 'SpeedTurnDown': 0, + 'SpeedRollRight':0, + 'SpeedRollLeft':0, + 'SpeedScaleUp':0, + 'SpeedScaleDown':0, + 'SpeedScaleXUp':0, + 'SpeedScaleXDown':0, + 'SpeedScaleYUp':0, + 'SpeedScaleYDown':0, + 'SpeedScaleZUp':0, + 'SpeedScaleZDown':0} + + def __init__(self): + + # Creat light manager to contorl the lighting + self.lightManager = seLightManager() + self.lightManager.allOn() + + # Initialize the basic message formate from CollisionHandler + self.CollisionHandler.setInPattern("%fnenter%in") + self.CollisionHandler.setOutPattern("%fnexit%in") + + pass + + + def resetAll(self): + ################################################################# + # resetAll(self) + # This function will reset the whole scene + ################################################################# + # Delete Everything in the Scene Graph + for index in self.ModelDic: + self.ModelDic[index].removeNode() + for index in self.ActorDic: + self.ActorDic[index].removeNode() + for index in self.dummyDict: + self.dummyDict[index].removeNode() + for index in self.collisionDict: + self.collisionDict[index].removeNode() + for index in self.particleNodes: + self.particleDict[index].cleanup() + self.particleNodes[index].removeNode() + + # Clear all data containers in the dataHolder + self.ModelDic.clear() + self.ModelRefDic.clear() + self.ActorDic.clear() + self.ActorRefDic.clear() + self.dummyDict.clear() + self.lightManager.deleteAll() + self.blendAnimDict.clear() + self.particleDict.clear() + self.particleNodes.clear() + + self.ModelNum=0 + self.ActorNum=0 + self.theScene=None + messenger.send('SGE_Update Explorer',[render]) + print 'Scene should be cleaned up!' + + def removeObj(self, nodePath): + ################################################################# + # removeObj(self, nodePath) + # This function will take one nodePath obj as a input + # and will remove this node from scene if it is legal to be removed. + # Also, this function will remove all children nodes belong to this specific nodePath by recursive call. + ################################################################# + name = nodePath.getName() + + ## Check if there is any child node, if so, remove it. + childrenList = nodePath.getChildrenAsList() + + + if self.ModelDic.has_key(name): + del self.ModelDic[name] + del self.ModelRefDic[name] + if len(childrenList) != 0: + for node in childrenList: + self.removeObj(node) + nodePath.removeNode() + self.ModelNum -= 1 + pass + elif self.ActorDic.has_key(name): + del self.ActorDic[name] + del self.ActorRefDic[name] + if len(childrenList) != 0: + for node in childrenList: + self.removeObj(node) + nodePath.removeNode() + self.ActorNum -= 1 + pass + elif self.collisionDict.has_key(name): + del self.collisionDict[name] + if len(childrenList) != 0: + for node in childrenList: + self.removeObj(node) + nodePath.removeNode() + pass + elif self.dummyDict.has_key(name): + del self.dummyDict[name] + if len(childrenList) != 0: + for node in childrenList: + self.removeObj(node) + nodePath.removeNode() + pass + elif self.lightManager.isLight(name): + if len(childrenList) != 0: + for node in childrenList: + self.removeObj(node) + list = self.lightManager.delete(name) + return list + elif self.particleNodes.has_key(name): + self.particleNodes[name].removeNode() + del self.particleNodes[name] + del self.particleDict[name] + else: + print 'You cannot remove this NodePath' + return + + messenger.send('SGE_Update Explorer',[render]) + return + + def duplicateObj(self, nodePath, pos, hpr, scale, num): + ############################################################################# + # duplicateObj(self, nodePath, pos, hpr, scale, num) + # This function now only worked for either Actor or Model type node. + # It won't duplicate lights or others!!! + # + # This function will duplicate the input nodePath "num" times. + # Each time it will use "pos", "hpr" and "scale" as a offset to change the properties of copy. + # Then, reparent copies to the same parent of origin + # + # To Do: + # Make it work for all kinds of objects.... + ############################################################################# + name = nodePath.getName() + isModel = True + cPos = pos + cHpr = hpr + cScale = scale + parent = nodePath.getParent() + if self.ActorDic.has_key(name): + holder = self.ActorDic + holderRef = self.ActorRefDic + isModel = False + elif self.ModelDic.has_key(name): + holder = self.ModelDic + holderRef = self.ModelRefDic + else: + print '---- DataHolder: Target Obj is not a legal object could be duplicate!' + return + + FilePath = holderRef[name] + oPos = holder[name].getPos()+cPos + oHpr = holder[name].getHpr()+cHpr + + for i in range(num): + if isModel: + ### copy model node from modelpool + newName = name+'_copy_%d'%i + while self.isInScene(newName): + newName = newName + '_1' + holder[newName] = loader.loadModelCopy(FilePath.getFullpath()) + holderRef[newName] = FilePath + self.ModelNum += 1 + holder[newName].reparentTo(parent) + holder[newName].setPos(oPos) + holder[newName].setHpr(oHpr) + holder[newName].setScale(cScale) + holder[newName].setName(newName) + oPos = oPos + cPos + oHpr = oHpr + cHpr + + else: + ### copy the actor- not includ its animations + ''' + Yeah, Yeah, Yeah, I know I should not reload the Actor but get it from modelpool too. + I tried, but it caused some error. + I 'might' be back to fix this problem. + ''' + newName = name+'_copy_%d'%i + while self.isInScene(newName): + newName = newName + '_1' + holder[newName] = Actor.Actor(FilePath.getFullpath()) + holderRef[newName] = FilePath + self.ActorNum += 1 + holder[newName].reparentTo(parent) + holder[newName].setPos(oPos) + holder[newName].setHpr(oHpr) + holder[newName].setScale(cScale) + holder[newName].setName(newName) + oPos = oPos + cPos + oHpr = oHpr + cHpr + + messenger.send('SGE_Update Explorer',[render]) + return + + def loadModel(self, lFilePath, FilePath, Name='Model_'): + ########################################################################### + # loadModel(self, lFilePath, FilePath, Name='Model_') + # This funciton will load a model node into the scene + # and will keep its reference in the ModelDic dictionary. {"NameOfModel":ModelRoot} + # Also it will keep the file path in ModelRefDic dictionary. + # + # The "lFilePath" parameter now is completely useless, + # but I still keep it here because maybe some day we will need it... + # (NOT because I am laze to change the funtion call in the sceneEditor...) + # + ########################################################################### + self.ModelNum += 1 + defaultName = Name + '%d'%self.ModelNum + while self.isInScene(defaultName): + defaultName = defaultName + '_1' + self.ModelDic[defaultName] = loader.loadModel(FilePath) + if self.ModelDic[defaultName]==None: + del self.ModelDic[defaultName] + self.ModelNum -= 1 + return False + self.ModelRefDic[defaultName] = FilePath + self.ModelDic[defaultName].setName(defaultName) + self.ModelDic[defaultName].reparentTo(render) + messenger.send('SGE_Update Explorer',[render]) + messenger.send('DH_LoadingComplete',[self.ModelDic[defaultName]]) + return True + + def loadActor(self, lFilePath, FilePath, Name='Actor_'): + ########################################################################### + # loadActor(self, lFilePath, FilePath, Name='Actor_') + # This funciton will load an actor node into the scene + # and will keep its reference in the ActorDic dictionary.{"NameOfActor":Actor} + # Also it will keep the file path in ActorRefDic dictionary. + # + # The "lFilePath" parameter now is completely useless, + # but I still keep it here because maybe some day we will need it... + # (NOT because I am laze to change the funtion call in the sceneEditor...) + # + ########################################################################### + self.ActorNum += 1 + defaultName = Name + '%d'%self.ActorNum + while self.isInScene(defaultName): + defaultName = defaultName + '_1' + self.ActorDic[defaultName] = Actor.Actor(FilePath.getFullpath()) + if self.ActorDic[defaultName]==None: + del self.ActorDic[defaultName] + self.ActorNum -= 1 + return False + self.ActorRefDic[defaultName] = FilePath + self.ActorDic[defaultName].setName(defaultName) + self.ActorDic[defaultName].reparentTo(render) + messenger.send('SGE_Update Explorer',[render]) + messenger.send('DH_LoadingComplete',[self.ActorDic[defaultName]]) + return True + + + def isActor(self, name): + ########################################################################### + # isActor(self, name) + # This funciton will return True if there is an Actor in the scene named "name" + # and will return False if not. + ########################################################################### + return self.ActorDic.has_key(name) + + def getActor(self, name): + ########################################################################### + # getActor(self, name) + # This funciton will return an Actor node named "name" + ########################################################################### + if self.isActor(name): + return self.ActorDic[name] + else: + print '----No Actor named: ', name + return None + + def getModel(self, name): + ########################################################################### + # getModel(self, name) + # This funciton will return a model node named "name" + ########################################################################### + if self.isModel(name): + return self.ModelDic[name] + else: + print '----No Model named: ', name + return None + + def isModel(self, name): + ########################################################################### + # isModel(self, name) + # This funciton will return True if there is a Model in the scene named "name" + # and will return False if not. + ########################################################################### + return self.ModelDic.has_key(name) + + def loadAnimation(self,name, Dic): + ########################################################################### + # loadAnimation(self,name, Dic) + # This funciton will load animation into an Actor NOde named "name." + # All animation data needs to be put into a dictionary "Dic". + # The formate of this dictionary is {"Name of Animation" : Path to the animation Egg file} + # + # Also, it will send out a message after the loading complete. + # 'DataH_loadFinish'+"the name of actor", + # this message will be catched by the sub window in the animation penal. + ########################################################################### + if self.isActor(name): + self.ActorDic[name].loadAnims(Dic) + for anim in Dic: + self.ActorDic[name].bindAnim(anim) + messenger.send('DataH_loadFinish'+name) + return + else: + print '------ Error when loading animation for Actor: ', name + + def removeAnimation(self, name, anim): + ########################################################################### + # removeAnimation(self, name, anim) + # This function will remove the specific animation "anim" from the actor named "name." + # + # After remove compelete, it will send out two messages. + # One is 'DataH_removeAnimFinish'+"name of the actor." It will be caught by Animation Panel of this actor. + # The other is 'animRemovedFromNode,' this will be caught by property window of this actor. + ########################################################################### + if self.isActor(name): + self.ActorDic[name].unloadAnims([anim]) + AnimDict = self.ActorDic[name].getAnimControlDict() + del AnimDict['lodRoot']['modelRoot'][anim] + messenger.send('DataH_removeAnimFinish'+name) + messenger.send('animRemovedFromNode',[self.ActorDic[name],self.getAnimationDictFromActor(name)]) + return + + def toggleLight(self): + ########################################################################### + # toggleLight(self) + # This function do noting but call a function inside the lightManger to toggle the lighting. + # If it is on, then it will turn it to off. + # If it is off, it will turn it on. + ########################################################################### + self.lightManager.toggle() + return + + def isLight(self,name): + ########################################################################### + # isLight(self, name) + # This function will check that there is a light named "name" of not. + # If it dose have, then return True. + # If it doesn't, then return False + ########################################################################### + return self.lightManager.isLight(name) + + def createLight(self, type = 'ambient', + lightcolor=VBase4(0.3,0.3,0.3,1), + specularColor = VBase4(1), + position = Point3(0,0,0), + orientation = Vec3(1,0,0), + constant = 1.0, + linear = 0.0, + quadratic = 0.0, + exponent = 0.0): + ########################################################################### + # createLight(self, type = 'ambient', + # lightcolor=VBase4(0.3,0.3,0.3,1), + # specularColor = VBase4(1), + # position = Point3(0,0,0), + # orientation = Vec3(1,0,0), + # constant = 1.0, + # linear = 0.0, + # quadratic = 0.0, + # exponent = 0.0) + # It will create a light(seLight) into the scene. + # + # For more detail about creating light, please look the seLight.py. + # + ########################################################################### + list,lightNode = self.lightManager.create(type, lightcolor, specularColor, position, + orientation, constant, linear, quadratic, exponent) + messenger.send('SGE_Update Explorer',[render]) + return list, lightNode + + def getLightList(self): + ########################################################################### + # getLightList(self) + # This function will return the lights(seLight) as a list. + # + # For more detail about creating light, please look the seLight.py. + # + ########################################################################### + return self.lightManager.getLightList() + + def getLightNode(self,lightName): + ########################################################################### + # getLightNode(self,lightName) + # This function will return the lightNode(seLigth) named 'lightName' back. + # + # For more detail about creating light, please look the seLight.py. + # + ########################################################################### + return self.lightManager.getLightNode(lightName) + + def toggleLightNode(self, lightNode): + ########################################################################### + # toggleLightNode(self, lightNode) + # This function will enable of disable the lightNode user put in. + # + # For more detail about creating light, please look the seLight.py. + # + ########################################################################### + if lightNode.active: + self.lightManager.setOff(lightNode) + else: + self.lightManager.setOn(lightNode) + return + + def rename(self,nodePath,nName): + ########################################################################### + # Rename(self,nodePath,nName) + # First, it will check the target object is legal to rename or not. + # this function now doesn't support user to rename everything on the scene gragh. + # If there already has object hase the same name with the target object, + # the new name will be changed. + ########################################################################### + oName = nodePath.getName() + if oName == nName: + # If the new name is the same with old name, do nothing. + return + + while self.isInScene(nName): + nName = nName + '_1' + + if self.isActor(oName): + self.ActorDic[nName]= self.ActorDic[oName] + self.ActorRefDic[nName]= self.ActorRefDic[oName] + self.ActorDic[nName].setName(nName) + if self.blendAnimDict.has_key(oName): + self.blendAnimDict[nName] = self.blendAnimDict[oName] + del self.blendAnimDict[oName] + del self.ActorDic[oName] + del self.ActorRefDic[oName] + elif self.isModel(oName): + self.ModelDic[nName]= self.ModelDic[oName] + self.ModelRefDic[nName]= self.ModelRefDic[oName] + self.ModelDic[nName].setName(nName) + del self.ModelDic[oName] + del self.ModelRefDic[oName] + elif self.lightManager.isLight(oName): + list, lightNode = self.lightManager.rename(oName, nName) + elif self.dummyDict.has_key(oName): + self.dummyDict[nName]= self.dummyDict[oName] + self.dummyDict[nName].setName(nName) + del self.dummyDict[oName] + elif self.collisionDict.has_key(oName): + self.collisionDict[nName]= self.collisionDict[oName] + self.collisionDict[nName].setName(nName) + del self.collisionDict[oName] + + elif self.particleNodes.has_key(oName): + self.particleNodes[nName]= self.particleNodes[oName] + self.particleDict[nName]= self.particleDict[oName] + self.particleDict[nName].setName(nName) + self.particleNodes[nName].setName(nName) + del self.particleNodes[oName] + del self.particleDict[oName] + else: + print '----Error: This Object is not allowed to this function!' + + if self.curveDict.has_key(oName): + self.curveDict[nName] = self.curveDict[oName] + del self.curveDict[oName] + + if self.lightManager.isLight(nName): + return list, lightNode + + def isInScene(self,name): + ########################################################################### + # isInScene(self,name) + # Return True if there is a Node named "name" inside the scene. + # This will check the whole scene, including model, actor, dummy, collisionObj... + ########################################################################### + if self.isActor(name): + return True + elif self.isModel(name): + return True + elif self.lightManager.isLight(name): + return True + elif self.dummyDict.has_key(name): + return True + elif self.collisionDict.has_key(name): + return True + elif self.particleNodes.has_key(name): + return True + elif (name == 'render')or(name == 'SEditor')or(name == 'Lights')or(name == 'camera'): + return True + + return False + + def bindCurveToNode(self,node,curveCollection): + ########################################################################### + # bindCurveToNode(self,node,curveCollection) + # This function will maintain the curvesDict + # using the node name as a reference to assosiate a list which contains all curves related to that node. + ########################################################################### + name = node.getName() + if self.curveDict.has_key(name): + self.curveDict[name].append(curveCollection) + return + else: + self.curveDict[name] = [curveCollection] + return + return + + def getCurveList(self, nodePath): + ########################################################################### + # getCureveList(self, nodePath) + # This function will return a list + # which contains all curves taht have been binded with the inout node + # If the input node has not been bindedwith any curve, it will return None. + ########################################################################### + name = nodePath.getName() + if self.curveDict.has_key(name): + return self.curveDict[name] + else: + return None + + def removeCurveFromNode(self, nodePath, curveName): + ########################################################################### + # removeCurveFromNode(self, nodePath, curveName) + # This function will remove the "curveName" curve(Motion path data) from the nodaPath. + # After remove, it will send out a message. + # 'curveRemovedFromNode' + # This message will be caught by Property Window for this node. + ########################################################################### + name =nodePath.getName() + if self.curveDict.has_key(name): + index = None + for curve in self.curveDict[name]: + if curve.getCurve(0).getName() == curveName : + index = self.curveDict[name].index(curve) + break + del self.curveDict[name][index] + if len(self.curveDict[name])!=0: + messenger.send('curveRemovedFromNode',[nodePath, self.curveDict[name]]) + else: + del self.curveDict[name] + messenger.send('curveRemovedFromNode',[nodePath, None]) + return + + def getInfoOfThisNode(self, nodePath): + ########################################################################### + # getInfoOfThisNode(self, nodePath) + # This function will return a list which contains all object properies + # that will be used in property window. + ########################################################################### + type = '' + info = {} + name = nodePath.getName() + if name == 'render': + type = 'render' + elif name == 'camera': + type = 'camera' + cameraNode = base.cam.node() + lens = cameraNode.getLens() + info['lensType'] = lens.getClassType().getName() + info['far'] = lens.getFar() + info['near'] = lens.getNear() + info['FilmSize'] = lens.getFilmSize() + info['fov'] = lens.getFov() + info['hFov'] = lens.getHfov() + info['vFov'] = lens.getVfov() + info['focalLength'] = lens.getFocalLength() + + + elif name == 'SEditor': + type = 'Special' + elif self.isActor(name): + type = 'Actor' + info['filePath'] = self.ActorRefDic[name] + info['animDict'] = self.getAnimationDictFromActor(name) + elif self.isModel(name): + type = 'Model' + info['filePath'] = self.ModelRefDic[name] + elif self.isLight(name): + type = 'Light' + info['lightNode'] = self.lightManager.getLightNode(name) + elif self.dummyDict.has_key(name): + type = 'dummy' + elif self.collisionDict.has_key(name): + type = 'collisionNode' + info['collisionNode'] = self.collisionDict[name] + if self.curveDict.has_key(name): + info['curveList'] = self.getCurveList(nodePath) + + return type, info + + def getAnimationDictFromActor(self, actorName): + ########################################################################### + # getAnimationDictFromActor(self, actorName) + # This function will return a Dictionary which contains the animation data in the actor "actorName". + # The data inside is get from the actor, so, it can't be wrong... + ########################################################################### + animContorlDict = self.ActorDic[actorName].getAnimControlDict() + animNameList = self.ActorDic[actorName].getAnimNames() + if len(animNameList)==0: + return {} + animDict = {} + for anim in animNameList: + animDict[anim] = animContorlDict['lodRoot']['modelRoot'][anim][0] + return animDict + + def addDummyNode(self,nodePath): + ########################################################################### + # addDummyNode(self,nodePath) + # This function will add a dummy node into the scane and reparent it to nodePath which user put in. + # + # This dummy actually is just a default sphere model. + # + ########################################################################### + number = len(self.dummyDict) + number += 1 + name = 'Dummy%d'%number + self.dummyModel = loader.loadModelCopy( "misc/sphere" ) + self.dummyModel.reparentTo(nodePath) + while self.isInScene(name): + name = name + '_1' + self.dummyModel.setName(name) + self.dummyDict[name] = self.dummyModel + messenger.send('SGE_Update Explorer',[render]) + return + + def addCollisionObject(self, collisionObj, nodePath, pointA=None, pointB=None, pointC=None, name = None): + ########################################################################### + # addCollisionObject(self, collisionObj, nodePath, pointA=None, pointB=None, pointC=None, name = None) + # This function will add a collision object into a "CollisionNode" object and put it into scene. + # The collision object will be reparent to "nodePath" and + # will be show on the screen if user has enable the "show collision objects" option. + ########################################################################### + if name == None: + name = 'CollisionNode_%d'%len(self.collisionDict) + while self.isInScene(name): + name=name + '_1' + node = CollisionNode(name) + node.addSolid(collisionObj) + self.collisionDict[name] = nodePath.attachNewNode(node) + + if pointA!=None: + self.collisionDict[name].setTag('A_X','%f'%pointA.getX()) + self.collisionDict[name].setTag('A_Y','%f'%pointA.getY()) + self.collisionDict[name].setTag('A_Z','%f'%pointA.getZ()) + self.collisionDict[name].setTag('B_X','%f'%pointB.getX()) + self.collisionDict[name].setTag('B_Y','%f'%pointB.getY()) + self.collisionDict[name].setTag('B_Z','%f'%pointB.getZ()) + self.collisionDict[name].setTag('C_X','%f'%pointC.getX()) + self.collisionDict[name].setTag('C_Y','%f'%pointC.getY()) + self.collisionDict[name].setTag('C_Z','%f'%pointC.getZ()) + + if self.collisionVisable: + self.collisionDict[name].show() + + base.cTrav.addCollider(node, self.CollisionHandler) + + messenger.send('SGE_Update Explorer',[render]) + + return + + def toggleCollisionVisable(self, visable): + ########################################################################### + # toggleCollisionVisable(self, visable) + # This fucntion will toggle the visibility of all collision node in the scene. + ########################################################################### + if visable == 1: + self.collisionVisable = True + for name in self.collisionDict: + if self.collisionDict[name].isHidden(): + self.collisionDict[name].show() + else: + self.collisionVisable = False + for name in self.collisionDict: + if not self.collisionDict[name].isHidden(): + self.collisionDict[name].hide() + + def toggleParticleVisable(self, visable): + if not visable: + for name in self.particleNodes: + self.particleNodes[name].setTransparency(True) + self.particleNodes[name].setAlphaScale(0) + self.particleNodes[name].setBin("fixed", 1) + else: + for name in self.particleNodes: + self.particleNodes[name].setTransparency(False) + self.particleNodes[name].setAlphaScale(1) + self.particleNodes[name].setBin("default", 1) + return + + def getBlendAnimAsDict(self, name): + ########################################################################### + # getBlendAnimAsDict(self, name) + # This function will return a dictionry + # which contains user blended animation data for actor named "name." + # The formate of thsi dictionary is + # {"name of Blend Animation" : ["Animation A, Animation B, Effect(Float, 0~1)"]} + ########################################################################### + if self.blendAnimDict.has_key(name): + return self.blendAnimDict[name] + else: + return {} + + def saveBlendAnim(self, actorName, blendName, animNameA, animNameB, effect): + ########################################################################### + # saveBlendAnim(self, actorName, blendName, animNameA, animNameB, effect) + # This function will save the blended Animation "blendname" for actor "actorNane" + # and keep the data in the blendAnimDict. + # + # Also, if this blend is the first blend animation that the target actor has, + # this function will add a "Blending" tag on this actor which is "True". + ########################################################################### + if self.blendAnimDict.has_key(actorName): + if self.blendAnimDict[actorName].has_key(blendName): + ### replace the original setting + self.blendAnimDict[actorName][blendName][0] = animNameA + self.blendAnimDict[actorName][blendName][1] = animNameB + self.blendAnimDict[actorName][blendName][2] = effect + else: + ### create new blend animation in the dictionary + self.blendAnimDict[actorName][blendName] = [animNameA, animNameB, effect] + else: + self.getActor(actorName).setTag('Blending','True') + self.blendAnimDict[actorName] = {blendName:[animNameA, animNameB, effect]} + return self.blendAnimDict[actorName] + + def renameBlendAnim(self, actorName, nName, oName, animNameA, animNameB, effect): + ########################################################################### + # renameBlendAnim(self, actorName, nName, oName, animNameA, animNameB, effect) + # This function is used to rename a exist blended animation named "oName" to "nName." + # The way it doing this is first remove the original blend fomr the actor + # and then re-create a new one named "nName" in. + # Because it is not just simply rename the animation, + # it will also rewrite the data to the newest one. + ########################################################################### + self.removeBlendAnim(actorName,oName) + print self.blendAnimDict + return self.saveBlendAnim(actorName, nName, animNameA, animNameB, effect) + + def removeBlendAnim(self, actorName, blendName): + ########################################################################### + # removeBlendAnim(self, actorName, blendName) + # This fucntion will remove the record of blended animation named "blendName" + # from the actor named "actorName". + # + # Also, it will check that there is any blended animation remained for this actor, + # If none, this function will clear the "Blending" tag of this object. + ########################################################################### + if self.blendAnimDict.has_key(actorName): + if self.blendAnimDict[actorName].has_key(blendName): + ### replace the original setting + del self.blendAnimDict[actorName][blendName] + if len(self.blendAnimDict[actorName])==0: + del self.blendAnimDict[actorName] + self.getActor(actorName).clearTag('Blending') + return {} + return self.blendAnimDict[actorName] + else: + return {} + + def getAllObjNameAsList(self): + ########################################################################### + # getAllObjNameAsList(self) + # This function will return a list which contains all objects' names in the scene. + # It means which won't have any kinds of animation, blend animation or Mopath data inside. + ########################################################################### + list = ['camera'] # Default object you can select camera + list = list + self.ModelDic.keys() \ + + self.ActorDic.keys() + self.collisionDict.keys() \ + + self.dummyDict.keys() + self.particleNodes.keys() \ + + self.lightManager.getLightList() + return list + + def getObjFromSceneByName(self, name): + ########################################################################### + # getObjFromSceneByName(self, name) + # return a reference to the nodePath named "name" + ########################################################################### + if name == 'camera': + return camera + elif self.ModelDic.has_key(name): + return self.ModelDic[name] + elif self.ActorDic.has_key(name): + return self.ActorDic[name] + elif self.collisionDict.has_key(name): + return self.collisionDict[name] + elif self.dummyDict.has_key(name): + return self.dummyDict[name] + elif self.particleNodes.has_key(name): + return self.particleNodes[name] + elif self.lightManager.isLight(name): + return self.lightManager.getLightNode(name) + + return None + + def getControlSetting(self): + ########################################################################### + # getControlSetting(self) + # return tqwo things. + # One is the type of the control. The other is the data about that control. + # Now we only support keyboard control, so it will return a list. + # The first object in the list is a reference to the target we want to control. + # The second object in the list is a dictionary which contains a map about + # which keyboard message should be accepted. + # The third and the last object here is a dictionary which contains the data + # indicating that the changing value for each keyboard control event. + ########################################################################### + if self.controlType == 'Keyboard': + return self.controlType, [self.controlTarget, self.keyboardMapDict.copy(), self.keyboardSpeedDict.copy()] + elif self.controlType == 'Tracker': + return self.controlType, [] + return + + def saveControlSetting(self, controlType, data): + ########################################################################### + # saveControlSetting(self, controlType, data) + # copy the current control setting into dataHolder + # Mainly called by sceneEditor. + ########################################################################### + if controlType == 'Keyboard': + self.controlType = controlType + self.controlTarget = data[0] + self.keyboardMapDict.clear() + self.keyboardMapDict = data[1].copy() + self.keyboardSpeedDict.clear() + self.keyboardSpeedDict = data[2].copy() + return + + def loadScene(self): + ########################################################################### + # loadScene(self) + # Opens a dialog box asking for a scene file to load. It then removes + # the current scene and opens the new one. + # It basically proceeds by executig the python file containing the scene + # and then re-populating the various dictionaries based on the dictionaries + # in the scene file hence reviving the state for the scene + ########################################################################### + + ### Ask for a filename + OpenFilename = tkFileDialog.askopenfilename(filetypes = [("PY","py")],title = "Load Scene") + if(not OpenFilename): + return None + f=Filename.fromOsSpecific(OpenFilename) + fileName=f.getBasenameWoExtension() + dirName=f.getFullpathWoExtension() + print "DATAHOLDER::" + dirName + ############################################################################ + # Append the path to this file to our sys path where python looks for modules + # We do this so that we can use "import" on our saved scene code and execute it + ############################################################################ + sys.path.append(os.path.dirname(f.toOsSpecific())) + + ############################################################################ + # Actually import the scene... this executes the code in the scene + ############################################################################ + self.theScene=__import__(fileName) + self.Scene=self.theScene.SavedScene(0,seParticleEffect,seParticles,dirName) # Specify load mode of 0 which will allow us to pass seParticle and seParticleEffect + messenger.send('SGE_Update Explorer',[render]) + + + # Lets call some important initialization methods on our scene: + #self.Scene.starteffects(0,seParticleEffect,seParticles,dirName) # This special calling of start effect with mode 0 is to use seParticleEffect and seParticles + + + ############################################################################ + # Populate Model related Dictionaries + ############################################################################ + for model in self.Scene.ModelDic: + self.ModelDic[model]=self.Scene.ModelDic[model] + #self.ModelRefDic[model]=self.Scene.ModelRefDic[model] # The Old absolute paths way + self.ModelRefDic[model]=Filename(dirName + "/" + self.Scene.ModelRefDic[model]) # Relative Paths + self.ModelNum=self.ModelNum+1 + + ############################################################################ + # Populate Actor related Dictionaries + ############################################################################ + for actor in self.Scene.ActorDic: + self.ActorDic[actor]=self.Scene.ActorDic[actor] + #self.ActorRefDic[actor]=self.Scene.ActorRefDic[actor] # Old way of doing absolute paths + self.ActorRefDic[actor]=Filename(dirName + "/" + self.Scene.ActorRefDic[actor]) # Relative Paths + if(self.Scene.blendAnimDict.has_key(str(actor))): + self.blendAnimDict[actor]=self.Scene.blendAnimDict[actor] + self.ActorNum=self.ActorNum+1 + + + + ############################################################################ + # Populate Light related Dictionaries + ############################################################################ + #print self.Scene.LightDict + for light in self.Scene.LightDict: + #print light + alight=self.Scene.LightDict[light] + type=self.Scene.LightTypes[light] + thenode=self.Scene.LightNodes[light] + #print type + if type == 'ambient': + self.lightManager.create('ambient',alight.getColor(),name=alight.getName(),tag=thenode.getTag("Metadata")) + elif type == 'directional': + #print alight.getPoint() + #print alight.getDirection() + self.lightManager.create('directional',alight.getColor(),alight.getSpecularColor(),thenode.getPos(),thenode.getHpr(),name=alight.getName(),tag=thenode.getTag("Metadata")) + elif type == 'point': + atten=alight.getAttenuation() + #print alight.getPoint() + self.lightManager.create('point',alight.getColor(),alight.getSpecularColor(),thenode.getPos(),Vec3(1,0,0),atten.getX(),atten.getY(),atten.getZ(),name=alight.getName(),tag=thenode.getTag("Metadata")) + elif type == 'spot': + atten=alight.getAttenuation() + self.lightManager.create('spot',alight.getColor(),alight.getSpecularColor(),thenode.getPos(),thenode.getHpr(),atten.getX(),atten.getY(),atten.getZ(),alight.getExponent(),name=alight.getName(),tag=thenode.getTag("Metadata")) + else: + print 'Invalid light type' + + ############################################################################ + # Populate Dummy related Dictionaries + ############################################################################ + for dummy in self.Scene.dummyDict: + self.dummyDict[dummy] = self.Scene.dummyDict[dummy] + + ############################################################################ + # Populate Collision related Dictionaries + ############################################################################ + for collnode in self.Scene.collisionDict: + self.collisionDict[collnode]=self.Scene.collisionDict[collnode] + + ############################################################################ + # Populate Mopath related Dictionaries + ############################################################################ + for node in self.Scene.curveDict: + curveCollection=self.Scene.curveDict[node] + for curve in curveCollection: + curveColl=ParametricCurveCollection() + nodeP=loader.loadModel(curve) + curveColl.addCurves(nodeP.node()) + nodeP.removeNode() + thenode=render.find("**/"+str(node)) + self.bindCurveToNode(thenode,curveColl) + + ############################################################################ + # Populate Particle related Dictionaries + ############################################################################ + for effect in self.Scene.particleDict: + theeffect=self.Scene.particleDict[effect] + emitter=loader.loadModel("sphere") + emitter.setPosHprScale(theeffect.getX(),theeffect.getY(),theeffect.getZ(),theeffect.getH(),theeffect.getP(),theeffect.getR(),theeffect.getSx(),theeffect.getSy(),theeffect.getSz()) + theeffect.setPos(0,0,0) + theeffect.setName(str(effect)) + tempparent=theeffect.getParent() + theeffect.reparentTo(emitter) + emitter.setName(str(effect)) + emitter.reparentTo(tempparent) + theeffect.enable() + self.particleDict[effect]=theeffect + self.particleNodes[effect]=emitter + + + + # Clean up things added to scene graph by saved file's code execution + for light in self.Scene.LightDict: + vestige=render.find('**/'+light) + if(vestige != None): + vestige.removeNode() + + ############################################################################ + # return the filename and update the scenegraph explorer window + ############################################################################ + messenger.send('SGE_Update Explorer',[render]) + if(OpenFilename): + return OpenFilename + else: + return None + + def getList(self): + return self.lightManager.getList() diff --git a/doc/SceneEditor/duplicateWindow.py b/doc/SceneEditor/duplicateWindow.py new file mode 100644 index 0000000000..aaa733781a --- /dev/null +++ b/doc/SceneEditor/duplicateWindow.py @@ -0,0 +1,172 @@ +################################################################# +# duplicateWindow.py +# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +################################################################# +from AppShell import * +from TkGlobal import * + +import seSceneGraphExplorer + + +class duplicateWindow(AppShell): + ################################################################# + # duplicateWindow(AppShell) + # This class is used to create a dialog window + # for handling the "dupicate" command from the sceneEditor + # + # Notice! + # The actual duplicating process is not happending here! + # The one handle the process is dataHolder... + ################################################################# + appversion = '1.0' + appname = 'Duplication' + frameWidth = 450 + frameHeight = 320 + frameIniPosX = 250 + frameIniPosY = 250 + padx = 0 + pady = 0 + + + def __init__(self, parent = None, nodePath = None, **kw): + # Define the megawidget options. + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + + if parent == None: + self.parent = Toplevel() + AppShell.__init__(self, self.parent) + self.parent.geometry('%dx%d+%d+%d' % (self.frameWidth, self.frameHeight,self.frameIniPosX,self.frameIniPosY)) + + self.nodePath = nodePath + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + def appInit(self): + print '----SideWindow is Initialized!!' + + def createInterface(self): + # The interior of the toplevel panel + interior = self.interior() + mainFrame = Frame(interior) + self.inputZone = Pmw.Group(mainFrame, tag_text='Offset setting') + self.inputZone.pack(fill='both',expand=1) + settingFrame = self.inputZone.interior() + Label(settingFrame,text=' X ').place(anchor=NW,x=110,y=15) + Label(settingFrame,text=' Y ').place(anchor=NW,x=205,y=15) + Label(settingFrame,text=' Z ').place(anchor=NW,x=295,y=15) + self.move_x = Pmw.EntryField(settingFrame,label_text='Move :',labelpos='w',value='0.0', validate=Pmw.realvalidator) + self.move_x.component('entry').config(width=10) + self.move_y = Pmw.EntryField(settingFrame,value='0.0', validate=Pmw.realvalidator) + self.move_y.component('entry').config(width=10) + self.move_z = Pmw.EntryField(settingFrame, value='0.0', validate=Pmw.realvalidator) + self.move_z.component('entry').config(width=10) + self.move_x.place(anchor=NW,x=50,y=40) + self.move_y.place(anchor=NW,x=185,y=40) + self.move_z.place(anchor=NW,x=275,y=40) + + self.rotate_x = Pmw.EntryField(settingFrame,label_text='Rotate:',labelpos='w',value='0.0', validate=Pmw.realvalidator) + self.rotate_x.component('entry').config(width=10) + self.rotate_y = Pmw.EntryField(settingFrame,value='0.0', validate=Pmw.realvalidator) + self.rotate_y.component('entry').config(width=10) + self.rotate_z = Pmw.EntryField(settingFrame, value='0.0', validate=Pmw.realvalidator) + self.rotate_z.component('entry').config(width=10) + self.rotate_x.place(anchor=NW,x=50,y=70) + self.rotate_y.place(anchor=NW,x=185,y=70) + self.rotate_z.place(anchor=NW,x=275,y=70) + + self.scale_x = Pmw.EntryField(settingFrame,label_text='Scale :',labelpos='w',value='1.0', validate=Pmw.realvalidator) + self.scale_x.component('entry').config(width=10) + self.scale_y = Pmw.EntryField(settingFrame,value='1.0', validate=Pmw.realvalidator) + self.scale_y.component('entry').config(width=10) + self.scale_z = Pmw.EntryField(settingFrame, value='1.0', validate=Pmw.realvalidator) + self.scale_z.component('entry').config(width=10) + self.scale_x.place(anchor=NW,x=52,y=100) + self.scale_y.place(anchor=NW,x=185,y=100) + self.scale_z.place(anchor=NW,x=275,y=100) + + self.numberOfCopy = Pmw.EntryField(settingFrame,label_text='Number of Copy :',labelpos='w',value='1', validate=Pmw.integervalidator) + self.numberOfCopy.component('entry').config(width=15) + self.numberOfCopy.place(anchor=NW,x=52,y=150) + + settingFrame.pack(fill=BOTH,expand=1,padx=7,pady=7) + + self.button_ok = Button(mainFrame, text="OK", command=self.ok_press,width=10) + self.button_ok.pack(fill=BOTH,expand=0,side=RIGHT) + + mainFrame.pack(fill = 'both', expand = 1,padx=7,pady=7) + + + def createMenuBar(self): + self.menuBar.destroy() + + def onDestroy(self, event): + messenger.send('DW_close') + ''' + If you have open any thing, please rewrite here! + ''' + pass + + ############################### + + def ok_press(self): + ################################################################# + # ok_press(self) + # Callback function + # This function will be called when user click on the "OK" button on the window. + # After collect all data we need for the duplication process, + # this function will send out a message with all data. + # 'DW_duplicating' + # This message will be caught by sceneEditor. + ################################################################# + if not self.allEntryValid(): + print '---- Duplication Window: Invalid value!!' + return + x = self.move_x.getvalue() + y = self.move_y.getvalue() + z = self.move_z.getvalue() + pos=Vec3(FloatType(x),FloatType(y),FloatType(z)) + x = self.rotate_x.getvalue() + y = self.rotate_y.getvalue() + z = self.rotate_z.getvalue() + hpr=Vec3(FloatType(x),FloatType(y),FloatType(z)) + x = self.scale_x.getvalue() + y = self.scale_y.getvalue() + z = self.scale_z.getvalue() + scale=Vec3(FloatType(x),FloatType(y),FloatType(z)) + num = int(self.numberOfCopy.getvalue()) + messenger.send('DW_duplicating',[self.nodePath,pos,hpr,scale,num]) + self.quit() + + def allEntryValid(self): + ################################################################# + # allEntryValid(self) + # This function is used to check all data in the input entries are valid. + # + # For example, none of entries contains blank data. + # + ################################################################# + if not self.move_x.valid(): + return False + elif not self.move_y.valid(): + return False + elif not self.move_z.valid(): + return False + elif not self.rotate_x.valid(): + return False + elif not self.rotate_y.valid(): + return False + elif not self.rotate_z.valid(): + return False + elif not self.scale_x.valid(): + return False + elif not self.scale_y.valid(): + return False + elif not self.scale_z.valid(): + return False + elif not self.numberOfCopy.valid(): + return False + + return True diff --git a/doc/SceneEditor/lightingPanel.py b/doc/SceneEditor/lightingPanel.py new file mode 100644 index 0000000000..38b0ee7717 --- /dev/null +++ b/doc/SceneEditor/lightingPanel.py @@ -0,0 +1,496 @@ +################################################################# +# lightingPanel.py +# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +################################################################# +# Import Tkinter, Pmw, and the floater code from this directory tree. +from AppShell import * +from TkGlobal import * +from seColorEntry import * +import VectorWidgets +import Floater +import Slider +import string +import math +import types + + +class lightingPanel(AppShell): + ################################################################# + # lightingPanel(AppShell) + # This will create a window to let user + # create any kinds of lighting into the scene + ################################################################# + # Override class variables + appname = 'Lighting Panel' + frameWidth = 400 + frameHeight = 400 + currentLight = None + + def __init__(self, lightList, parent = None, **kw): + self.lightList = lightList + self.lightColor = [0.3*255,0.3*255,0.3*255] + self.type = '' + INITOPT = Pmw.INITOPT + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + + # Initialize the superclass + AppShell.__init__(self) + + # Execute option callbacks + self.initialiseoptions(lightingPanel) + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + def createInterface(self): + # Handle to the toplevels interior + interior = self.interior() + menuBar = self.menuBar + self.menuBar.destroy() + + # Create a frame to hold all stuff + mainFrame = Frame(interior) + + self.listZone = Pmw.Group(mainFrame,tag_pyclass = None) + self.listZone.pack(expand=0, fill=X,padx=3,pady=3) + listFrame = self.listZone.interior() + + self.lightEntry = self.createcomponent( + 'Lights List', (), None, + Pmw.ComboBox, (listFrame,),label_text='Light :', + labelpos = W, entry_width = 25, selectioncommand = self.selectLight, + scrolledlist_items = self.lightList) + self.lightEntry.pack(side=LEFT) + + self.renameButton = self.createcomponent( + 'Rename Light', (), None, + Button, (listFrame,), + text = ' Rename ', + command = self.renameLight) + self.renameButton.pack(side=LEFT) + + self.addLighZone = Pmw.Group(listFrame,tag_pyclass = None) + self.addLighZone.pack(side=LEFT) + insideFrame = self.addLighZone.interior() + self.lightsButton = Menubutton(insideFrame, text = 'Add light',borderwidth = 3, + activebackground = '#909090') + lightsMenu = Menu(self.lightsButton) + lightsMenu.add_command(label = 'Add Ambient Light', + command = self.addAmbient) + lightsMenu.add_command(label = 'Add Directional Light', + command = self.addDirectional) + lightsMenu.add_command(label = 'Add Point Light', + command = self.addPoint) + lightsMenu.add_command(label = 'Add Spotlight', + command = self.addSpot) + + self.lightsButton.pack(expand=0) + self.lightsButton['menu'] = lightsMenu + + self.deleteButton = self.createcomponent( + 'delete Light', (), None, + Button, (listFrame,), + text = ' Delete ', + command = self.deleteLight) + self.deleteButton.pack(side=LEFT) + + self.lightColor = seColorEntry( + mainFrame, text = 'Light Color', value=self.lightColor) + self.lightColor['command'] = self.setLightingColorVec + self.lightColor['resetValue'] = [0.3*255,0.3*255,0.3*255,0] + self.lightColor.pack(fill=X,expand=0) + self.bind(self.lightColor, 'Set light color') + + # Notebook pages for light specific controls + self.lightNotebook = Pmw.NoteBook(mainFrame, tabpos = None, + borderwidth = 0) + ambientPage = self.lightNotebook.add('Ambient') + directionalPage = self.lightNotebook.add('Directional') + pointPage = self.lightNotebook.add('Point') + spotPage = self.lightNotebook.add('Spot') + # Put this here so it isn't called right away + self.lightNotebook['raisecommand'] = self.updateLightInfo + + # Directional light controls + self.dSpecularColor = seColorEntry( + directionalPage, text = 'Specular Color') + self.dSpecularColor['command'] = self.setSpecularColor + self.dSpecularColor.pack(fill = X, expand = 0) + self.bind(self.dSpecularColor, + 'Set directional light specular color') + self.dPosition = VectorWidgets.Vector3Entry( + directionalPage, text = 'Position') + self.dPosition['command'] = self.setPosition + self.dPosition['resetValue'] = [0,0,0,0] + self.dPosition.pack(fill = X, expand = 0) + self.bind(self.dPosition, 'Set directional light position') + self.dOrientation = VectorWidgets.Vector3Entry( + directionalPage, text = 'Orientation') + self.dOrientation['command'] = self.setOrientation + self.dOrientation['resetValue'] = [0,0,0,0] + self.dOrientation.pack(fill = X, expand = 0) + self.bind(self.dOrientation, 'Set directional light orientation') + + # Point light controls + self.pSpecularColor = seColorEntry( + pointPage, text = 'Specular Color') + self.pSpecularColor['command'] = self.setSpecularColor + self.pSpecularColor.pack(fill = X, expand = 0) + self.bind(self.pSpecularColor, + 'Set point light specular color') + + self.pPosition = VectorWidgets.Vector3Entry( + pointPage, text = 'Position') + self.pPosition['command'] = self.setPosition + self.pPosition['resetValue'] = [0,0,0,0] + self.pPosition.pack(fill = X, expand = 0) + self.bind(self.pPosition, 'Set point light position') + + self.pConstantAttenuation = Slider.Slider( + pointPage, + text = 'Constant Attenuation', + max = 1.0, + resolution = 0.01, + value = 1.0) + self.pConstantAttenuation['command'] = self.setConstantAttenuation + self.pConstantAttenuation.pack(fill = X, expand = 0) + self.bind(self.pConstantAttenuation, + 'Set point light constant attenuation') + + self.pLinearAttenuation = Slider.Slider( + pointPage, + text = 'Linear Attenuation', + max = 1.0, + resolution = 0.01, + value = 0.0) + self.pLinearAttenuation['command'] = self.setLinearAttenuation + self.pLinearAttenuation.pack(fill = X, expand = 0) + self.bind(self.pLinearAttenuation, + 'Set point light linear attenuation') + + self.pQuadraticAttenuation = Slider.Slider( + pointPage, + text = 'Quadratic Attenuation', + max = 1.0, + resolution = 0.01, + value = 0.0) + self.pQuadraticAttenuation['command'] = self.setQuadraticAttenuation + self.pQuadraticAttenuation.pack(fill = X, expand = 0) + self.bind(self.pQuadraticAttenuation, + 'Set point light quadratic attenuation') + + # Spot light controls + self.sSpecularColor = seColorEntry( + spotPage, text = 'Specular Color') + self.sSpecularColor['command'] = self.setSpecularColor + self.sSpecularColor.pack(fill = X, expand = 0) + self.bind(self.sSpecularColor, + 'Set spot light specular color') + + self.sConstantAttenuation = Slider.Slider( + spotPage, + text = 'Constant Attenuation', + max = 1.0, + resolution = 0.01, + value = 1.0) + self.sConstantAttenuation['command'] = self.setConstantAttenuation + self.sConstantAttenuation.pack(fill = X, expand = 0) + self.bind(self.sConstantAttenuation, + 'Set spot light constant attenuation') + + self.sLinearAttenuation = Slider.Slider( + spotPage, + text = 'Linear Attenuation', + max = 1.0, + resolution = 0.01, + value = 0.0) + self.sLinearAttenuation['command'] = self.setLinearAttenuation + self.sLinearAttenuation.pack(fill = X, expand = 0) + self.bind(self.sLinearAttenuation, + 'Set spot light linear attenuation') + + self.sQuadraticAttenuation = Slider.Slider( + spotPage, + text = 'Quadratic Attenuation', + max = 1.0, + resolution = 0.01, + value = 0.0) + self.sQuadraticAttenuation['command'] = self.setQuadraticAttenuation + self.sQuadraticAttenuation.pack(fill = X, expand = 0) + self.bind(self.sQuadraticAttenuation, + 'Set spot light quadratic attenuation') + + self.sExponent = Slider.Slider( + spotPage, + text = 'Exponent', + max = 1.0, + resolution = 0.01, + value = 0.0) + self.sExponent['command'] = self.setExponent + self.sExponent.pack(fill = X, expand = 0) + self.bind(self.sExponent, + 'Set spot light exponent') + + # MRM: Add frustum controls + + self.lightNotebook.setnaturalsize() + self.lightNotebook.pack(expand = 1, fill = BOTH) + + mainFrame.pack(expand=1, fill = BOTH) + + def onDestroy(self, event): + messenger.send('LP_close') + ''' + If you have open any thing, please rewrite here! + ''' + pass + + def renameLight(self): + ################################################################# + # renameLight(self) + # Call Back function + # This function will be called when user push + # the "Rename" button on the panel. + # + # Then, this function will collect data and send out them with a message + # "LP_rename" + # Which will be caught by sceneEditor and pass to dataHolder to + # complete the renaming. + # + ################################################################# + oName = self.currentLight + nName = self.lightEntry.get() + messenger.send('LP_rename',[oName,nName]) + return + + def deleteLight(self): + ################################################################# + # deleteLight(self) + # Call Back Function. + # This function will be called when user click on + # the "Delete" button on the panel. + # + # Then, this function will send out a message with current seleted light + # "LP_removeLight" + # Which will be caught by sceneEditor and pass to dataHolder to + # complete the delete process. + # + ################################################################# + messenger.send('LP_removeLight',[self.currentLight]) + return + + def updateList(self, list, node=None): + ################################################################# + # updataList(self, list, node = None) + # This function will take a list object which contains names of lights in the scene. + # Also, if user has put node as a parameter, + # this function will automatically select that node as the current working target. + ################################################################# + self.lightList = list + self.lightEntry.setlist(list) + if node!=None: + self.lightEntry.selectitem(index=node.getName(), setentry=True ) + self.updateDisplay(node) + elif len(list)>0: + self.lightEntry.selectitem(index=0, setentry=True ) + self.selectLight(list[0]) + else: + self.lightEntry.clear() + return + + def selectLight(self, lightName): + ################################################################# + # selectLight(self, lightName) + # This function will be called each time when + # user select a light from the list on the panel. + # Then, this function will send out the message, + # 'LP_selectLight' to sceneEditorand get the current light information from dataHolder. + ################################################################# + if lightName in self.lightList: + messenger.send('LP_selectLight',[lightName]) + return + + def updateDisplay(self, lightNode): + ################################################################# + # updateDisplay(self, lightNode) + # This function will update the information showing on the panel. + # For example, give a lightNode which is a Point Light. + # This function will switch the page to specify the type. + # Also, new node is the same type with the previous one, + # then this is function won't do the page switching, + # but will call other function to refresh the data to target node. + ################################################################# + self.currentLight = lightNode + if self.currentLight != None: + color = lightNode.getLightColor() + self.lightColor.set([255*color.getX(),255*color.getY(),255*color.getZ()]) + oldType = self.type + self.type = lightNode.getType() + else: + self.lightColor.set([255*0.3,255*0.3,255*0.3]) + oldType = self.type + self.type = 'ambient' + + if self.type=='ambient': + self.lightNotebook.selectpage('Ambient') + elif self.type =='directional': + self.lightNotebook.selectpage('Directional') + elif self.type =='point': + self.lightNotebook.selectpage('Point') + elif self.type =='spot': + self.lightNotebook.selectpage('Spot') + if oldType == self.type: + # The same type with previous one, call updateLightInfo to refresh the values. + self.updateLightInfo() + return + + def updateLightInfo(self, page=None): + ################################################################# + # updateLightInfo(self, page=None) + # This function will refresh the data we user have done any selection. + ################################################################# + if self.currentLight != None: + light = self.currentLight.getLight() + if self.type != 'ambient': + specColor = light.getSpecularColor() + if self.type =='directional': + point = self.currentLight.getPosition() + dir = self.currentLight.getOrientation() + self.dSpecularColor.set([specColor.getX()*255,specColor.getY()*255,specColor.getZ()*255]) + self.dPosition.set([point.getX(),point.getY(),point.getZ()]) + self.dOrientation.set([dir.getX(),dir.getY(),dir.getZ()]) + elif self.type =='point': + point = self.currentLight.getPosition() + attenuation = light.getAttenuation() + self.pSpecularColor.set([specColor.getX()*255,specColor.getY()*255,specColor.getZ()*255]) + self.pPosition.set([point.getX(),point.getY(),point.getZ()]) + self.pConstantAttenuation.set(attenuation.getX()) + self.pLinearAttenuation.set(attenuation.getY()) + self.pQuadraticAttenuation.set(attenuation.getZ()) + elif self.type =='spot': + attenuation = light.getAttenuation() + expo = light.getExponent() + self.sSpecularColor.set([specColor.getX()*255,specColor.getY()*255,specColor.getZ()*255]) + self.sConstantAttenuation.set(attenuation.getX()) + self.sLinearAttenuation.set(attenuation.getY()) + self.sQuadraticAttenuation.set(attenuation.getZ()) + self.sExponent.set(expo) + return + + def addAmbient(self): + ################################################################# + # addAmbient(self) + # This function will send out a message to + # ask dataHolder to create a default ambient light + ################################################################# + messenger.send('LP_addLight',['ambient']) + return + + def addDirectional(self): + ################################################################# + # addDirectional(self) + # This function will send out a message to + # sk dataHolder to create a default Directional light + ################################################################# + messenger.send('LP_addLight',['directional']) + return + + def addPoint(self): + ################################################################# + # addPoint(self) + # This function will send out a message to + # ask dataHolder to create a default Point light + ################################################################# + messenger.send('LP_addLight',['point']) + return + + def addSpot(self): + ################################################################# + # addSpot(self) + # This function will send out a message to + # ask dataHolder to create a default Spot light + ################################################################# + messenger.send('LP_addLight',['spot']) + return + + def setLightingColorVec(self,color): + ################################################################# + # setLightingColorVec(self,color) + # Call Back function. This will be called + # when user try to change the color of light. + ################################################################# + if self.currentLight==None: + return + self.currentLight.setColor(VBase4((color[0]/255),(color[1]/255),(color[2]/255),1)) + return + + def setSpecularColor(self,color): + ################################################################# + # setSpecularColor(self,color) + # Call Back function. This will be called + # when user try to change the Specular color of light. + ################################################################# + if self.currentLight==None: + return + self.currentLight.setSpecColor(VBase4((color[0]/255),(color[1]/255),(color[2]/255),1)) + return + + def setPosition(self,position): + ################################################################# + # setPosition(self,position) + # Call Back function. This will be called + # when user try to change the position of light. + ################################################################# + if self.currentLight==None: + return + self.currentLight.setPosition(Point3(position[0],position[1],position[2])) + return + + def setOrientation(self, orient): + ################################################################# + # setOrientation(self, orient) + # Call Back function. This will be called + # when user try to change the orientation of light. + ################################################################# + if self.currentLight==None: + return + self.currentLight.setOrientation(Vec3(orient[0],orient[1],orient[2])) + return + + def setConstantAttenuation(self, value): + ################################################################# + # setConstantAttenuation(self, value) + # Call Back function. This will be called + # when user try to change the Constant Attenuation of light. + ################################################################# + self.currentLight.setConstantAttenuation(value) + return + + def setLinearAttenuation(self, value): + ################################################################# + # setLinearAttenuation(self, value) + # Call Back function. This will be called + # when user try to change the Linear Attenuation of light. + ################################################################# + self.currentLight.setLinearAttenuation(value) + return + + def setQuadraticAttenuation(self, value): + ################################################################# + # setQuadraticAttenuation(self, value) + # Call Back function. This will be called + # when user try to change the Quadratic Attenuation of light. + ################################################################# + self.currentLight.setQuadraticAttenuation(value) + return + + def setExponent(self, value): + ################################################################# + # setExponent(self, value) + # Call Back function. This will be called + # when user try to change Exponent value of light. + ################################################################# + self.currentLight.setExponent(value) + return diff --git a/doc/SceneEditor/propertyWindow.py b/doc/SceneEditor/propertyWindow.py new file mode 100644 index 0000000000..26607bb4dd --- /dev/null +++ b/doc/SceneEditor/propertyWindow.py @@ -0,0 +1,1454 @@ +################################################################# +# propertyWindow.py +# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +################################################################# +from AppShell import * +from TkGlobal import * + +from seColorEntry import * + +import Floater +import Dial +import Slider +import VectorWidgets +import Task + +class propertyWindow(AppShell,Pmw.MegaWidget): + ################################################################# + # propertyWindow(AppShell,Pmw.MegaWidget) + # This class will create a widow to show the object property and + # let user can change shoe of them. + ################################################################# + appversion = '1.0' + appname = 'Property Window' + frameWidth = 400 + frameHeight = 400 + padx = 0 + pady = 0 + usecommandarea = 0 + usestatusarea = 0 + widgetsDict = {} + + + def __init__(self, target, type, info, parent = None, nodePath = render, **kw): + self.nodePath = target + self.name = target.getName() + self.type = type + self.info = info + + + # Initialise superclass + Pmw.MegaWidget.__init__(self, parent) + + # Define the megawidget options. + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + + if parent == None: + self.parent = Toplevel() + AppShell.__init__(self, self.parent) + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + def appInit(self): + return + + def createInterface(self): + # The interior of the toplevel panel + interior = self.interior() + mainFrame = Frame(interior) + name_label = Label(mainFrame, text= self.name,font=('MSSansSerif', 15), + relief = RIDGE, borderwidth=5) + name_label.pack() + outFrame = Frame(mainFrame, relief = RIDGE, borderwidth=3) + self.contentWidge = self.createcomponent( + 'scrolledFrame', + (), None, + Pmw.ScrolledFrame, (outFrame,), + hull_width = 200, hull_height = 300, + usehullsize = 1) + self.contentFrame = self.contentWidge.component('frame') + self.contentWidge.pack(fill = 'both', expand = 1,padx = 3, pady = 5) + outFrame.pack(fill = 'both', expand = 1) + + # Creating different interface depands on object's type + if self.type == 'camera': + self.cameraInterface(self.contentFrame) + self.accept('forPorpertyWindow'+self.name, self.trackDataFromSceneCamera) + elif self.type == 'Model': + self.modelInterface(self.contentFrame) + self.accept('forPorpertyWindow'+self.name, self.trackDataFromSceneModel) + elif self.type == 'Actor': + self.modelInterface(self.contentFrame) + self.actorInterface(self.contentFrame) + self.accept('forPorpertyWindow'+self.name, self.trackDataFromSceneActor) + pass + elif self.type == 'Light': + self.lightInterface(self.contentFrame) + self.accept('forPorpertyWindow'+self.name, self.trackDataFromSceneLight) + pass + elif self.type == 'dummy': + self.dummyInterface(self.contentFrame) + self.accept('forPorpertyWindow'+self.name, self.trackDataFromSceneDummy) + pass + elif self.type == 'collisionNode': + self.collisionInterface(self.contentFrame) + self.accept('forPorpertyWindow'+self.name, self.trackDataFromSceneCollision) + pass + elif self.type == 'Special': + # If user try to open the property window for node "SEditor" + # It will show the grid property. + self.gridInterface(self.contentFrame) + self.accept('forPorpertyWindow'+self.name, None) + pass + + self.curveFrame = None + #### If nodePath has been binded with any curves + if self.info.has_key('curveList'): + self.createCurveFrame(self.contentFrame) + + ## Set all stuff done + mainFrame.pack(fill = 'both', expand = 1) + + + def createMenuBar(self): + # we don't need menu bar here. + self.menuBar.destroy() + + def onDestroy(self, event): + self.ignore('forPorpertyWindow'+self.name) + messenger.send('PW_close', [self.name]) + ''' + If you have open any thing, please rewrite here! + ''' + pass + + def createEntryField(self, parent,text, value, + command, initialState, labelWidth = 12, + side = 'left', fill = X, expand = 0, + validate = None, + defaultButton = False, buttonText = 'Default',defaultFunction = None ): + ################################################################# + # createEntryField(self, parent,text, value, + # command, initialState, labelWidth = 12, + # side = 'left', fill = X, expand = 0, + # validate = None, + # defaultButton = False, buttonText = 'Default',defaultFunction = None ): + # This function will create a Entry on the frame "parent" + # Also, if user has enabled the "defaultButton," it will create a button right after the entry. + ################################################################# + frame = Frame(parent) + widget = Pmw.EntryField(frame, labelpos='w', label_text = text, + value = value, entry_font=('MSSansSerif', 10),label_font=('MSSansSerif', 10), + modifiedcommand=command, validate = validate, + label_width = labelWidth) + widget.configure(entry_state = initialState) + widget.pack(side=LEFT) + self.widgetsDict[text] = widget + if defaultButton and (defaultFunction!=None): + # create a button if they need. + widget = Button(frame, text=buttonText, font=('MSSansSerif', 10), command = defaultFunction) + widget.pack(side=LEFT, padx=3) + self.widgetsDict[text+'-'+'DefaultButton']=widget + + frame.pack(side = side, fill = fill, expand = expand,pady=3) + + + def createPosEntry(self, contentFrame): + ################################################################# + # createPosEntry(self, contentFrame) + # This function will create three entries for setting position for the objects. + # the entry type is Floater. + # And, it will set the call back function to setNodePathPosHprScale() + ################################################################# + posInterior = Frame(contentFrame) + self.posX = self.createcomponent('posX', (), None, + Floater.Floater, (posInterior,), + text = 'X', relief = FLAT, + value = self.nodePath.getX(), + label_foreground = 'Red', + entry_width = 9) + self.posX['commandData'] = ['x'] + self.posX['command'] = self.setNodePathPosHprScale + self.posX.pack(side=LEFT,expand=0,fill=X, padx=1) + + self.posY = self.createcomponent('posY', (), None, + Floater.Floater, (posInterior,), + text = 'Y', relief = FLAT, + value = self.nodePath.getY(), + label_foreground = '#00A000', + entry_width = 9) + self.posY['commandData'] = ['y'] + self.posY['command'] = self.setNodePathPosHprScale + self.posY.pack(side=LEFT, expand=0,fill=X, padx=1) + + self.posZ = self.createcomponent('posZ', (), None, + Floater.Floater, (posInterior,), + text = 'Z', relief = FLAT, + value = self.nodePath.getZ(), + label_foreground = 'Blue', + entry_width = 9) + self.posZ['commandData'] = ['z'] + self.posZ['command'] = self.setNodePathPosHprScale + self.posZ.pack(side=LEFT, expand=0,fill=X, padx=1) + posInterior.pack(side=TOP, expand=0,fill=X, padx=3, pady=3) + + def createHprEntry(self, contentFrame): + ################################################################# + # createHprEntry(self, contentFrame) + # This function will create three entries for setting orientation for the objects. + # the entry type is Floater. + # And, it will set the call back function to setNodePathPosHprScale() + ################################################################# + hprInterior = Frame(contentFrame) + self.hprH = self.createcomponent('hprH', (), None, + Dial.AngleDial, (hprInterior,), + style = 'mini', + text = 'H', value = self.nodePath.getH(), + relief = FLAT, + label_foreground = 'blue', + entry_width = 9) + self.hprH['commandData'] = ['h'] + self.hprH['command'] = self.setNodePathPosHprScale + self.hprH.pack(side = LEFT, expand=0,fill=X) + + self.hprP = self.createcomponent('hprP', (), None, + Dial.AngleDial, (hprInterior,), + style = 'mini', + text = 'P', value = self.nodePath.getP(), + relief = FLAT, + label_foreground = 'red', + entry_width = 9) + self.hprP['commandData'] = ['p'] + self.hprP['command'] = self.setNodePathPosHprScale + self.hprP.pack(side = LEFT, expand=0,fill=X) + + self.hprR = self.createcomponent('hprR', (), None, + Dial.AngleDial, (hprInterior,), + style = 'mini', + text = 'R', value = self.nodePath.getR(), + relief = FLAT, + label_foreground = '#00A000', + entry_width = 9) + self.hprR['commandData'] = ['r'] + self.hprR['command'] = self.setNodePathPosHprScale + self.hprR.pack(side = LEFT, expand=0,fill=X) + + hprInterior.pack(side=TOP, expand=0,fill=X, padx=3, pady=3) + + + def createScaleEntry(self, contentFrame): + ################################################################# + # createScaleEntry(self, contentFrame) + # This function will create three entries for setting scale for the objects. + # the entry type is Floater. + # And, it will set the call back function to setNodePathPosHprScale() + ################################################################# + scaleInterior = Frame(contentFrame) + + self.scale = self.createcomponent('scale', (), None, + Floater.Floater, (scaleInterior,), + text = 'Scale', + relief = FLAT, + min = 0.0001, value = self.nodePath.getScale().getX(), + resetValue = 1.0, + label_foreground = 'Blue') + self.scale['commandData'] = ['s'] + self.scale['command'] = self.setNodePathPosHprScale + self.scale.pack(side=LEFT,expand=0,fill=X) + + scaleInterior.pack(side=TOP,expand=0,fill=X, padx=3, pady=3) + + def createColorEntry(self, contentFrame): + ################################################################# + # createColorEntry(self, contentFrame) + # This function will create three entries for setting color for the objects. + # the entry type is Floater. + # And, it will set the call back function to setNodeColorVec() + ################################################################# + color = self.nodePath.getColor() + print color + self.nodeColor = VectorWidgets.ColorEntry( + contentFrame, text = 'Node Color', value=[color.getX()*255, + color.getY()*255, + color.getZ()*255, + color.getW()*255]) + self.nodeColor['command'] = self.setNodeColorVec + self.nodeColor['resetValue'] = [255,255,255,255] + self.nodeColor.place(anchor=NW,y=235) + self.bind(self.nodeColor, 'Set nodePath color') + self.nodeColor.pack(side=TOP,expand=0,fill=X, padx=3, pady=3) + return + + def setNodeColorVec(self, color): + ################################################################# + # setNodeColorVec(self, color) + # This function will set the color of the object + ################################################################# + self.nodePath.setColor(color[0]/255.0, + color[1]/255.0, + color[2]/255.0, + color[3]/255.0) + return + + + def setNodePathPosHprScale(self, data, axis): + ################################################################# + # setNodePathPosHprScale(self, data, axis) + # This function will set the postion, orientation or scale of the object + # use the "axis" parameter to decide which property should be set. + ################################################################# + if axis == 'x': + self.nodePath.setX(data) + elif axis == 'y': + self.nodePath.setY(data) + elif axis == 'z': + self.nodePath.setZ(data) + elif axis == 'h': + self.nodePath.setH(data) + elif axis == 'p': + self.nodePath.setP(data) + elif axis == 'r': + self.nodePath.setR(data) + elif axis == 's': + self.nodePath.setScale(data) + + + #### Curve property + def createCurveFrame(self, contentFrame): + ################################################################# + # createCurveFrame(self, contentFrame) + # Draw the curve property frame + # This function will draw the property frame and content of curves + # pass the target frame as a variable + ################################################################# + if self.curveFrame==None: + self.curveFrame = Frame(contentFrame) + group = Pmw.Group(self.curveFrame, + tag_text='Motion Path List for this Node', + tag_font=('MSSansSerif', 10)) + innerFrame = group.interior() + n = 0 + for curve in self.info['curveList']: + n += 1 + self.createEntryField(innerFrame,'Curve %d:' %n, + value = curve.getCurve(0).getName(), + command = None, + initialState='disabled', + side = 'top', + defaultButton = True, + buttonText = 'delete', + defaultFunction = lambda a = n, b = self : b.deleteCurve(a)) + group.pack(side = TOP, fill = X, expand = 0,pady=3, padx=3) + self.curveFrame.pack(side = TOP, fill = X, expand = 0,pady=3, padx=3) + + return + + def deleteCurve(self, number = 0): + ################################################################# + # deleteCurve(self, number = 0) + # Call back function, will be called when user click on the "delete" button beside the curve name. + # This function will send the message to sceneEditor to remove the target curve + # and will set a callback function waitting the result. + ################################################################# + widget = self.widgetsDict['Curve %d:' %number] + curveName = widget.getvalue() + self.accept('curveRemovedFromNode',self.redrawCurveProperty) + messenger.send('PW_removeCurveFromNode',[self.nodePath, curveName]) + return + + def redrawCurveProperty(self, nodePath, curveList): + ################################################################# + # redrawCurveProperty(self, nodePath, curveList) + # Callback function, will be called once get the result from dataHolder. + # It will check the target nodePath first, then check the curve list is empty or not. + # If yes, then delete whole curve frame. If not, then renew the data and redraw the curve frame again. + ################################################################# + self.name = self.nodePath.getName() + if self.name != nodePath.getName(): + messenger.send('curveRemovedFromNode',[nodePath, curveList]) + return + else: + self.ignore('curveRemovedFromNode') + + if curveList!= None: + del self.info['curveList'] + self.info['curveList'] = curveList + self.curveFrame.destroy() + del self.curveFrame + self.curveFrame = None + self.createCurveFrame(self.contentFrame) + else: + del self.info['curveList'] + self.curveFrame.destroy() + del self.curveFrame + self.curveFrame = None + return + + #### + #### Anything about Camera will be here! + #### + def cameraInterface(self, contentFrame): + ################################################################# + # cameraInterface(self, interior, mainFrame) + # Create the interface for camera node. + ################################################################# + + ## Type entry : unchageable + widget = self.createEntryField(contentFrame,'Type:', + value = self.type, + command = None, + initialState='disabled', + side = 'top') + + ## lens Type entry + widget = self.createEntryField(contentFrame, 'Lens Type:', + value = self.info['lensType'], + command = None, + initialState='disabled', + side = 'top') + + ## Pos + group = Pmw.Group(contentFrame,tag_text='Position', + tag_font=('MSSansSerif', 10)) + self.createPosEntry(group.interior()) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + + ## Orientation + group = Pmw.Group(contentFrame,tag_text='Orientation', + tag_font=('MSSansSerif', 10)) + self.createHprEntry(group.interior()) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + + ## near entry + group = Pmw.Group(contentFrame,tag_text='Lens Property', + tag_font=('MSSansSerif', 10)) + lensFrame = group.interior() + widget = self.createEntryField(lensFrame, 'Near:',value = self.info['near'], + command = self.setCameraNear, + initialState='normal', + validate = Pmw.realvalidator, + side = 'top', + defaultButton = True, + defaultFunction = self.defaultCameraNear) + + ## far entry + widget = self.createEntryField(lensFrame, 'Far:', + value = self.info['far'], + command = self.setCameraFar, + initialState='normal', + side = 'top', + validate = Pmw.realvalidator, + defaultButton = True, + defaultFunction = self.defaultCameraFar) + + ## Hfov entry + widget = self.createEntryField(lensFrame, 'H.F.O.V.:', + value = self.info['hFov'], + command = self.setCameraFov, + validate = Pmw.realvalidator, + initialState='normal', + side = 'top', + defaultButton = True, + defaultFunction = self.defaultCameraHfov) + + ## Vfov entry + widget = self.createEntryField(lensFrame, 'V.F.O.V.:', + value = self.info['vFov'], + command = self.setCameraFov, + validate = Pmw.realvalidator, + initialState='normal', + side = 'top', + defaultButton = True, + defaultFunction = self.defaultCameraVfov) + + ## Film Size entry + frame = Frame(lensFrame) + widget = Label(frame, text = "Film Size:", font=('MSSansSerif', 10),width=12) + widget.pack(side=LEFT) + frame.pack(side = TOP, fill = X, expand = 0, pady=3) + + frame = Frame(lensFrame) + widget = Pmw.EntryField(frame, labelpos='w', label_text = ' ', + value = self.info['FilmSize'].getX(), + entry_font=('MSSansSerif', 10), + label_font=('MSSansSerif', 10), + modifiedcommand=self.setCameraFilmSize, validate = Pmw.realvalidator, + entry_width = 8) + self.widgetsDict['FilmSizeX']=widget + widget.pack(side=LEFT, padx=3) + widget = Pmw.EntryField(frame, labelpos='w', label_text = ': ', value = self.info['FilmSize'].getY() , + label_font=('MSSansSerif', 10), + entry_font=('MSSansSerif', 10), + modifiedcommand=self.setCameraFilmSize, validate = Pmw.realvalidator, + entry_width = 8) + self.widgetsDict['FilmSizeY']=widget + widget.pack(side=LEFT, padx=3) + widget = Button(frame, text='Default', font=('MSSansSerif', 10), command = self.defaultCameraFilmSize) + widget.pack(side=LEFT, padx=3) + self.widgetsDict['FilmSize'+'-'+'DefaultButton']=widget + frame.pack(side = TOP, fill = X, expand = 0,pady=0) + + ## Focal Length entry + widget = self.createEntryField(lensFrame, 'Focal Length:', + value = self.info['focalLength'], + command = self.setCameraFocalLength, + validate = Pmw.realvalidator, + initialState='normal', + side = 'top', + defaultButton = True, + defaultFunction = self.defaultCameraFocalLength) + group.pack(side = TOP, fill = X, expand = 0,pady=2) + + + def defaultCameraFar(self): + ################################################################# + # defaultCameraFar(self) + # set the camera "Far" value back to default. + ################################################################# + widget = self.widgetsDict['Far:'] + widget.setvalue(base.cam.node().getLens().getDefaultFar()) + return + + def setCameraFar(self): + ################################################################# + # setCameraFar(self) + # set the camera "Far" value to what now user has typed in the entry + ################################################################# + if self.widgetsDict['Far:'].getvalue() != '': + value = float(self.widgetsDict['Far:'].getvalue()) + else: + value = 0 + camera.getChild(0).node().getLens().setFar(value) + return + + def defaultCameraNear(self): + ################################################################# + # defaultCameraNear(self) + # set the camera "Near" value back to default. + ################################################################# + widget = self.widgetsDict['Near:'] + widget.setvalue(base.cam.node().getLens().getDefaultNear()) + return + + def setCameraNear(self): + ################################################################# + # setCameraNear(self) + # set the camera "Near" value to what now user has typed in the entry + ################################################################# + if self.widgetsDict['Near:'].getvalue() != '': + value = float(self.widgetsDict['Near:'].getvalue()) + else: + value = 0 + camera.getChild(0).node().getLens().setNear(value) + return + + def defaultCameraHfov(self): + ################################################################# + # defaultCameraHfov(self) + # set the camera "Hfov" value back to default. + ################################################################# + widget = self.widgetsDict['H.F.O.V.:'] + widget.setvalue(45.0) + return + + def setCameraFov(self): + ################################################################# + # setCameraFov(self) + # set the camera "Fov" value to what now user has typed in the entry + ################################################################# + if self.widgetsDict['H.F.O.V.:'].getvalue() != '': + value1 = float(self.widgetsDict['H.F.O.V.:'].getvalue()) + else: + value1 = 0 + if self.widgetsDict['V.F.O.V.:'].getvalue() != '': + value2 = float(self.widgetsDict['V.F.O.V.:'].getvalue()) + else: + value2 = 0 + camera.getChild(0).node().getLens().setFov(VBase2(value1,value2)) + return + + def defaultCameraVfov(self): + ################################################################# + # defaultCameraVfov(self) + # set the camera "Vfov" value back to default. + ################################################################# + widget = self.widgetsDict['V.F.O.V.:'] + widget.setvalue(34.51587677) + return + + def defaultCameraFocalLength(self): + ################################################################# + # defaultCameraFocalLength(self) + # set the camera "Focal Length" value back to default. + ################################################################# + widget = self.widgetsDict['Focal Length:'] + widget.setvalue(1.20710682869) + return + + def setCameraFocalLength(self): + ################################################################# + # setCameraFocalLength(self) + # set the camera "Focal Length" value to what now user has typed in the entry + ################################################################# + if self.widgetsDict['Focal Length:'].getvalue() != '': + value = float(self.widgetsDict['Focal Length:'].getvalue()) + else: + value = 0 + camera.getChild(0).node().getLens().setFocalLength(value) + camera.getChild(0).node().getLens().setFilmSize(VBase2(float(self.widgetsDict['FilmSizeX'].getvalue()),float(self.widgetsDict['FilmSizeY'].getvalue()))) + return + + def defaultCameraFilmSize(self): + ################################################################# + # defaultCameraFilmSize(self) + # set the camera "Film Size" value back to default. + ################################################################# + widget = self.widgetsDict['FilmSizeX'] + widget.setvalue(1) + widget = self.widgetsDict['FilmSizeY'] + widget.setvalue(0.75) + return + + def setCameraFilmSize(self): + ################################################################# + # setCameraFilmSize(self) + # set the camera "Film Size" value to what now user has typed in the entry + ################################################################# + if self.widgetsDict['FilmSizeX'].getvalue() != '': + value1 = float(self.widgetsDict['FilmSizeX'].getvalue()) + else: + value1 = 0 + if self.widgetsDict['FilmSizeY'].getvalue() != '': + value2 = float(self.widgetsDict['FilmSizeY'].getvalue()) + else: + value2 = 0 + camera.getChild(0).node().getLens().setFilmSize(VBase2(value1,value2)) + return + + #### + #### Anything about Model & Actor will be here! + #### + def modelInterface(self, contentFrame): + ################################################################# + # modelInterface(self, contentFrame) + # Create the basic interface for ModelRoot Type Node + ################################################################# + widget = self.createEntryField(contentFrame,'Type:', + value = self.type, + command = None, + initialState='disabled', + side = 'top') + widget = self.createEntryField(contentFrame,'Model File:', + value = self.info['filePath'].getFullpath(), + command = None, + initialState='disabled', + side = 'top', + defaultButton = False, + buttonText = 'Change', + defaultFunction = None) + group = Pmw.Group(contentFrame,tag_text='Position', + tag_font=('MSSansSerif', 10)) + self.createPosEntry(group.interior()) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + + group = Pmw.Group(contentFrame,tag_text='Orientation', + tag_font=('MSSansSerif', 10)) + self.createHprEntry(group.interior()) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + + self.createScaleEntry(contentFrame) + + group = Pmw.Group(contentFrame,tag_text='Color', + tag_font=('MSSansSerif', 10)) + frame = group.interior() + self.createColorEntry(frame) + self.varAlpha = IntVar() + self.varAlpha.set(self.nodePath.hasTransparency()) + checkButton = Checkbutton(frame, text='Enable Alpha', + variable=self.varAlpha, command=self.toggleAlpha) + checkButton.pack(side=RIGHT,pady=3) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + return + + def toggleAlpha(self): + ################################################################# + # toggleAlpha(self) + # This funtion will toggle the objects alpha value + # And, it will also reset the "Bin" to + # "fixed" if user enable the alpha for this object. + ################################################################# + if self.nodePath.hasTransparency(): + self.nodePath.clearTransparency() + self.nodePath.setBin("default", 0) + else: + self.nodePath.setTransparency(True) + self.nodePath.setBin("fixed", 1) + return + + def actorInterface(self, contentFrame): + ################################################################# + # actorInterface(self, contentFrame) + # Create the basic interface for Actor Type Node + ################################################################# + self.animFrame = None + animeDict = self.info['animDict'] + if len(animeDict)==0: + return + + self.animFrame = Frame(contentFrame) + group = Pmw.Group(self.animFrame,tag_text='Animations', + tag_font=('MSSansSerif', 10)) + innerFrame = group.interior() + for name in animeDict: + self.createEntryField(innerFrame, name, + value = animeDict[name], + command = None, + initialState='disabled', + side = 'top', + defaultButton = True, + buttonText = 'Remove', + defaultFunction = lambda a = name, b = self : b.deleteAnimation(a)) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + self.animFrame.pack(side=TOP,fill = X, expand = 0, pady=3) + return + + + def deleteAnimation(self, anim): + ################################################################# + # deleteAnimation(self, anim) + # This function will delete the animation named "anim" in this actor + # But, not directly removed be this function. + # This function will send out a message to notice dataHolder to remove this animation + ################################################################# + print anim + widget = self.widgetsDict[anim] + self.accept('animRemovedFromNode',self.redrawAnimProperty) + messenger.send('PW_removeAnimFromNode',[self.name, anim]) + return + + def redrawAnimProperty(self, nodePath, animDict): + ################################################################# + # redrawCurveProperty(self, nodePath, curveList) + # Callback function, will be called once get the result from dataHolder. + # It will check the target nodePath first, then check the curve list is empty or not. + # If yes, then delete whole curve frame. If not, then renew the data and redraw the curve frame again. + ################################################################# + self.name = self.nodePath.getName() + if self.name != nodePath.getName(): + messenger.send('animRemovedFromNode',[nodePath, animDict]) + return + else: + self.ignore('animRemovedFromNode') + + if len(animDict)!= 0: + del self.info['animDict'] + self.info['animDict'] = animDict + self.animFrame.destroy() + del self.animFrame + self.animFrame = None + self.actorInterface(self.contentFrame) + else: + del self.info['animDict'] + self.animFrame.destroy() + del self.animFrame + self.animFrame = None + return + + #### + #### Anything about Light will be here! + #### + def lightInterface(self, contentFrame): + ################################################################# + # lightInterface(self, contentFrame) + # Create the basic interface for light Type Node + ################################################################# + widget = self.createEntryField(contentFrame,'Type:', + value = self.nodePath.node().getType().getName(), + command = None, + initialState='disabled', + side = 'top') + + self.lightNode = self.info['lightNode'] + + lightingGroup = Pmw.Group(contentFrame,tag_pyclass=None) + frame = lightingGroup.interior() + self.lightColor = seColorEntry( + frame, text = 'Light Color', label_font=('MSSansSerif', 10), + value=[self.lightNode.lightcolor.getX()*255, self.lightNode.lightcolor.getY()*255,self.lightNode.lightcolor.getZ()*255,0]) + self.lightColor['command'] = self.setLightingColorVec + self.lightColor['resetValue'] = [0.3*255,0.3*255,0.3*255,0] + self.lightColor.pack(side=TOP, fill=X,expand=1, padx = 2, pady =2) + self.bind(self.lightColor, 'Set light color') + + self.varActive = IntVar() + self.varActive.set(self.lightNode.active) + checkButton = Checkbutton(frame, text='Enable This Light', + variable=self.varActive, command=self.toggleLight) + checkButton.pack(side=RIGHT,pady=3) + lightingGroup.pack(side=TOP, fill = X, expand =1) + + # Directional light controls + if self.lightNode.type == 'directional': + lightingGroup = Pmw.Group(contentFrame,tag_pyclass=None) + directionalPage = lightingGroup.interior() + self.dSpecularColor = seColorEntry( + directionalPage, text = 'Specular Color', label_font=('MSSansSerif', 10),value = [self.lightNode.specularColor.getX()*255,self.lightNode.specularColor.getY()*255,self.lightNode.specularColor.getZ()*255,0]) + self.dSpecularColor['command'] = self.setSpecularColor + self.dSpecularColor.pack(fill = X, expand = 1) + self.bind(self.dSpecularColor, + 'Set directional light specular color') + self.dPosition = VectorWidgets.Vector3Entry( + directionalPage, text = 'Position', label_font=('MSSansSerif', 10),value = [self.lightNode.getPosition().getX(),self.lightNode.getPosition().getY(),self.lightNode.getPosition().getZ()]) + self.dPosition['command'] = self.setPosition + self.dPosition['resetValue'] = [0,0,0,0] + self.dPosition.pack(fill = X, expand = 1) + self.bind(self.dPosition, 'Set directional light position') + self.dOrientation = VectorWidgets.Vector3Entry( + directionalPage, text = 'Orientation', label_font=('MSSansSerif', 10), + value = [self.lightNode.getOrientation().getX(),self.lightNode.getOrientation().getY(),self.lightNode.getOrientation().getZ(),0]) + self.dOrientation['command'] = self.setOrientation + self.dOrientation['resetValue'] = [0,0,0,0] + self.dOrientation.pack(fill = X, expand = 1) + self.bind(self.dOrientation, 'Set directional light orientation') + + lightingGroup.pack(side=TOP, fill = X, expand =1) + + + elif self.lightNode.type == 'point': + # Point light controls + lightingGroup = Pmw.Group(contentFrame,tag_pyclass=None) + pointPage = lightingGroup.interior() + self.pSpecularColor = seColorEntry( + pointPage, text = 'Specular Color', label_font=('MSSansSerif', 10), + value = [self.lightNode.specularColor.getX(),self.lightNode.specularColor.getY(),self.lightNode.specularColor.getZ(),0]) + self.pSpecularColor['command'] = self.setSpecularColor + self.pSpecularColor.pack(fill = X, expand = 1) + self.bind(self.pSpecularColor, + 'Set point light specular color') + + self.pPosition = VectorWidgets.Vector3Entry( + pointPage, text = 'Position', label_font=('MSSansSerif', 10), + value = [self.lightNode.getPosition().getX(),self.lightNode.getPosition().getY(),self.lightNode.getPosition().getZ(),0]) + self.pPosition['command'] = self.setPosition + self.pPosition['resetValue'] = [0,0,0,0] + self.pPosition.pack(fill = X, expand = 1) + self.bind(self.pPosition, 'Set point light position') + + self.pConstantAttenuation = Slider.Slider( + pointPage, + text = 'Constant Attenuation', label_font=('MSSansSerif', 10), + max = 1.0, + value = self.lightNode.constant) + self.pConstantAttenuation['command'] = self.setConstantAttenuation + self.pConstantAttenuation.pack(fill = X, expand = 1) + self.bind(self.pConstantAttenuation, + 'Set point light constant attenuation') + + self.pLinearAttenuation = Slider.Slider( + pointPage, + text = 'Linear Attenuation', label_font=('MSSansSerif', 10), + max = 1.0, + value = self.lightNode.linear) + self.pLinearAttenuation['command'] = self.setLinearAttenuation + self.pLinearAttenuation.pack(fill = X, expand = 1) + self.bind(self.pLinearAttenuation, + 'Set point light linear attenuation') + + self.pQuadraticAttenuation = Slider.Slider( + pointPage, + text = 'Quadratic Attenuation', label_font=('MSSansSerif', 10), + max = 1.0, + value = self.lightNode.quadratic) + self.pQuadraticAttenuation['command'] = self.setQuadraticAttenuation + self.pQuadraticAttenuation.pack(fill = X, expand = 1) + self.bind(self.pQuadraticAttenuation, + 'Set point light quadratic attenuation') + + lightingGroup.pack(side=TOP, fill = X, expand =1) + + + elif self.lightNode.type == 'spot': + # Spot light controls + lightingGroup = Pmw.Group(contentFrame,tag_pyclass=None) + spotPage = lightingGroup.interior() + self.sSpecularColor = seColorEntry( + spotPage, text = 'Specular Color', label_font=('MSSansSerif', 10), + value = [self.lightNode.specularColor.getX()*255,self.lightNode.specularColor.getY()*255,self.lightNode.specularColor.getZ()*255,0]) + self.sSpecularColor['command'] = self.setSpecularColor + self.sSpecularColor.pack(fill = X, expand = 1) + self.bind(self.sSpecularColor, + 'Set spot light specular color') + + self.sConstantAttenuation = Slider.Slider( + spotPage, + text = 'Constant Attenuation', label_font=('MSSansSerif', 10), + max = 1.0, + value = self.lightNode.constant) + self.sConstantAttenuation['command'] = self.setConstantAttenuation + self.sConstantAttenuation.pack(fill = X, expand = 1) + self.bind(self.sConstantAttenuation, + 'Set spot light constant attenuation') + + self.sLinearAttenuation = Slider.Slider( + spotPage, + text = 'Linear Attenuation', label_font=('MSSansSerif', 10), + max = 1.0, + value = self.lightNode.linear) + self.sLinearAttenuation['command'] = self.setLinearAttenuation + self.sLinearAttenuation.pack(fill = X, expand = 1) + self.bind(self.sLinearAttenuation, + 'Set spot light linear attenuation') + + self.sQuadraticAttenuation = Slider.Slider( + spotPage, + text = 'Quadratic Attenuation', label_font=('MSSansSerif', 10), + max = 1.0, + value = self.lightNode.quadratic) + self.sQuadraticAttenuation['command'] = self.setQuadraticAttenuation + self.sQuadraticAttenuation.pack(fill = X, expand = 1) + self.bind(self.sQuadraticAttenuation, + 'Set spot light quadratic attenuation') + + self.sExponent = Slider.Slider( + spotPage, + text = 'Exponent', label_font=('MSSansSerif', 10), + max = 1.0, + value = self.lightNode.exponent) + self.sExponent['command'] = self.setExponent + self.sExponent.pack(fill = X, expand = 1) + self.bind(self.sExponent, + 'Set spot light exponent') + lightingGroup.pack(side=TOP, fill = X, expand =1) + + return + + def setLightingColorVec(self,color): + if self.lightNode==None: + return + self.lightNode.setColor(VBase4((color[0]/255),(color[1]/255),(color[2]/255),1)) + return + + def setSpecularColor(self,color): + if self.lightNode==None: + return + self.lightNode.setSpecColor(VBase4((color[0]/255),(color[1]/255),(color[2]/255),1)) + return + + def setPosition(self,position): + if self.lightNode==None: + return + self.lightNode.setPosition(Point3(position[0],position[1],position[2])) + return + + def setOrientation(self, orient): + if self.lightNode==None: + return + self.lightNode.setOrientation(Vec3(orient[0],orient[1],orient[2])) + return + + def setConstantAttenuation(self, value): + self.lightNode.setConstantAttenuation(value) + return + + def setLinearAttenuation(self, value): + self.lightNode.setLinearAttenuation(value) + return + + def setQuadraticAttenuation(self, value): + self.lightNode.setQuadraticAttenuation(value) + return + + def setExponent(self, value): + self.lightNode.setExponent(value) + return + + def toggleLight(self): + messenger.send('PW_toggleLight',[self.lightNode]) + return + + + #### + #### Anything about Dummy will be here! + #### + def dummyInterface(self, contentFrame): + ################################################################# + # dummyInterface(self, contentFrame) + # Create the basic interface for dummy Type Node + ################################################################# + '''dummyInterface(self, contentFrame) + Create the basic interface for dummy Node + ''' + widget = self.createEntryField(contentFrame,'Type:', + value = 'Dummy Nodepath', + command = None, + initialState='disabled', + side = 'top') + + group = Pmw.Group(contentFrame,tag_text='Position', + tag_font=('MSSansSerif', 10)) + self.createPosEntry(group.interior()) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + + group = Pmw.Group(contentFrame,tag_text='Orientation', + tag_font=('MSSansSerif', 10)) + self.createHprEntry(group.interior()) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + + self.createScaleEntry(contentFrame) + + group = Pmw.Group(contentFrame,tag_text='Color', + tag_font=('MSSansSerif', 10)) + frame = group.interior() + self.createColorEntry(frame) + self.varAlpha = IntVar() + self.varAlpha.set(self.nodePath.hasTransparency()) + checkButton = Checkbutton(frame, text='Enable Alpha', + variable=self.varAlpha, command=self.toggleAlpha) + checkButton.pack(side=RIGHT,pady=3) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + return + + + ######### + ####### This will be called when user try to open property window for SEditor Node + ######### + def gridInterface(self, contentFrame): + ################################################################# + # gridInterface(self, contentFrame) + # Create the basic interface for grid (Which is stolen from directGrid) + ################################################################# + group = Pmw.Group(contentFrame,tag_text='Grid Property', + tag_font=('MSSansSerif', 10)) + group.pack(side=TOP,fill = X, expand = 0, padx = 3, pady=3) + + gridPage = group.interior() + + self.xyzSnap = BooleanVar() + self.xyzSnapButton = Checkbutton( + gridPage, + text = 'XYZ Snap', + anchor = 'w', justify = LEFT, + variable = self.xyzSnap, + command = self.toggleXyzSnap) + self.xyzSnapButton.pack(fill = X, expand = 0, pady=3) + + self.hprSnap = BooleanVar() + self.hprSnapButton = Checkbutton( + gridPage, + text = 'HPR Snap', + anchor = 'w', justify = LEFT, + variable = self.hprSnap, + command = self.toggleHprSnap) + self.hprSnapButton.pack(fill = X, expand = 0, pady=3) + + self.xyzSnap.set(SEditor.grid.getXyzSnap()) + self.hprSnap.set(SEditor.grid.getHprSnap()) + + self.gridSpacing = Floater.Floater( + gridPage, + text = 'Grid Spacing', + min = 0.1, + value = SEditor.grid.getGridSpacing()) + self.gridSpacing['command'] = SEditor.grid.setGridSpacing + self.gridSpacing.pack(fill = X, expand = 0, pady=3) + + self.gridSize = Floater.Floater( + gridPage, + text = 'Grid Size', + min = 1.0, + value = SEditor.grid.getGridSize()) + self.gridSize['command'] = SEditor.grid.setGridSize + self.gridSize.pack(fill = X, expand = 0, pady=3) + + self.gridSnapAngle = Dial.AngleDial( + gridPage, + text = 'Snap Angle', + style = 'mini', + value = SEditor.grid.getSnapAngle()) + self.gridSnapAngle['command'] = SEditor.grid.setSnapAngle + self.gridSnapAngle.pack(fill = X, expand = 0, pady=3) + + return + + def toggleXyzSnap(self): + SEditor.grid.setXyzSnap(self.xyzSnap.get()) + return + + def toggleHprSnap(self): + SEditor.grid.setHprSnap(self.hprSnap.get()) + return + + + + ###### Collision Section!!!! + def collisionInterface(self, contentFrame): + ################################################################# + # collisionInterface(self, contentFrame) + # Create the basic interface for CollisionNode Type Node + ################################################################# + collisionNode = self.info['collisionNode'] + self.collisionObj = collisionNode.node().getSolid(0) + widget = self.createEntryField(contentFrame,'Node Type:', + value = self.type, + command = None, + initialState='disabled', + side = 'top') + cType = self.collisionObj.getType().getName() + widget = self.createEntryField(contentFrame,'Object Type:', + value = cType, + command = None, + initialState='disabled', + side = 'top') + group = Pmw.Group(contentFrame,tag_text='Position', + tag_font=('MSSansSerif', 10)) + self.createPosEntry(group.interior()) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + + group = Pmw.Group(contentFrame,tag_text='Orientation', + tag_font=('MSSansSerif', 10)) + self.createHprEntry(group.interior()) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + + self.createScaleEntry(contentFrame) + + collisionGroup = Pmw.Group(contentFrame,tag_text='Collision Object Properties', + tag_font=('MSSansSerif', 10)) + cObjFrame = collisionGroup.interior() + + ### Generate different Interface for each different kinds of Collision Objects + ### Yeah, yeah. I know this part of code looks so ugly... + if cType == 'CollisionSphere': + centerPos = self.collisionObj.getCenter() + radius = self.collisionObj.getRadius() + group = Pmw.Group(cObjFrame,tag_text='Origin', + tag_font=('MSSansSerif', 10)) + + + posInterior = Frame(group.interior()) + self.cPosX = self.createcomponent('originX', (), None, + Floater.Floater, (posInterior,), + text = 'X', relief = FLAT, + value = centerPos.getX(), + label_foreground = 'Red', + entry_width = 9) + self.cPosX['commandData'] = ['sphere-o'] + self.cPosX['command'] = self.setCollisionPosHprScale + self.cPosX.pack(side=LEFT,expand=0,fill=X, padx=1) + + self.cPosY = self.createcomponent('originY', (), None, + Floater.Floater, (posInterior,), + text = 'Y', relief = FLAT, + value = centerPos.getY(), + label_foreground = '#00A000', + entry_width = 9) + self.cPosY['commandData'] = ['sphere-o'] + self.cPosY['command'] = self.setCollisionPosHprScale + self.cPosY.pack(side=LEFT, expand=0,fill=X, padx=1) + + self.cPosZ = self.createcomponent('originZ', (), None, + Floater.Floater, (posInterior,), + text = 'Z', relief = FLAT, + value = centerPos.getZ(), + label_foreground = 'Blue', + entry_width = 9) + self.cPosZ['commandData'] = ['sphere-o'] + self.cPosZ['command'] = self.setCollisionPosHprScale + self.cPosZ.pack(side=LEFT, expand=0,fill=X, padx=1) + posInterior.pack(side=TOP, expand=0,fill=X, padx=3, pady=3) + + group.pack(side=TOP,fill = X, expand = 0, pady=3) + + scaleInterior = Frame(cObjFrame) + + self.scaleS = self.createcomponent('radius', (), None, + Floater.Floater, (scaleInterior,), + text = 'Radius', + relief = FLAT, + min = 0.0001, value = radius, + resetValue = 1.0, + label_foreground = 'Blue') + self.scaleS['commandData'] = ['sphere-radius'] + self.scaleS['command'] = self.setCollisionPosHprScale + self.scaleS.pack(side=LEFT,expand=0,fill=X) + + scaleInterior.pack(side=TOP,expand=0,fill=X, padx=3, pady=3) + pass + + elif cType == 'CollisionPolygon': + frame = Frame(cObjFrame) + label = Label(frame, text= "Sorry!",font=('MSSansSerif', 10), + borderwidth=5) + label.pack(side=LEFT) + frame.pack(side=TOP, fill=X, expand=True) + frame = Frame(cObjFrame) + label = Label(frame, text= "There is no way to change",font=('MSSansSerif', 10), + borderwidth=5) + label.pack(side=LEFT) + frame.pack(side=TOP, fill=X, expand=True) + frame = Frame(cObjFrame) + label = Label(frame, text= "the basic properties of Collision Polygon!",font=('MSSansSerif', 10), + borderwidth=5) + label.pack(side=LEFT) + frame.pack(side=TOP, fill=X, expand=True) + frame = Frame(cObjFrame) + label = Label(frame, text= "If you really need to change, recreate one...",font=('MSSansSerif', 10), + borderwidth=5) + label.pack(side=LEFT) + frame.pack(side=TOP, fill=X, expand=True) + pass + + elif cType == 'CollisionSegment': + pointA = self.collisionObj.getPointA() + pointB = self.collisionObj.getPointB() + group = Pmw.Group(cObjFrame,tag_text='Point A', + tag_font=('MSSansSerif', 10)) + posInterior = Frame(group.interior()) + self.cPosX = self.createcomponent('pointA-X', (), None, + Floater.Floater, (posInterior,), + text = 'X', relief = FLAT, + value = pointA.getX(), + label_foreground = 'Red', + entry_width = 9) + self.cPosX['commandData'] = ['segment-A'] + self.cPosX['command'] = self.setCollisionPosHprScale + self.cPosX.pack(side=LEFT,expand=0,fill=X, padx=1) + + self.cPosY = self.createcomponent('pointA-Y', (), None, + Floater.Floater, (posInterior,), + text = 'Y', relief = FLAT, + value = pointA.getY(), + label_foreground = '#00A000', + entry_width = 9) + self.cPosY['commandData'] = ['segment-A'] + self.cPosY['command'] = self.setCollisionPosHprScale + self.cPosY.pack(side=LEFT, expand=0,fill=X, padx=1) + + self.cPosZ = self.createcomponent('pointA-Z', (), None, + Floater.Floater, (posInterior,), + text = 'Z', relief = FLAT, + value = pointA.getZ(), + label_foreground = 'Blue', + entry_width = 9) + self.cPosZ['commandData'] = ['segment-A'] + self.cPosZ['command'] = self.setCollisionPosHprScale + self.cPosZ.pack(side=LEFT, expand=0,fill=X, padx=1) + posInterior.pack(side=TOP, expand=0,fill=X, padx=3, pady=3) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + group = Pmw.Group(cObjFrame,tag_text='Point B', + tag_font=('MSSansSerif', 10)) + posInterior = Frame(group.interior()) + self.cPosXB = self.createcomponent('pointB-X', (), None, + Floater.Floater, (posInterior,), + text = 'X', relief = FLAT, + value = pointB.getX(), + label_foreground = 'Red', + entry_width = 9) + self.cPosXB['commandData'] = ['segment-B'] + self.cPosXB['command'] = self.setCollisionPosHprScale + self.cPosXB.pack(side=LEFT,expand=0,fill=X, padx=1) + + self.cPosYB = self.createcomponent('pointB-Y', (), None, + Floater.Floater, (posInterior,), + text = 'Y', relief = FLAT, + value = pointB.getY(), + label_foreground = '#00A000', + entry_width = 9) + self.cPosYB['commandData'] = ['segment-B'] + self.cPosYB['command'] = self.setCollisionPosHprScale + self.cPosYB.pack(side=LEFT, expand=0,fill=X, padx=1) + + self.cPosZB = self.createcomponent('pointB-Z', (), None, + Floater.Floater, (posInterior,), + text = 'Z', relief = FLAT, + value = pointB.getZ(), + label_foreground = 'Blue', + entry_width = 9) + self.cPosZB['commandData'] = ['segment-B'] + self.cPosZB['command'] = self.setCollisionPosHprScale + self.cPosZB.pack(side=LEFT, expand=0,fill=X, padx=1) + posInterior.pack(side=TOP, expand=0,fill=X, padx=3, pady=3) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + pass + elif cType == 'CollisionRay': + origin = self.collisionObj.getOrigin() + direction = self.collisionObj.getDirection() + group = Pmw.Group(cObjFrame,tag_text='Origin Point', + tag_font=('MSSansSerif', 10)) + posInterior = Frame(group.interior()) + self.cPosX = self.createcomponent('origin-X', (), None, + Floater.Floater, (posInterior,), + text = 'X', relief = FLAT, + value = origin.getX(), + label_foreground = 'Red', + entry_width = 9) + self.cPosX['commandData'] = ['ray-A'] + self.cPosX['command'] = self.setCollisionPosHprScale + self.cPosX.pack(side=LEFT,expand=0,fill=X, padx=1) + + self.cPosY = self.createcomponent('origin-Y', (), None, + Floater.Floater, (posInterior,), + text = 'Y', relief = FLAT, + value = origin.getY(), + label_foreground = '#00A000', + entry_width = 9) + self.cPosY['commandData'] = ['ray-A'] + self.cPosY['command'] = self.setCollisionPosHprScale + self.cPosY.pack(side=LEFT, expand=0,fill=X, padx=1) + + self.cPosZ = self.createcomponent('origin-Z', (), None, + Floater.Floater, (posInterior,), + text = 'Z', relief = FLAT, + value = origin.getZ(), + label_foreground = 'Blue', + entry_width = 9) + self.cPosZ['commandData'] = ['ray-A'] + self.cPosZ['command'] = self.setCollisionPosHprScale + self.cPosZ.pack(side=LEFT, expand=0,fill=X, padx=1) + posInterior.pack(side=TOP, expand=0,fill=X, padx=3, pady=3) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + group = Pmw.Group(cObjFrame,tag_text='Direction', + tag_font=('MSSansSerif', 10)) + posInterior = Frame(group.interior()) + self.cPosXB = self.createcomponent('direction-X', (), None, + Floater.Floater, (posInterior,), + text = 'X', relief = FLAT, + value = direction.getX(), + label_foreground = 'Red', + entry_width = 9) + self.cPosXB['commandData'] = ['ray-B'] + self.cPosXB['command'] = self.setCollisionPosHprScale + self.cPosXB.pack(side=LEFT,expand=0,fill=X, padx=1) + + self.cPosYB = self.createcomponent('direction-Y', (), None, + Floater.Floater, (posInterior,), + text = 'Y', relief = FLAT, + value = direction.getY(), + label_foreground = '#00A000', + entry_width = 9) + self.cPosYB['commandData'] = ['ray-B'] + self.cPosYB['command'] = self.setCollisionPosHprScale + self.cPosYB.pack(side=LEFT, expand=0,fill=X, padx=1) + + self.cPosZB = self.createcomponent('direction-Z', (), None, + Floater.Floater, (posInterior,), + text = 'Z', relief = FLAT, + value = direction.getZ(), + label_foreground = 'Blue', + entry_width = 9) + self.cPosZB['commandData'] = ['ray-B'] + self.cPosZB['command'] = self.setCollisionPosHprScale + self.cPosZB.pack(side=LEFT, expand=0,fill=X, padx=1) + posInterior.pack(side=TOP, expand=0,fill=X, padx=3, pady=3) + group.pack(side=TOP,fill = X, expand = 0, pady=3) + pass + + collisionGroup.pack(side=TOP,fill = X, expand = 0, pady=3) + + return + + def setCollisionPosHprScale(self, data, dataType): + ################################################################# + # setCollisionPosHprScale(self, data, dataType) + # Well, the reason that we didn't use the same one with other nodePath + # is that each tyoe of collsion objects has its unique properties and way to set value. + # So, they have to be separated from other nodePath + ################################################################# + if dataType == 'sphere-o': + origin = Point3(float(self.cPosX._entry.get()), + float(self.cPosY._entry.get()), + float(self.cPosZ._entry.get())) + self.collisionObj.setCenter(origin) + elif dataType == 'sphere-radius': + self.collisionObj.setRadius(data) + elif dataType == 'segment-A': + pointA = Point3(float(self.cPosX._entry.get()), + float(self.cPosY._entry.get()), + float(self.cPosZ._entry.get())) + self.collisionObj.setPointA(pointA) + elif dataType == 'segment-B': + pointB = Point3(float(self.cPosXB._entry.get()), + float(self.cPosYB._entry.get()), + float(self.cPosZB._entry.get())) + self.collisionObj.setPointB(pointB) + elif dataType == 'ray-A': + pointA = Point3(float(self.cPosX._entry.get()), + float(self.cPosY._entry.get()), + float(self.cPosZ._entry.get())) + self.collisionObj.setOrigin(pointA) + elif dataType == 'ray-B': + pointB = Vec3(float(self.cPosXB._entry.get()), + float(self.cPosYB._entry.get()), + float(self.cPosZB._entry.get())) + self.collisionObj.setDirection(pointB) + return + + ################################################################# + ################################################################# + # Functions below are all call back function + # They will be called when user has manipulated its node on the screen + # The message itself is sent by a task called monitorSelectedNode in the sceneEditor. + ################################################################# + ################################################################# + def trackDataFromSceneCamera(self, pos=Point3(0,0,0), hpr=Vec3(0,0,0), scale=Point3(0,0,0)): + self.posX.set(pos.getX()) + self.posY.set(pos.getY()) + self.posZ.set(pos.getZ()) + self.hprH.set(hpr.getX()) + self.hprP.set(hpr.getY()) + self.hprR.set(hpr.getZ()) + return + + def trackDataFromSceneModel(self, pos=Point3(0,0,0), hpr=Vec3(0,0,0), scale=Point3(0,0,0)): + self.posX.set(pos.getX()) + self.posY.set(pos.getY()) + self.posZ.set(pos.getZ()) + self.hprH.set(hpr.getX()) + self.hprP.set(hpr.getY()) + self.hprR.set(hpr.getZ()) + self.scale.set(scale.getX()) + return + + def trackDataFromSceneActor(self, pos=Point3(0,0,0), hpr=Vec3(0,0,0), scale=Point3(0,0,0)): + self.posX.set(pos.getX()) + self.posY.set(pos.getY()) + self.posZ.set(pos.getZ()) + self.hprH.set(hpr.getX()) + self.hprP.set(hpr.getY()) + self.hprR.set(hpr.getZ()) + self.scale.set(scale.getX()) + return + + def trackDataFromSceneLight(self, pos=Point3(0,0,0), hpr=Vec3(0,0,0), scale=Point3(0,0,0)): + if self.lightNode.type == 'directional': + self.dPosition.set([pos.getX(),pos.getY(),pos.getZ()]) + self.dOrientation.set([hpr.getX(),hpr.getY(),hpr.getZ()]) + pass + + elif self.lightNode.type == 'point': + self.pPosition.set([pos.getX(),pos.getY(),pos.getZ()]) + pass + return + + def trackDataFromSceneDummy(self, pos=Point3(0,0,0), hpr=Vec3(0,0,0), scale=Point3(0,0,0)): + self.posX.set(pos.getX()) + self.posY.set(pos.getY()) + self.posZ.set(pos.getZ()) + self.hprH.set(hpr.getX()) + self.hprP.set(hpr.getY()) + self.hprR.set(hpr.getZ()) + self.scale.set(scale.getX()) + return + + def trackDataFromSceneCollision(self, pos=Point3(0,0,0), hpr=Vec3(0,0,0), scale=Point3(0,0,0)): + self.posX.set(pos.getX()) + self.posY.set(pos.getY()) + self.posZ.set(pos.getZ()) + self.hprH.set(hpr.getX()) + self.hprP.set(hpr.getY()) + self.hprR.set(hpr.getZ()) + self.scale.set(scale.getX()) + return + diff --git a/doc/SceneEditor/quad.py b/doc/SceneEditor/quad.py new file mode 100644 index 0000000000..8bff35bdd0 --- /dev/null +++ b/doc/SceneEditor/quad.py @@ -0,0 +1,674 @@ +######################################################################################################################################### +# This file implements a Quad View for the level editor +# This feature is not yet enabled in the level editor because picking objects in quad view doesnt work +# I have tried to send the picking function in seSelection.py the correct camera and mouse coordinates but something seems to be wron +# There are two classes in this file..the QuadView and the viewPort...there are four instances of viewport, one for each view in QuadView +######################################################################################################################################### + + +from ShowBaseGlobal import * +from intervalGlobal import * +import DirectObject +import math +from MouseWatcher import * + + +class ViewPort: +######################################################################################################################################### +# The ViewPort class has the camera and associated display region set up for actually rendering the four sub-views +# The constructor needs the bounds, window layer, camera, color, projection type, name and scene for the view +######################################################################################################################################### + + def __init__(self,X1,X2,Y1,Y2,layer,cam,background=Vec4(0.3,0.3,0.3,1),projection="perspective",type="top",scene=render): + self.VPType=type + self.VP_X1=X1 + self.VP_Y1=Y1 + self.VP_X2=X2 + self.VP_Y2=Y2 + self.VP_width=self.VP_X2 - self.VP_X1 + self.VP_height=self.VP_Y2 - self.VP_Y1 + + self.the_viewport=layer.makeDisplayRegion(self.VP_X1, self.VP_X2,self.VP_Y1, self.VP_Y2) + self.the_viewport.setCamera(cam) + self.the_viewport.setClearDepthActive(1) + self.the_viewport.setClearColorActive(1) + self.the_viewport.setClearColor(background) + self.cam=cam + + + # Set up the cameras to look in the right place. + if(type=="top"): + self.cam.setP(-90) + self.cam.setZ(-40) + elif(type=="left"): + self.cam.setH(-90) + self.cam.setX(10) + elif(type=="front"): + self.cam.setY(-10) + elif(type=="perspective"): + cam.setY(-100) + #cam.setX(10) + #cam.setZ(-10) + #cam.setH(45) + #cam.setP(-45) + #print "aa" + + + if(projection=="ortho"): + self.lens=OrthographicLens() + self.lens.setAspectRatio((self.VP_X2-self.VP_X1)/(self.VP_Y2-self.VP_Y1)) + self.lens.setFilmSize(self.VP_width*200,self.VP_height*200) + #lens.setFilmOffset((self.VP_X2 + self.VP_X1) * 0.5, (self.VP_Y2 + self.VP_Y1) * 0.5) + self.lens.setNearFar(-1000, 1000) + self.cam.node().setLens(self.lens) + self.cam.node().setScene(scene) + elif(projection=="perspective"): + self.lens=base.cam.node().getLens() + self.lens.setAspectRatio((self.VP_X2-self.VP_X1)/(self.VP_Y2-self.VP_Y1)) + self.cam.node().setLens(self.lens) + self.cam.node().setScene(scene) + + self.the_viewport.setCamera(self.cam) + + def resizeX(self,width_increment): + if(self.VPType=="top" or self.VPType=="left"): + self.the_viewport.setDimensions(self.VP_X1,self.VP_X2+width_increment,self.VP_Y1,self.VP_Y2) + elif(self.VPType=="perspective" or self.VPType=="front"): + self.the_viewport.setDimensions(self.VP_X1+width_increment,self.VP_X2,self.VP_Y1,self.VP_Y2) + + def resizeY(self,height_increment,direction): + if(self.VPType=="left" or self.type=="perspective"): + self.the_viewport.setDimensions(self.VP_X1,self.VP_X2,self.VP_Y1,self.VP_Y2+height_increment) + else: + self.the_viewport.setDimensions(self.VP_X1,self.VP_X2,self.VP_Y1+height_increment,self.VP_Y2) + + def AdjustAspect(self,x,y): + if (y==0): + y=1 + self.lens.setAspectRatio(x/y) + self.cam.node().setLens(self.lens) + + def resize(self,x,y): + + if(self.VPType=="left"): + self.the_viewport.setDimensions(0,x,0,y) + w=abs(x-self.VP_X1) + h=abs(y-self.VP_Y1) + if(h==0): + h=1 + self.lens.setAspectRatio(w/h) + self.cam.node().setLens(self.lens) + if(self.VPType=="top"): + self.the_viewport.setDimensions(0,x,y,1) + w=abs(x-self.VP_X1) + h=abs(self.VP_Y2-y) + if(h==0): + h=1 + self.lens.setAspectRatio(w/h) + self.cam.node().setLens(self.lens) + if(self.VPType=="front"): + self.the_viewport.setDimensions(x,1,y,1) + w=abs(self.VP_X2-x) + h=abs(self.VP_Y2-y) + if(h==0): + h=1 + self.lens.setAspectRatio(w/h) + self.cam.node().setLens(self.lens) + if(self.VPType=="perspective"): + self.the_viewport.setDimensions(x,1,0,y) + w=abs(self.VP_X2-x) + h=abs(y-self.VP_Y1) + if(h==0): + h=1 + self.lens.setAspectRatio(w/h) + self.cam.node().setLens(self.lens) + + def setScene(self,scene): + self.cam.node().setScene(scene) + + def setDR(self,mouseWatcher): + #mouseWatcher.setDisplayRegion(self.the_viewport) + pass + + def setCam(self): + #base.cam=self.cam + #base.cam.node().setLens(self.cam.node().getLens()) + base.camNode=self.cam.node() + #base.camNode.setLens(self.cam.node().getLens()) + #base.camLens=self.cam.node().getLens() + + def getCam(self): + return self.cam + + +class QuadView(DirectObject.DirectObject): +######################################################################################################################################### +# This class sets up four cameras for the scene (ideally we want four instances of render too) +# and then instatiates a ViewPort class for each of them +# +######################################################################################################################################### + + def __init__(self): + + self.PTracker=1 + self.ControlPressed=0 + self.AltPressed=0 + self.PanConstantX=50 + self.PanConstantY=50 + self.ZoomConstant=1 + self.FrontWidth=100 + self.FrontHeight=100 + self.TopWidth=100 + self.TopHeight=100 + self.LeftWidth=100 + self.LeftHeight=100 + + self.MouseButton=0 + self.CurrentQuad=4 + self.HorizontalAxis=0.0 + self.VerticalAxis=0.0 + #base.disableMouse() + self.MouseDragging=0 + self.currX= 0 + self.oldX=self.currX + self.currY= 0 + self.oldY=self.currY + + self.FrontTexture=1 + self.LeftTexture=1 + self.PerspectiveTexture=1 + self.TopTexture=1 + + self.FrontWire=0 + self.LeftWire=0 + self.PerspectiveWire=0 + self.TopWire=0 + + # Keep track of the currently selected window... values are 1-4 for four quadrants of a standard + # Cartesian coordinate system + + # These are the orthographic cameras + # They will be restricted to panning and zooming i.e. no rotation + # Top could be flipped to back, left to right and front to back + self.topCam= render.attachNewNode(Camera('topCam')) + self.frontCam = render.attachNewNode(Camera('frontCam')) + self.leftCam= render.attachNewNode(Camera('leftCam')) + + # This camera will have a trackball control since its perspective + self.perspectiveCam = render.attachNewNode(Camera('perspectiveCam')) + + #self.toplens=OrthographicLens() + #self.leftLens=OrthographicLens() + #self.frontLens=OrthographicLens() + #self.perspectiveLens=base.cam.node().getLens() + + # For now all lenses are same as that of base.cam + #self.topCamLens=OrthographicLens() + #self.frontCamLens= base.cam.node().getLens() + #self.leftCamLens= base.cam.node().getLens() + #self.perspectiveCamLens= base.cam.node().getLens() + + # Manipulate lenses here if need be + #self.topCamLens.setFilmSize(250) + + # Set the Lenses + #self.topCam.node().setLens(self.topCamLens) + #self.frontCam.node().setLens(self.frontCamLens) + #self.leftCam.node().setLens(self.leftCamLens) + #self.perspectiveCam.node().setLens(self.perspectiveCamLens) + + #self.badwiz = loader.loadModel('badwizard1') + #self.badwiz.reparentTo(render) + + # Create four separate display regions for the quad view. + # These will overlap the main display region + # To stack these overlapping DisplayRegions, we need a new layer. If + # they didn't overlap, we could put them in the same layer. + + self.newLayer = base.win.getChannel(0).makeLayer() + + self.PerspectiveScene=NodePath('PerspectiveScene') + self.FrontScene=NodePath('FrontScene') + self.TopScene=NodePath('TopScene') + self.LeftScene=NodePath('LeftScene') + self.SceneParent=NodePath('SceneParent') + + #self.PerspectiveScene=render.copyTo(self.SceneParent) + #self.FrontScene=render.copyTo(self.SceneParent) + #self.TopScene=render.copyTo(self.SceneParent) + #self.LeftScene=render.copyTo(self.SceneParent) + + self.PerspectiveScene=render + self.FrontScene=render + self.TopScene=render + self.LeftScene=render + + #self.PerspectiveScene.reparentTo(self.SceneParent) + #self.FrontScene.reparentTo(self.SceneParent) + #self.TopScene.reparentTo(self.SceneParent) + #self.LeftScene.reparentTo(self.SceneParent) + + self.Perspective=ViewPort(0.5,1.0,0.0,0.5,self.newLayer,self.perspectiveCam,Vec4(0.75,0.75,0.75,1),"perspective","perspective",self.PerspectiveScene) + self.Top=ViewPort(0.0,0.5,0.5,1.0,self.newLayer,self.topCam,Vec4(0.80,0.80,0.80,1),"ortho","top",self.TopScene) + self.Left=ViewPort(0.0,0.5,0.0,0.5,self.newLayer,self.leftCam,Vec4(0.85,0.85,0.85,1),"ortho","left",self.LeftScene) + self.Front=ViewPort(0.5,1.0,0.5,1.0,self.newLayer,self.frontCam,Vec4(0.85,0.85,0.85,1),"ortho","front",self.FrontScene) + #self.Perspective=None + #self.Top=None + #self.Front=None + #self.Left=None + + #self.raycaster = RayCaster( camera ) + #self.lastPickPoint = None + + #base.useTrackball() + + #self.dataRoot = NodePath('dataRoot') + # Cache the node so we do not ask for it every frame + #self.dataRootNode = self.dataRoot.node() + #self.dataUnused = NodePath('dataUnused') + #self.mak=None + #self.mak = self.dataRoot.attachNewNode(MouseAndKeyboard(base.win, 0, 'mak')) + #self.mak.node().setSource(base.win, 0) + self.mouseWatcherNode = MouseWatcher('mouseWatcher') + + self.mouseWatcher = base.mak.attachNewNode(self.mouseWatcherNode) + #self.Perspective.setDR(self.mouseWatcherNode) + + self.buttonThrower = self.mouseWatcher.attachNewNode(ButtonThrower('buttons')) + + + #ddr=DisplayRegionContext(self.Perspective.getCam()) + #base.setMouseOnNode(self.smiley.node()) # Let Mouse Control Perspective View for now + #base.enableSoftwareMousePointer() + + # Message Handlers + self.accept("a",self.setLeft) + self.accept("q",self.setTop) + self.accept("w",self.setFront) + self.accept("s",self.setPerspective) + self.accept("mouse1",self.MouseTell,[1]) + self.accept("mouse2",self.MouseTell,[2]) + self.accept("mouse3",self.MouseTell,[3]) + self.accept("mouse1-up",self.MouseTellUp,[4]) + self.accept("mouse2-up",self.MouseTellUp,[5]) + self.accept("mouse3-up",self.MouseTellUp,[6]) + self.accept("mouse2-scroll",self.resizedr) + self.accept("r",self.resizedr) + self.accept("alt",self.AltHandler) + self.accept("alt-up",self.AltUpHandler) + self.accept("alt-mouse1",self.AltDown) + self.accept("alt-mouse1-up",self.AltUp) + self.accept("control-mouse1",self.CtlDown) + self.accept("control-mouse1-up",self.CtlUp) + + # Methods + + #def setLastPickPoint( self ): + # mouseX, mouseY = self.mouseWatcherNode.getMouseX(), self.mouseWatcherNode.getMouseY() + # self.lastPickPoint = self.raycaster.pick( mouseX, mouseY ) + # print self.lastPickPoint + + def AltDown(self): + self.AltPressed=1 + + + def AltUp(self): + self.AltPressed=0 + + + def CtlDown(self): + self.ControlPressed=1 + + + def CtlUp(self): + self.ControlPressed=0 + + + def ToggleWire(self): + if (self.CurrentQuad==1): # Front View + if(self.FrontWire): # Wireframe is On so turn it off + self.FrontScene.setRenderModeWireframe(100); + self.FrontScene.setTwoSided(1); + self.FrontScene.setTextureOff(100) + self.FrontWire=0 + else: + self.FrontScene.clearRenderMode() + #self.FrontScene.setTwoSided(not self.backfaceCullingEnabled) + if(self.FrontTexture): + self.FrontScene.clearTexture() + self.FrontWire=1 + elif (self.CurrentQuad==2): # Front View + if(self.TopWire): # Wireframe is On so turn it off + self.TopScene.setRenderModeWireframe(100); + self.TopScene.setTwoSided(1); + self.TopScene.setTextureOff(100) + self.TopWire=0 + else: + self.TopScene.clearRenderMode() + #self.TopScene.setTwoSided(not self.backfaceCullingEnabled) + if(self.TopTexture): + self.TopScene.clearTexture() + self.TopWire=1 + elif (self.CurrentQuad==3): # Front View + if(self.LeftWire): # Wireframe is On so turn it off + self.LeftScene.setRenderModeWireframe(100); + self.LeftScene.setTwoSided(1); + self.LeftScene.setTextureOff(100) + self.LeftWire=0 + else: + self.LeftScene.clearRenderMode() + #self.LeftScene.setTwoSided(not self.backfaceCullingEnabled) + if(self.LeftTexture): + self.LeftScene.clearTexture() + self.LeftWire=1 + elif (self.CurrentQuad==4): # Front View + if(self.PerspectiveWire): # Wireframe is On so turn it off + self.PerspectiveScene.setRenderModeWireframe(100); + self.PerspectiveScene.setTwoSided(1); + self.PerspectiveScene.setTextureOff(100) + self.PerspectiveWire=0 + else: + self.PerspectiveScene.clearRenderMode() + #self.PerspectiveScene.setTwoSided(not self.backfaceCullingEnabled) + if(self.PerspectiveTexture): + self.PerspectiveScene.clearTexture() + self.PerspectiveWire=1 + + + def ToggleTexture(self): + if (self.CurrentQuad==1): # Front View + if(self.FrontTexture): # Texture is on so turn it off + self.FrontScene.setTextureOff(100) + self.FrontTexture=0; + else: + self.FrontScene.clearTexture() + self.FrontTexture=1 + elif (self.CurrentQuad==2): # Top View + if(self.TopTexture): # Texture is on so turn it off + self.TopScene.setTextureOff(100) + self.TopTexture=0; + else: + self.TopScene.clearTexture() + self.TopTexture=1 + elif (self.CurrentQuad==3): # Left View + if(self.LeftTexture): # Texture is on so turn it off + self.LeftScene.setTextureOff(100) + self.LeftTexture=0; + else: + self.LeftScene.clearTexture() + self.LeftTexture=1 + elif (self.CurrentQuad==4): # Perspective View + if(self.PerspectiveTexture): # Texture is on so turn it off + self.PerspectiveScene.setTextureOff(100) + self.PerspectiveTexture=0; + else: + self.PerspectiveScene.clearTexture() + self.PerspectiveTexture=1 + + + + def reparenter(self): + #self.FrontScene.reparentTo(render) + #self.Front.setScene(render) + #self.Top.setScene(render) + #self.Left.setScene(render) + #self.Perspective.setScene(render) + pass + + def unparenter(self): + #self.PerspectiveScene=render.copyTo(render) + #self.FrontScene=render.copyTo(render) + #self.TopScene=render.copyTo(render) + #self.LeftScene=render.copyTo(render) + #self.SceneParent.reparentTo(render) + #self.PerspectiveScene.reparentTo(self.SceneParent) + #self.FrontScene.reparentTo(self.SceneParent) + #self.TopScene.reparentTo(self.SceneParent) + #self.LeftScene.reparentTo(self.SceneParent) + pass + + def AltHandler(self): + self.oldX=self.mouseWatcherNode.getMouseX() + if(self.oldX<-1 or self.oldX>1): + return + self.oldY=self.mouseWatcherNode.getMouseY() + if(self.oldY<-1 or self.oldY>1): + return + taskMgr.add(self.DragAction,'DragAction') + + def AltUpHandler(self): + taskMgr.remove('DragAction') + + def gridtoggle(self): + #grid=DirectGrid() + #grid.enable() + pass + + def resizedr(self,x,y): + #print "X: " + str(x) + " Y: " + str(y) + x=(x+1)/2.0 + y=(y+1)/2.0 + self.Perspective.resize(x,y) + self.Top.resize(x,y) + self.Front.resize(x,y) + self.Left.resize(x,y) + + def setAppropriateViewPort(self,x,y): + #print "SET APPROPRIATE:" + str(x) + " " + str(y) + if(x1): + return + + self.oldY=self.mouseWatcherNode.getMouseY() + if(self.oldY<-1 or self.oldY>1): + return + self.Mouse_Dragging=1 + taskMgr.add(self.DragAction,'DragAction') + + def MouseTellUp(self,buttoncode): + #self.MouseButton=0 + self.PanConstantX= 50 + self.PanConstantY= 50 + self.ZoomConstant=1 + taskMgr.remove('DragAction') + self.Mouse_Draggin=0 + #print "Mouse Up" + + + def Max_Style_Mouse_View(self,buttoncode): + pass + + def ChangeBaseDR(self): + dr=base.win.getDisplayRegion(0) + if(self.CurrentQuad==1): #Front + dr.setDimensions(0.5,1,0.5,1) + elif(self.CurrentQuad==2): #Top + dr.setDimensions(0,0.5,0.5,1) + elif(self.CurrentQuad==3): #Left + dr.setDimensions(0,0.5,0,0.5) + elif(self.CurrentQuad==4): #Perspective + dr.setDimensions(0.5,1,0,0.5) + + def setLeft(self): + print "LEFT" + self.CurrentQuad=3 + self.ChangeBaseDR() + self.Left.setCam() + #self.Left.setDR(self.mouseWatcherNode) + + def setTop(self): + print "TOP" + self.CurrentQuad=2 + self.ChangeBaseDR() + self.Top.setCam() + #self.Top.setDR(self.mouseWatcherNode) + + def setPerspective(self): + print "PERSPECTIVE" + self.CurrentQuad=4 + self.ChangeBaseDR() + self.Perspective.setCam() + #self.Perspective.setDR(self.mouseWatcherNode) + + def setFront(self): + print "FRONT" + self.CurrentQuad=1 + self.ChangeBaseDR() + self.Front.setCam() + #self.Front.setDR(self.mouseWatcherNode) + + + def DragAction(self,task): + #if(self.MouseDragging==1): + self.currX= self.mouseWatcherNode.getMouseX() + if(self.currX<-1 or self.currX>1): + return + self.currY= self.mouseWatcherNode.getMouseY() + if(self.currY<-1 or self.currY>1): + return + + + self.diffX=self.currX-self.oldX + self.diffY=self.currY-self.oldY + + if(self.ControlPressed): # Change Size of the ViewPorts + #if(base.getControl()): + self.VerticalAxis=self.currX + self.HorizontalAxis=self.currY + if(self.HorizontalAxis<-1 or self.HorizontalAxis>1 or self.VerticalAxis<-1 or self.VerticalAxis>1): + return + self.resizedr(self.VerticalAxis,self.HorizontalAxis) + + #if(self.AltPressed): # View Camera Transforms -> Maya style + elif(1): + #print "ALTPRESSED" + if(self.PanConstantX<4096): + self.PanConstantX= self.PanConstantX * 2 + self.PanConstantY= self.PanConstantY * 2 + self.ZoomConstant= self.ZoomConstant + 50 + if(self.MouseButton==1): # TrackBall rotation only for Perspective View + if(self.CurrentQuad==4): + pass + elif(self.MouseButton==2): # Do Panning + if(self.CurrentQuad==1): # Y and Z values change meanings for different cameras + self.MoveCamera(-self.diffX*self.PanConstantX,0,-self.diffY*self.PanConstantY,self.CurrentQuad) + elif(self.CurrentQuad==2): + self.MoveCamera(-self.diffX*self.PanConstantX,-self.diffY*self.PanConstantY,0,self.CurrentQuad) + elif(self.CurrentQuad==3): + self.MoveCamera(0,self.diffX*self.PanConstantX,-self.diffY*self.PanConstantY,self.CurrentQuad) + elif(self.CurrentQuad==4): + pass + elif(self.MouseButton==3): # Do Zoom + if(self.CurrentQuad==1): # Y and Z values change meanings for different cameras + #lens = OrthographicLens() + #lens.setFilmSize(l,self.VP_height*200) + #lens.setFilmOffset((self.VP_X2 + self.VP_X1) * 0.5, (self.VP_Y2 + self.VP_Y1) * 0.5) + #lens.setNearFar(-1000, 1000) + self.FrontWidth= self.FrontWidth + self.diffX + self.FrontHeight= self.FrontHeight + self.diffX + self.FrontWidth= self.FrontWidth + self.diffY + self.FrontHeight= self.FrontHeight + self.diffY + + if(self.FrontWidth<=0): + Frontwidth=1 + if(self.FrontHeight<=0): + FrontHeight=1 + self.frontCam.node().getLens().setFilmSize(self.FrontWidth,self.FrontHeight) + self.resizedr(self.VerticalAxis,self.HorizontalAxis) + elif(self.CurrentQuad==2): + self.TopWidth= self.TopWidth + self.diffX + self.TopHeight= self.TopHeight + self.diffX + self.TopWidth= self.TopWidth + self.diffY + self.TopHeight= self.TopHeight + self.diffY + self.topCam.node().getLens().setFilmSize(self.TopWidth,self.TopHeight) + self.resizedr(self.VerticalAxis,self.HorizontalAxis) + elif(self.CurrentQuad==3): + self.LeftWidth= self.LeftWidth + self.diffX + self.LeftHeight= self.LeftHeight + self.diffX + self.LeftWidth= self.LeftWidth + self.diffY + self.LeftHeight= self.LeftHeight + self.diffY + self.leftCam.node().getLens().setFilmSize(self.LeftWidth,self.LeftHeight) + self.resizedr(self.VerticalAxis,self.HorizontalAxis) + elif(self.CurrentQuad==4): + pass + else: + pass + + self.oldX=self.currX + self.oldY=self.currY + return Task.cont + + + + def MoveCamera(self,X_amt,Y_amt,Z_amt,quad): + if(quad==1): + self.frontCam.setPos(self.frontCam.getX()+X_amt,self.frontCam.getY()+Y_amt,self.frontCam.getZ()+Z_amt) + elif(quad==2): + self.topCam.setPos(self.topCam.getX()+X_amt,self.topCam.getY()+Y_amt,self.topCam.getZ()+Z_amt) + elif(quad==3): + self.leftCam.setPos(self.leftCam.getX()+X_amt,self.leftCam.getY()+Y_amt,self.leftCam.getZ()+Z_amt) + elif(quad==4): + self.perspectiveCam.setPos(self.perspectiveCam.getX()+X_amt,self.perspectiveCam.getY()+Y_amt,self.perspectiveCam.getZ()+Z_amt) + + + +#View=QuadView() +#run() \ No newline at end of file diff --git a/doc/SceneEditor/sceneEditor.py b/doc/SceneEditor/sceneEditor.py new file mode 100644 index 0000000000..4b39c8606f --- /dev/null +++ b/doc/SceneEditor/sceneEditor.py @@ -0,0 +1,1679 @@ +from ShowBaseGlobal import* +from TkGlobal import* +from tkFileDialog import * +from DirectGlobals import * +from AppShell import* + +from SideWindow import* +from duplicateWindow import* +from lightingPanel import * +from seMopathRecorder import * +from seSession import * +from quad import * +from sePlacer import * +from seFileSaver import * +from propertyWindow import * +import seParticlePanel +from collisionWindow import * +from DirectGui import * +from MetadataPanel import * +from seBlendAnimPanel import * +from controllerWindow import * +from AlignTool import * + + + +import os +import string +import Dial +import Floater +import Slider +import Actor +import seAnimPanel +import Task +import math + + +################################################################# +# All scene and windows object will be stored in here. +# So, any event which will or need to change contents +# should be wirtten in here or imported into here! +################################################################# +from dataHolder import* ## Use this thing to Save/load data. +AllScene = dataHolder() + + + +class myLevelEditor(AppShell): + ## overridden the basic app info ## + appname = 'Scene Editor - New Scene' + appversion = '1.0' + copyright = ('Copyright 2004 E.T.C. Carnegie Mellon U.' + + ' All Rights Reserved') + contactname = 'Jesse Schell, Shalin Shodhan & YiHong Lin' + contactphone = '(412) 268-5791' + contactemail = 'etc-panda3d@lists.andrew.cmu.edu' + frameWidth = 1024 + frameHeight = 80 + frameIniPosX = 0 + frameIniPosY = 0 + usecommandarea = 0 + usestatusarea = 0 + padx = 5 + pady = 5 + + sideWindowCount = 0 + + ## Basic World default setting (For side window) + worldColor = [0,0,0,0] + lightEnable = 1 + ParticleEnable = 1 + basedriveEnable = 0 + collision = 1 + backface = 0 + texture = 1 + wireframe = 0 + grid = 0 + widgetVis = 0 + enableAutoCamera = 1 + + enableControl = False + controlType = 'Keyboard' + keyboardMapDict = {} + keyboardSpeedDict = {} + + Scene=None + isSelect = False + nodeSelected = None + undoDic = {} + redoDic = {} + animPanel = {} + animBlendPanel = {} + propertyWindow = {} + CurrentFileName=None #Holds the current scene file name + CurrentDirName=None # Holds the current file name without extension which is the path where file's data gets saved + Dirty=0 # Keeps track of whether there are any modifications that should be saved + + + + def __init__(self, parent = None, **kw): + base.setBackgroundColor(0,0,0) + self.parent = parent + ## Check TkTool is activated! ## + self.wantTK = config.GetBool('want-tk', 0) + if self.wantTK: + pass + else: + taskMgr.remove('tkloop') + spawnTkLoop() + ## Set up window frame + INITOPT = Pmw.INITOPT + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + + AppShell.__init__(self, parent) + self.parent.geometry('%dx%d+%d+%d' % (self.frameWidth, self.frameHeight,self.frameIniPosX,self.frameIniPosY)) + + + ###### Put th directLabel on the screen to show the selected object Data + self.posLabel = DirectLabel( + relief = None, + pos = (-1.3, 0, 0.90), + text = "Position : X: 00.00 Y: 00.00 Z: 00.00", + color = Vec4(1, 1, 1, 1), + text_scale = 0.05, + text_align = TextNode.ALeft + ) + self.hprLabel = DirectLabel( + relief = None, + pos = (-1.3 , 0, 0.80), + text = "Orientation: H: 00.00 P: 00.00 R: 00.00", + color = Vec4(1, 1, 1, 1), + text_scale = 0.05, + text_align = TextNode.ALeft + ) + self.scaleLabel = DirectLabel( + relief = None, + pos = (-1.3, 0, 0.70), + text = "Scale : X: 00.00 Y: 00.00 Z: 00.00", + color = Vec4(1, 1, 1, 1), + text_scale = 0.05, + text_align = TextNode.ALeft + ) + + + self.initialiseoptions(myLevelEditor) + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + ######### Set the event handler ########## + self.dataFlowEvents = [ + ## Event from Side Window + ['SW_lightToggle',self.lightToggle], + ['SW_collisionToggle',AllScene.toggleCollisionVisable], + ['SW_particleToggle',self.toggleParticleVisable], + ['SW_close',self.sideWindowClose], + ## From Duplication Window + ['DW_duplicating',self.duplicationObj], + ## From Animation Panel + ['AW_AnimationLoad',self.animationLoader], + ['AW_removeAnim',self.animationRemove], + ['AW_close',self.animPanelClose], + ## From Blending Animation Window + ['BAW_saveBlendAnim',self.animBlendPanelSave], + ['BAW_removeBlendAnim',self.animBlendPanelRemove], + ['BAW_renameBlendAnim',self.animBlendPanelRename], + ['BAW_close',self.animBlendPanelClose], + ## From Lighting Panel + ['LP_selectLight', self.lightSelect], + ['LP_addLight',self.addLight], + ['LP_rename',self.lightRename], + ['LP_removeLight',self.removeLight], + ['LP_close',self.lightingPanelClose], + ## From MotionPath Panel + ['mPath_bindPathToNode',AllScene.bindCurveToNode], + ['mPath_requestCurveList', self.requestCurveList], + ['mPath_close', self.mopathClosed], + ## From Property Window + ['PW_removeCurveFromNode', AllScene.removeCurveFromNode], + ['PW_removeAnimFromNode', AllScene.removeAnimation], + ['PW_toggleLight', AllScene.toggleLightNode], + ['PW_close', self.closePropertyWindow], + ## From collisionWindow + ['CW_addCollisionObj', AllScene.addCollisionObject], + ## From AlignWindow + ['ALW_close', self.closeAlignPanel], + ['ALW_align', self.alignObject], + ## From controllerWindow + ['ControlW_close', self.closeInputPanel], + ['ControlW_require', self.requestObjFromControlW], + ['ControlW_controlSetting', self.setControlSet], + ['ControlW_controlEnable', self.startControl], + ['ControlW_controlDisable', self.stopControl], + ['ControlW_saveSetting', AllScene.saveControlSetting], + ## From Placer + ['Placer_close', self.closePlacerPanel], + ## From Particle Panel + ['ParticlePanle_close', self.closeParticlePanel], + ## From SEditor object which is a altered DirectSession + ['SEditor-ToggleWidgetVis',self.toggleWidgetVis], + ['SEditor-ToggleBackface',self.toggleBackface], + ['SEditor-ToggleTexture',self.toggleTexture], + ['SEditor-ToggleWireframe',self.toggleWireframe], + ['ParticlePanel_Added_Effect',self.addParticleEffect], + ] + + + ################################# + ### Collision detection + ################################# + self.cTrav = CollisionTraverser() + base.cTrav = self.cTrav + + for event in self.dataFlowEvents: + self.accept(event[0], event[1], extraArgs = event[2:]) + + self.actionEvents = [ + # Scene graph explorer functions + ['SGE_changeName', self.changeName], + ['SGE_Properties', self.openPropertyPanel], + ['SGE_Duplicate', self.duplicate], + ['SGE_Remove', self.remove], + ['SGE_Add Dummy', self.addDummyNode], + ['SGE_Add Collision Object', self.addCollisionObj], + ['SGE_Metadata', self.openMetadataPanel], + ['SGE_Set as Reparent Target', self.setAsReparentTarget], + ['SGE_Reparent to Target', self.reparentToNode], + ['SGE_Animation Panel', self.openAnimPanel], + ['SGE_Blend Animation Panel', self.openBlendAnimPanel], + ['SGE_MoPath Panel', self.openMoPathPanel], + ['SGE_Align Tool', self.openAlignPanel], + ['SGE_Flash', self.flash], + ['SGE_madeSelection', self.selectNode], + ['select',self.selectNode], + ['deselect', self.deSelectNode], + ['se_selectedNodePath',self.selectFromScene], + ['se_deselectedAll',self.deselectFromScene], + ] + ''' All messages starting with "SGE_" are generated in seSceneGraphExplorer''' + + for event in self.actionEvents: + self.accept(event[0], event[1], extraArgs = event[2:]) + + camera.toggleVis() + self.selectNode(base.camera) ## Initially, we select camera as the first node... + + def appInit(self): + ################################################################# + # appInit(self) + # Initialize the application. + # This function will be called when you call AppShell's constructor + ################################################################# + + ### Create SceneEditor Ver. DirectSession + self.seSession = SeSession() + self.seSession.enable() + SEditor.camera.setPos(0,-50,10) + + self.placer=None + self.MopathPanel = None + self.alignPanelDict = {} + #self.quadview=QuadView() + + + self.lightingPanel = None + self.controllerPanel = None + self.particlePanel = None + + ### Create Side Window + self.sideWindow = sideWindow(worldColor = self.worldColor, + lightEnable = self.lightEnable, + ParticleEnable = self.ParticleEnable, + basedriveEnable = self.basedriveEnable, + collision = self.collision, + backface = self.backface, + texture = self.texture, + wireframe = self.wireframe, + grid = self.grid, + widgetVis = self.widgetVis, + enableAutoCamera = self.enableAutoCamera) + self.sideWindowCount = 1 + self.sideWindow.selectPage() + messenger.send('SGE_Update Explorer',[render]) ## Update the Scene Graph + + pass + + def createInterface(self): + # The interior of the toplevel panel + interior = self.interior() + + ####################################################### + ### Creating the Buttons in the window frame + ####################################################### + buttonFrame = Frame(interior) + self.image=[] + self.image.append(PhotoImage(file='new.gif'))#0 + self.image.append(PhotoImage(file='open.gif'))#1 + self.image.append(PhotoImage(file='save.gif'))#2 + self.image.append(PhotoImage(file='model.gif'))#3 + self.image.append(PhotoImage(file='actor.gif'))#4 + self.image.append(PhotoImage(file='placer.gif'))#5 + self.image.append(PhotoImage(file='mopath.gif'))#6 + self.image.append(PhotoImage(file='lights.gif'))#7 + self.image.append(PhotoImage(file='particles.gif'))#8 + self.image.append(PhotoImage(file='control.gif')) + self.image.append(PhotoImage(file='help.gif'))#9 + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + self.image.append(PhotoImage(file='blank.gif')) + + + i = 0 + for element in self.image: + i += 1 + button = Button(buttonFrame, image = element, command=lambda n=i : self.buttonPushed(n)) + button.pack(fill=X, side = LEFT) + + + buttonFrame.pack(fill=X, side=LEFT,expand=True) + + + def buttonPushed(self, buttonIndex): + ################################################################# + # buttonPushed(self, buttonNum) + # This function will handle all button events from top level window + # Take the button index as a reference to sence which button has been pushed. + ################################################################# + #### + #### Change here to process the button event further. + #### + if buttonIndex==1: # New Scene + self.newScene() + return + elif buttonIndex==2: # Open Scene + self.openScene() + return + elif buttonIndex==3: # Save Scene + self.saveScene() + return + elif buttonIndex==4: # Load Model + self.loadModel() + return + elif buttonIndex==5: # Load Actor + self.loadActor() + return + elif buttonIndex==6: # Open Placer + self.openPlacerPanel() + return + elif buttonIndex==7: # Open Mopath Panel + self.openMoPathPanel() + return + elif buttonIndex==8: # Open Lighting Panel + self.openLightingPanel() + return + elif buttonIndex==9: # Open Particle Panel + self.openParticlePanel() + return + elif buttonIndex==10: + self.openInputPanel() + return + elif buttonIndex==11: # Help + self.showAbout() + return + elif buttonIndex==12: + print "You haven't defined the function for this Button, Number %d."%buttonIndex + return + elif buttonIndex==13: + print "You haven't defined the function for this Button, Number %d."%buttonIndex + return + elif buttonIndex==14: + print "You haven't defined the function for this Button, Number %d."%buttonIndex + return + elif buttonIndex==15: + print "You haven't defined the function for this Button, Number %d."%buttonIndex + return + elif buttonIndex==16: + print "Your scene will be eliminated within five seconds, Save your world!!!, Number %d."%buttonIndex + return + elif buttonIndex==17: + print "You haven't defined the function for this Button, Number %d."%buttonIndex + return + elif buttonIndex==18: + print "You haven't defined the function for this Button, Number %d."%buttonIndex + return + elif buttonIndex==19: + print "You haven't defined the function for this Button, Number %d."%buttonIndex + return + elif buttonIndex==20: + print "You haven't defined the function for this Button, Number %d."%buttonIndex + return + + return + + def createMenuBar(self): + # Creates default menus. Can be overridden or simply augmented + # Using button Add below + self.menuBar.addmenuitem('Help', 'command', + 'Get information on application', + label='About...', command=self.showAbout) + ## Creat stuff inside the "File" + self.menuBar.addmenuitem('File', 'command', 'Creat New Scene', + label='New Scene', + command=self.newScene) + + self.menuBar.addmenuitem('File', 'command', 'Open a Scene', + label='Open Scene', + command=self.openScene) + + self.menuBar.addmenuitem('File', 'command', 'Save a Scene', + label='Save Scene', + command=self.saveScene) + + self.menuBar.addmenuitem('File', 'command', 'Save Scene as...', + label='Save as...', + command=self.saveAsScene) + + self.menuBar.addmenuitem('File', 'separator') + + self.menuBar.addmenuitem('File', 'command', 'Load Model', + label='Load Model', + command=self.loadModel) + + self.menuBar.addmenuitem('File', 'command', 'Load Actor', + label='Load Actor', + command=self.loadActor) + + self.menuBar.addmenuitem('File', 'separator') + + self.menuBar.addmenuitem('File', 'command', 'Import a Scene', + label='Import...', + command=self.importScene) + + self.menuBar.addmenuitem('File', 'separator') + + self.menuBar.addmenuitem('File', 'command', 'Quit this application', + label='Exit', + command=self.quit) + + ## Creat "Edit" on the menu and its stuff + self.menuBar.addmenu('Edit', 'Editting tools') + self.menuBar.addmenuitem('Edit', 'command', 'Un-do', + label='Undo...', + command=self.unDo) + self.menuBar.addmenuitem('Edit', 'command', 'Re-do', + label='Redo...', + command=self.reDo) + self.menuBar.addmenuitem('Edit', 'separator') + self.menuBar.addmenuitem('Edit', 'command', 'Deselect nodepath', + label='Deselect', + command=self.deSelectNode) + self.menuBar.addmenuitem('Edit', 'separator') + self.menuBar.addmenuitem('Edit', 'command', 'Add a Dummy', + label='Add Dummy', + command=self.addDummy) + self.menuBar.addmenuitem('Edit', 'command', 'Duplicate nodepath', + label='Duplicate', + command=self.duplicateNode) + self.menuBar.addmenuitem('Edit', 'command', 'Remove the nodepath', + label='Remove', + command=self.removeNode) + self.menuBar.addmenuitem('Edit', 'command', 'Show the object properties', + label='Object Properties', + command=self.showObjProp) + self.menuBar.addmenuitem('Edit', 'separator') + self.menuBar.addmenuitem('Edit', 'command', 'Show the Camera setting', + label='Camera Setting', + command=self.showCameraSetting) + self.menuBar.addmenuitem('Edit', 'command', 'Render setting', + label='Render Setting', + command=self.showRenderSetting) + + ## Creat "Panel" on the menu and its stuff + self.menuBar.addmenu('Panel', 'Panel tools') + self.menuBar.addmenuitem('Panel', 'command', 'Open Side Window', + label='Side Window', + command=self.openSideWindow) + self.menuBar.addmenuitem('Panel', 'command', 'Placer Panel', + label='Placer Panel', + command=self.openPlacerPanel) + self.menuBar.addmenuitem('Panel', 'command', 'Animation Panel', + label='Animation Panel', + command=self.openAnimationPanel) + self.menuBar.addmenuitem('Panel', 'command', 'Motion Path Panel', + label='Mopath Panel', + command=self.openMopathPanel) + self.menuBar.addmenuitem('Panel', 'command', 'Lighting Panel', + label='Lighting Panel', + command=self.openLightingPanel) + self.menuBar.addmenuitem('Panel', 'command', 'Particle Panel', + label='Particle Panel', + command=self.openParticlePanel) + self.menuBar.addmenuitem('Panel', 'separator') + self.menuBar.addmenuitem('Panel', 'command', 'Input control Panel', + label='Input device panel', + command=self.openInputPanel) + + self.menuBar.pack(fill=X, side = LEFT) + + ## get "Menu" items in order to control the entry status + self.menuFile = self.menuBar.component('File-menu') + self.menuEdit = self.menuBar.component('Edit-menu') + self.menuPanel = self.menuBar.component('Panel-menu') + + ## Disable entries when user doesn't select anything + if not self.isSelect: + self.menuEdit.entryconfig('Deselect', state=DISABLED) + self.menuEdit.entryconfig('Add Dummy', state=DISABLED) + self.menuEdit.entryconfig('Duplicate', state=DISABLED) + self.menuEdit.entryconfig('Remove', state=DISABLED) + self.menuEdit.entryconfig('Object Properties', state=DISABLED) + self.menuPanel.entryconfig('Animation Panel', state=DISABLED) + self.menuPanel.entryconfig('Side Window', state=DISABLED) + + + def onDestroy(self, event): + ################################################################# + # If you have open any thing, please rewrite here! + ################################################################# + if taskMgr.hasTaskNamed('seMonitorSelectedNode'): + taskMgr.remove('seMonitorSelectedNode') + pass + + def closeAllSubWindows(self): + ################################################################# + # closeAllSubWindows(self) + # except side window. this function will close all sub window if there is any. + ################################################################# + if self.lightingPanel != None: + self.lightingPanel.quit() + if self.placer != None: + self.placer.quit() + if self.MopathPanel != None: + self.MopathPanel.quit() + + if self.particlePanel != None: + self.particlePanel.quit() + + if self.controllerPanel != None: + self.controllerPanel.quit() + + list = self.animPanel.keys() + for index in list: + self.animPanel[index].quit() + list = self.animBlendPanel.keys() + for index in list: + self.animBlendPanel[index].quit() + list = self.propertyWindow.keys() + for index in list: + self.propertyWindow[index].quit() + list = self.alignPanelDict.keys() + for index in list: + self.alignPanelDict[index].quit() + self.animPanel.clear() + self.animBlendPanel.clear() + self.propertyWindow.clear() + self.alignPanelDict.clear() + + return + + ## Processing message events + + def makeDirty(self): + self.Dirty=1 + + def removeLight(self, lightNode): + ################################################################# + # removeLight(self, lightNode) + # This function will be called when user try to remove the light from lightingPanel + # (by sending out the message) + # So, in here we will call dataHolder(AllScene) to remove the light + # and return a list contains the newest data of lights in he scene. + # Then, this function will reset the lighting list in the lightingPanel + ################################################################# + list = AllScene.removeObj(lightNode) + if self.lightingPanel != None: + self.lightingPanel.updateList(list) + return + + def lightRename(self,oName, nName): + ################################################################# + # lightRename(self,oName, nName) + # This function will be called when user try to rename the light from lightingPanel + # (by sending out the message) + # So, in here we will call dataHolder(AllScene) to rename the light + # and return a list contains the newest data of lights in he scene. + # Then, this function will reset the lighting list in the lightingPanel + ################################################################# + list, lightNode = AllScene.rename(oName, nName) + if self.lightingPanel != None: + self.lightingPanel.updateList(list,lightNode) + return + + def lightSelect(self,lightName): + ################################################################# + # lightSelect(self,lightName) + # This function will be called when user try to select the light from lightingPanel + # (by sending out the message) + # So, in here we will call dataHolder(AllScene) to get the target light node + # Then, this function will put this light node back into lighting + # panel and update the data on the panel. + ################################################################# + lightNode = AllScene.getLightNode(lightName) + if self.lightingPanel != None: + self.lightingPanel.updateDisplay(lightNode) + return + + def addLight(self, type): + ################################################################# + # addLight(self, type) + # This function will be called when user try to add a light from lightingPanel + # (by sending out the message) + # So, in here we will call dataHolder(AllScene) to create a default light node + # by the type that user assigned. + # Then, this function will put this light node back into lighting + # panel with the newest lighting list and update the data on the panel. + ################################################################# + list, lightNode = AllScene.createLight(type = type) + if self.lightingPanel != None: + self.lightingPanel.updateList(list,lightNode) + self.makeDirty() + return + + def lightingPanelClose(self): + ################################################################# + # lightingPanelClose(self) + # This function will be called when user try to close the lighting panel + # This function will re-config the state of the lighting panel button on the top screen + # And it will set the self.lightingPanel to None + ################################################################# + self.menuPanel.entryconfig('Lighting Panel', state=NORMAL) + self.lightingPanel = None + return + + def openPropertyPanel(self, nodePath = None): + ################################################################# + # openPropertyPanel(self, nodePath = None) + # This function will be called when user try to open a property window + # for one specific node in the scene. + # Here we will call dataHolder to get the basic properties + # we would like to let user to see and cange. + # And then we pass those information into propertyWindow + ################################################################# + type, info = AllScene.getInfoOfThisNode(nodePath) + name = nodePath.getName() + if not self.propertyWindow.has_key(name): + self.propertyWindow[name] = propertyWindow(nodePath, type,info ) + pass + + def closePropertyWindow(self, name): + if self.propertyWindow.has_key(name): + del self.propertyWindow[name] + return + + def openMetadataPanel(self,nodePath=None): + print nodePath + self.MetadataPanel=MetadataPanel(nodePath) + pass + + def duplicate(self, nodePath = None): + ################################################################# + # duplicate(self, nodePath = None) + # This function will be called when user try to open the duplication window + ################################################################# + print '----Duplication!!' + if nodePath != None: + self.duplicateWindow = duplicateWindow(nodePath = nodePath) + pass + + def remove(self, nodePath = None): + ################################################################# + # remove(self, nodePath = None) + # This function will be called when user try to delete a node from scene + # + # For safty issue, + # we will do deselect first then remove the certain node. + # + ################################################################# + if nodePath==None: + if self.nodeSelected == None: + return + nodePath = self.nodeSelected + self.deSelectNode() + if AllScene.isLight(nodePath.getName()): + self.removeLight(nodePath) + else: + AllScene.removeObj(nodePath) + pass + + def addDummyNode(self, nodepath = None): + ################################################################# + # addDummyNode(self, nodepath = None) + # This function will be called when user try to create a dummy node into scene + # + # Here we will call dataHolder to create a dummy node + # and reparent it to the nodePath that user has assigned. + # + ################################################################# + AllScene.addDummyNode(nodepath) + self.makeDirty() + pass + + def addCollisionObj(self, nodepath = None): + ################################################################# + # addCollisionObj(self, nodepath = None) + # This function will be called when user try to create a collision object into the scene + # + # Here we will call collisionWindow to ask user what kind of collision objects they want to have. + # Then, send the information and generated collision object to dataHolder to finish the whole process + # and reparent it to the nodePath that user has assigned. + # + ################################################################# + self.collisionWindow = collisionWindow(nodepath) + pass + + def setAsReparentTarget(self, nodepath = None): + ################################################################# + # setAsReparentTarget(self, nodepath = None) + # This function will be called when user select a nodePaht + # and want to reparent other node under it. (Drom side window pop-up nemu) + ################################################################# + SEditor.setActiveParent(nodepath) + return + + def reparentToNode(self, nodepath = None): + ################################################################# + # reparentToNode(self, nodepath = None) + # This function will be call when user try to reparent a node to + # that node he selected as a reparent target before. + # + # The whole reparent process is handled by seSession, + # which is tunned from DirectSession + # + ################################################################# + SEditor.reparent(nodepath, fWrt = 1) + return + + def openPlacerPanel(self, nodePath = None): + ################################################################# + # openPlacerPanel(self, nodePath = None) + # This function will be call when user try to open a placer panel. + # This call will only success if there is no other placer panel been activated + ################################################################# + if(self.placer==None): + self.placer = Placer() + self.menuPanel.entryconfig('Placer Panel', state=DISABLED) + return + + def closePlacerPanel(self): + ################################################################# + # closePlacerPanel(self) + # This function will be called when user close the placer panel. + # Here we will reset the self.placer back to None. + # (You can think this is just like a reference count) + ################################################################# + self.placer = None + self.menuPanel.entryconfig('Placer Panel', state=NORMAL) + return + + def openAnimPanel(self, nodePath = None): + ################################################################# + # openAnimPanel(self, nodePath = None) + # This function will be called when user tries to open an Animation Panel + # This will generated a panel and put it + # into a dictionary using the actor's name as an index. + # So, if there already has an animation panel for the target actor, + # it won't allow user to open another one. + ################################################################# + name = nodePath.getName() + if AllScene.isActor(name): + if self.animPanel.has_key(name): + print '---- You already have an animation panel for this Actor!' + return + else: + Actor = AllScene.getActor(name) + self.animPanel[name] = seAnimPanel.AnimPanel(aNode=Actor) + pass + + def openMoPathPanel(self, nodepath = None): + ################################################################# + # openMoPathPanel(self, nodepath = None) + # This function will open a Motion Path Recorder for you. + ################################################################# + if self.MopathPanel == None: + self.MopathPanel = MopathRecorder() + pass + + def mopathClosed(self): + self.MopathPanel = None + return + + def changeName(self, nodePath, nName): + ################################################################# + # changeName(self, nodePath, nName) + # This function will be called when user tries to change the name of the node + ################################################################# + oName = nodePath.getName() # I need this line in order to check the obj name in the control panel. + AllScene.rename(nodePath,nName) + + # reset the list in the controller panel if it has been opened. + if (self.controllerPanel) != None: + list = AllScene.getAllObjNameAsList() + self.controllerPanel.resetNameList(list = list, name = oName, nodePath = nodePath) + return + + # Take care things under File menu + def newScene(self): + ################################################################# + # newScene(self) + # This function will clear whole stuff in the scene + # and will reset the application title to "New Scene" + ################################################################# + self.closeAllSubWindows() ## Close all sub window + if(self.CurrentFileName): + currentF=Filename(self.CurrentFileName) + self.CurrentFileName=None + AllScene.resetAll() + currentModName=currentF.getBasenameWoExtension() + # Let us actually remove the scene from sys modules... this is done because every scene is loaded as a module + # And if we reload a scene python wont reload since its already in sys.modules... and hence we delete it + # If there is ever a garbage colleciton bug..this might be a point to look at + if sys.modules.has_key(currentModName): + del sys.modules[currentModName] + print sys.getrefcount(AllScene.theScene) + del AllScene.theScene + else: + AllScene.resetAll() + self.parent.title('Scene Editor - New Scene') + pass + + def openScene(self): + ################################################################# + # openScene(self) + ################################################################# + # In the future try and provide merging of two scenes + + if(self.CurrentFileName or self.Dirty): + saveScene = tkMessageBox._show("Load scene","Save the current scene?",icon = tkMessageBox.QUESTION,type = tkMessageBox.YESNOCANCEL) + if (saveScene == "yes"): + self.saveScene() + elif (saveScene == "cancel"): + return + + self.closeAllSubWindows() ## Close all sub window + if(self.CurrentFileName): + currentF=Filename(self.CurrentFileName) + AllScene.resetAll() + currentModName=currentF.getBasenameWoExtension() + # Let us actually remove the scene from sys modules... this is done because every scene is loaded as a module + # And if we reload a scene python wont reload since its already in sys.modules... and hence we delete it + # If there is ever a garbage colleciton bug..this might be a point to look at + if sys.modules.has_key(currentModName): + del sys.modules[currentModName] + print sys.getrefcount(AllScene.theScene) + del AllScene.theScene + else: + AllScene.resetAll() + + self.CurrentFileName = AllScene.loadScene() + + if(self.CurrentFileName==None): + return + + thefile=Filename(self.CurrentFileName) + thedir=thefile.getFullpathWoExtension() + print "SCENE EDITOR::" + thedir + self.CurrentDirName=thedir + if self.CurrentFileName != None: + self.parent.title('Scene Editor - '+ Filename.fromOsSpecific(self.CurrentFileName).getBasenameWoExtension()) + if self.lightingPanel !=None: + lightList=AllScene.getList() + self.lightingPanel.updateList(lightList) + messenger.send('SGE_Update Explorer',[render]) + + + # Close the side window in order to reset all world settings to fit the scene we have loaded. + self.sideWindow.quit() + + # Try to re-open the side window again + while self.sideWindow == None: + wColor = base.getBackgroundColor() + self.worldColor[0] = wColor.getX() + self.worldColor[1] = wColor.getY() + self.worldColor[2] = wColor.getZ() + self.worldColor[3] = wColor.getW() + self.lightEnable = 1 + self.ParticleEnable = 1 + self.collision = 1 + self.openSideWindow() + + def saveScene(self): + ################################################################# + # saveScene(self) + # If this is an open file call saveAsScene + # or else instantiate FileSaver from seFileSaver.py and pass it the filename + # If this filename exists in sys.modules you cannot use it + ################################################################# + + if(self.CurrentFileName): + f=FileSaver() + f.SaveFile(AllScene,self.CurrentFileName,self.CurrentDirName,1) + self.Dirty=0 + else: + self.saveAsScene() + pass + + + def saveAsScene(self): + ################################################################# + # saveAsScene(self) + # Ask for filename using a file save dialog + # If this filename exists in sys.modules you cannot use it + # Instantiate FileSaver from seFileSaver.py and pass it the filename + ################################################################# + + fileName = tkFileDialog.asksaveasfilename(filetypes = [("PY","py")],title = "Save Scene") + if(not fileName): + return + fCheck=Filename(fileName) + #print fCheck.getBasenameWoExtension() + ############################################################################### + # !!!!! See if a module exists by this name... if it does you cannot use this filename !!!!! + ############################################################################### + if(sys.modules.has_key(fCheck.getBasenameWoExtension())): + tkMessageBox.showwarning( + "Save file", + "Cannot save with this name because there is a system module with the same name. Please resave as something else." + ) + + return + self.CurrentDirName=fileName + fileName=fileName+".py" + f=FileSaver() + self.CurrentFileName=fileName + f.SaveFile(AllScene,fileName,self.CurrentDirName,0) + self.Dirty=0 + self.parent.title('Scene Editor - '+ Filename.fromOsSpecific(self.CurrentFileName).getBasenameWoExtension()) + pass + + def loadModel(self): + ################################################################# + # loadModel(self) + # This function will be called when user tries to load a model into the scene. + # Here we will pop-up a dialog to ask user which model file should be loaded in. + # Then, pass the path to dataHolder to load the model in. + ################################################################# + modelFilename = askopenfilename( + defaultextension = '.egg', + filetypes = (('Egg Files', '*.egg'), + ('Bam Files', '*.bam'), + ('All files', '*')), + initialdir = '.', + title = 'Load New Model', + parent = self.parent) + if modelFilename: + self.makeDirty() + if not AllScene.loadModel(modelFilename, Filename.fromOsSpecific(modelFilename)): + print '----Error! No Such Model File!' + pass + + def loadActor(self): + ################################################################# + # loadActor(self) + # This function will be called when user tries to load an Actor into the scene. + # Here we will pop-up a dialog to ask user which Actor file should be loaded in. + # Then, pass the path to dataHolder to load the Actor in. + ################################################################# + ActorFilename = askopenfilename( + defaultextension = '.egg', + filetypes = (('Egg Files', '*.egg'), + ('Bam Files', '*.bam'), + ('All files', '*')), + initialdir = '.', + title = 'Load New Actor', + parent = self.parent) + + + if ActorFilename: + self.makeDirty() + if not AllScene.loadActor(ActorFilename, Filename.fromOsSpecific(ActorFilename)): + print '----Error! No Such Model File!' + pass + + def importScene(self): + self.makeDirty() + print '----God bless you Please Import!' + pass + + + ## Take care those things under Edit nemu + def unDo(self): + pass + + def reDo(self): + pass + + def deSelectNode(self, nodePath=None): + ################################################################# + # deSelectNode(self, nodePath=None) + # This function will deselect the node which we have selected currently. + # This will also remove the monitor task which monitor selected object's + # position, orientation and scale each frame. + ################################################################# + if nodePath != None: + self.seSession.deselect(nodePath) + if self.isSelect: + self.isSelect = False + #if self.nodeSelected != None: + # self.nodeSelected.hideBounds() + self.nodeSelected =None + self.menuEdit.entryconfig('Deselect', state=DISABLED) + self.menuEdit.entryconfig('Add Dummy', state=DISABLED) + self.menuEdit.entryconfig('Duplicate', state=DISABLED) + self.menuEdit.entryconfig('Remove', state=DISABLED) + self.menuEdit.entryconfig('Object Properties', state=DISABLED) + if self.sideWindowCount==1: + self.sideWindow.SGE.deSelectTree() + if taskMgr.hasTaskNamed('seMonitorSelectedNode'): + taskMgr.remove('seMonitorSelectedNode') + return + pass + + def addDummy(self): + ################################################################# + # addDummy(self) + # This function will do nothing but call other function + # to add a dummy into the scene. + # + # Ok... this is really redundancy... + # + ################################################################# + self.addDummyNode(self.nodeSelected) + pass + + def duplicateNode(self): + ################################################################# + # duplicateNode(self) + # This function will do nothing but call other function + # to open the duplication window. + # + # Ok... this is really redundancy... + # + ################################################################# + if self.nodeSelected!=None: + self.duplicate(self.nodeSelected) + pass + + def removeNode(self): + ################################################################# + # removeNode(self) + # This function will do nothing but call other function + # to remove the current selected node.. + # + # Ok... this is really redundancy... + # + ################################################################ + self.remove(self.nodeSelected) + pass + + def showObjProp(self): + ################################################################ + # showObjProp(self) + # This function will do nothing but call other function + # to open the property window of current selected node.. + # + # Ok... this is really redundancy... + # + ################################################################ + self.openPropertyPanel(self.nodeSelected) + pass + + def showCameraSetting(self): + ################################################################ + # showCameraSetting(self) + # This function will do nothing but call other function + # to open the property window of camera.. + # + # Ok... this is really redundancy... + # + ################################################################ + self.openPropertyPanel(camera) + pass + + def showRenderSetting(self): + '''Currently, no idea what gonna pop-out here...''' + pass + + ## Take care those thins under Edit nemu + def openSideWindow(self): + ################################################################ + # openSideWindow(self) + # This function will open the side window and set the reference number + # so that we can make sure there won't have two or more side windows in the same time. + ################################################################ + if self.sideWindowCount==0: + self.sideWindow = sideWindow(worldColor = self.worldColor, + lightEnable = self.lightEnable, + ParticleEnable = self.ParticleEnable, + basedriveEnable = self.basedriveEnable, + collision = self.collision, + backface = self.backface, + texture = self.texture, + wireframe = self.wireframe, + grid = self.grid, + widgetVis = self.widgetVis, + enableAutoCamera = self.enableAutoCamera) + self.sideWindowCount = 1 + self.menuPanel.entryconfig('Side Window', state=DISABLED) + return + + def openAnimationPanel(self): + ################################################################ + # openAnimationPanel(self) + # This function will do nothing but call other function + # to open the animation window for selected node(if it is an Actor).. + # + # Ok... this is really redundancy... + # + ################################################################ + if AllScene.isActor(self.nodeSelected): + self.openAnimPanel(self.nodeSelected) + pass + + def openMopathPanel(self): + ################################################################ + # openMopathPanel(self) + # This function will create a Motion Path Recorder + ################################################################ + MopathPanel = MopathRecorder() + pass + + def toggleParticleVisable(self, visable): + ################################################################ + # toggleParticleVisable(self, visable) + # This function will be called each time user has toggled + # the check box of Particle visibility in the side window. + # The reason we keep track this is because + # we have to know we should show/hide the model on the new-created particle + ################################################################ + self.ParticleEnable = visable + AllScene.toggleParticleVisable(visable) + return + + def openLightingPanel(self): + ################################################################ + # openLightingPanel(self) + # open the lighting panel here. + # If there is already exist a lighting panel, then do nothing + ################################################################ + if self.lightingPanel==None: + self.lightingPanel = lightingPanel(AllScene.getLightList()) + self.menuPanel.entryconfig('Lighting Panel', state=DISABLED) + return + + def addParticleEffect(self,effect_name,effect,node): + AllScene.particleDict[effect_name]=effect + AllScene.particleNodes[effect_name]=node + if not self.ParticleEnable: + AllScene.particleNodes[effect_name].setTransparency(True) + AllScene.particleNodes[effect_name].setAlphaScale(0) + AllScene.particleNodes[effect_name].setBin("fixed",1) + return + + def openParticlePanel(self): + if self.particlePanel != None: + ## There already has a Particle panel! + return + if(len(AllScene.particleDict)==0): + self.particlePanel=seParticlePanel.ParticlePanel() + else: + for effect in AllScene.particleDict: + theeffect=AllScene.particleDict[effect] + self.particlePanel=seParticlePanel.ParticlePanel(particleEffect=theeffect,effectsDict=AllScene.particleDict) + + pass + + def closeParticlePanel(self): + self.particlePanel = None + return + + def openInputPanel(self): + if self.controllerPanel==None: + list = AllScene.getAllObjNameAsList() + type, dataList = AllScene.getControlSetting() + self.controllerPanel = controllerWindow(listOfObj = list, controlType = type, dataList = dataList) + pass + + def closeInputPanel(self): + self.controllerPanel = None + return + + def requestObjFromControlW(self, name): + ################################################################ + # requestObjFromControlW(self, name) + # Call back function + # Each time when user selects a node from Control Panel, + # this function will be called. + # This function will get the actual nodePath from dataHolder and then + # set it back into controller panel + ################################################################ + node = AllScene.getObjFromSceneByName(name) + + if (self.controllerPanel) != None and (node!=None): + self.controllerPanel.setNodePathIn(node) + + return + + def setControlSet(self, controlType, dataList): + if controlType == 'Keyboard': + self.controlTarget = dataList[0] + self.keyboardMapDict.clear() + self.keyboardMapDict = dataList[1].copy() + self.keyboardSpeedDict.clear() + self.keyboardSpeedDict = dataList[2].copy() + return + + def startControl(self, controlType, dataList): + if not self.enableControl: + self.enableControl = True + else: + # Stop the current control setting first + # Also this will make sure we won't catch wrong keyboard message + self.stopControl(controlType) + self.enableControl = True + + self.setControlSet(controlType, dataList) + self.lastContorlTimer = globalClock.getFrameTime() + if controlType == 'Keyboard': + self.controlType = 'Keyboard' + self.keyControlEventDict = {} + self.transNodeKeyboard = self.controlTarget.attachNewNode('transformNode') + self.transNodeKeyboard.hide() + for index in self.keyboardMapDict: + self.keyControlEventDict[index] = 0 + self.accept(self.keyboardMapDict[index], lambda a = index:self.keyboardPushed(a)) + self.accept(self.keyboardMapDict[index]+'-up', lambda a = index:self.keyboardReleased(a)) + return + + def stopControl(self, controlType): + if not self.enableControl: + return + if controlType == 'Keyboard': + self.enableControl = False + for index in self.keyboardMapDict: + self.ignore(self.keyboardMapDict[index]) + self.ignore(self.keyboardMapDict[index]+'-up') + taskMgr.remove("KeyboardControlTask") + self.transNodeKeyboard.removeNode() + return + + def keyboardPushed(self, key): + self.keyControlEventDict[key] = 1 + if not taskMgr.hasTaskNamed("KeyboardControlTask"): + self.keyboardLastTimer = globalClock.getFrameTime() + taskMgr.add(self.keyboardControlTask, "KeyboardControlTask") + return + + def keyboardReleased(self, key): + self.keyControlEventDict[key] = 0 + for index in self.keyControlEventDict: + if self.keyControlEventDict[index] == 1: + return + if taskMgr.hasTaskNamed("KeyboardControlTask"): + taskMgr.remove("KeyboardControlTask") + return + + def keyboardControlTask(self, task): + newTimer = globalClock.getFrameTime() + delta = newTimer - self.keyboardLastTimer + self.keyboardLastTimer = newTimer + pos = self.controlTarget.getPos() + hpr = self.controlTarget.getHpr() + scale = self.controlTarget.getScale() + self.transNodeKeyboard.setPosHpr((self.keyControlEventDict['KeyRight']*self.keyboardSpeedDict['SpeedRight']-self.keyControlEventDict['KeyLeft']*self.keyboardSpeedDict['SpeedLeft'])*delta, + (self.keyControlEventDict['KeyForward']*self.keyboardSpeedDict['SpeedForward']-self.keyControlEventDict['KeyBackward']*self.keyboardSpeedDict['SpeedBackward'])*delta, + (self.keyControlEventDict['KeyUp']*self.keyboardSpeedDict['SpeedUp']-self.keyControlEventDict['KeyDown']*self.keyboardSpeedDict['SpeedDown'])*delta, + (self.keyControlEventDict['KeyTurnLeft']*self.keyboardSpeedDict['SpeedTurnLeft']-self.keyControlEventDict['KeyTurnRight']*self.keyboardSpeedDict['SpeedTurnRight'])*delta, + (self.keyControlEventDict['KeyTurnUp']*self.keyboardSpeedDict['SpeedTurnUp']-self.keyControlEventDict['KeyTurnDown']*self.keyboardSpeedDict['SpeedTurnDown'])*delta, + (self.keyControlEventDict['KeyRollLeft']*self.keyboardSpeedDict['SpeedRollLeft']-self.keyControlEventDict['KeyRollRight']*self.keyboardSpeedDict['SpeedRollRight'])*delta) + newPos = self.transNodeKeyboard.getPos(self.controlTarget.getParent()) + newHpr = self.transNodeKeyboard.getHpr(self.controlTarget.getParent()) + overAllScale = self.keyControlEventDict['KeyScaleUp']*self.keyboardSpeedDict['SpeedScaleUp']-self.keyControlEventDict['KeyScaleDown']*self.keyboardSpeedDict['SpeedScaleDown'] + newScale = Point3(scale.getX() + (overAllScale + self.keyControlEventDict['KeyScaleXUp']*self.keyboardSpeedDict['SpeedScaleXUp'] - self.keyControlEventDict['KeyScaleXDown']*self.keyboardSpeedDict['SpeedScaleXDown'])*delta, + scale.getY() + (overAllScale + self.keyControlEventDict['KeyScaleYUp']*self.keyboardSpeedDict['SpeedScaleYUp'] - self.keyControlEventDict['KeyScaleYDown']*self.keyboardSpeedDict['SpeedScaleYDown'])*delta, + scale.getZ() + (overAllScale + self.keyControlEventDict['KeyScaleZUp']*self.keyboardSpeedDict['SpeedScaleZUp'] - self.keyControlEventDict['KeyScaleZDown']*self.keyboardSpeedDict['SpeedScaleZDown'])*delta + ) + self.controlTarget.setPos(newPos.getX(), newPos.getY() , newPos.getZ()) + self.controlTarget.setHpr(newHpr.getX(), newHpr.getY() , newHpr.getZ()) + self.controlTarget.setScale(newScale.getX(),newScale.getY(),newScale.getZ()) + self.transNodeKeyboard.setPosHpr(0,0,0,0,0,0) + return Task.cont + + ## Misc + ##### This one get the event from SGE (Scene Graph Explorer) and Picking + def selectNode(self, nodePath=None, callBack = True): + ################################################################ + # selectNode(self, nodePath=None, callBack = True) + # This will be called when user try to select nodes from the + # side window. + # It will also call seSession to select this node in order to keep data's consistency + ################################################################ + if nodePath==None: + self.isSelect = False + self.nodeSelected =None + if taskMgr.hasTaskNamed('seMonitorSelectedNode'): + taskMgr.remove('seMonitorSelectedNode') + return + else: + self.isSelect = True + #if self.nodeSelected != None: + # self.nodeSelected.hideBounds() + self.nodeSelected = nodePath + #self.nodeSelected.showBounds() + self.menuEdit.entryconfig('Deselect', state=NORMAL) + self.menuEdit.entryconfig('Add Dummy', state=NORMAL) + self.menuEdit.entryconfig('Duplicate', state=NORMAL) + self.menuEdit.entryconfig('Remove', state=NORMAL) + self.menuEdit.entryconfig('Object Properties', state=NORMAL) + if callBack: + self.seSession.select(nodePath,fResetAncestry=1) + messenger.send('SGE_Update Explorer',[render]) + if not taskMgr.hasTaskNamed('seMonitorSelectedNode'): + self.oPos = self.nodeSelected.getPos() + self.oHpr = self.nodeSelected.getHpr() + self.oScale = self.nodeSelected.getScale() + taskMgr.add(self.monitorSelectedNodeTask, 'seMonitorSelectedNode') + return + pass + + def selectFromScene(self, nodePath=None, callBack=True): + ################################################################ + # selectFromScene(self, nodePath=None, callBack = True) + # This will be called when user try to select nodes from the + # scene. (By picking) + # Actually this will be called by seSession + # The reason we make two selections is we don't want they call each other and never stop... + ################################################################ + if nodePath==None: + self.isSelect = False + self.nodeSelected =None + if taskMgr.hasTaskNamed('seMonitorSelectedNode'): + taskMgr.remove('seMonitorSelectedNode') + return + else: + self.isSelect = True + #if self.nodeSelected != None: + # self.nodeSelected.hideBounds() + self.nodeSelected = nodePath + #self.nodeSelected.showBounds() + self.menuEdit.entryconfig('Deselect', state=NORMAL) + self.menuEdit.entryconfig('Add Dummy', state=NORMAL) + self.menuEdit.entryconfig('Duplicate', state=NORMAL) + self.menuEdit.entryconfig('Remove', state=NORMAL) + self.menuEdit.entryconfig('Object Properties', state=NORMAL) + self.sideWindow.SGE.selectNodePath(nodePath,callBack) + messenger.send('SGE_Update Explorer',[render]) + if not taskMgr.hasTaskNamed('seMonitorSelectedNode'): + self.oPos = self.nodeSelected.getPos() + self.oHpr = self.nodeSelected.getHpr() + self.oScale = self.nodeSelected.getScale() + taskMgr.add(self.monitorSelectedNodeTask, 'seMonitorSelectedNode') + return + pass + + def monitorSelectedNodeTask(self, task): + ################################################################ + # monitorSelectedNodeTask(self, task) + # This is a function which will keep tracking + # the position, orientation and scale data of selected node and update the display on the screen. + # Alos, it will send out message to sychronize the data in the placer and property window. + ################################################################ + if self.nodeSelected != None: + pos = self.nodeSelected.getPos() + hpr = self.nodeSelected.getHpr() + scale = self.nodeSelected.getScale() + if ((self.oPos != pos )or(self.oScale != scale)or(self.oHpr != hpr)): + messenger.send('forPorpertyWindow'+self.nodeSelected.getName(),[pos, hpr, scale]) + messenger.send('placerUpdate') + self.oPos = pos + self.oScale = scale + self.oHpr = hpr + self.posLabel['text'] = "Position : X: %2.2f Y: %2.2f Z: %2.2f"%(pos.getX(), pos.getY(),pos.getZ()) + self.hprLabel['text'] = "Orientation: H: %2.2f P: %2.2f R: %2.2f"%(hpr.getX(), hpr.getY(),hpr.getZ()) + self.scaleLabel['text'] = "Scale : X: %2.2f Y: %2.2f Z: %2.2f"%(scale.getX(), scale.getY(),scale.getZ()) + return Task.cont + + def deselectFromScene(self): + ################################################################ + # deselectFromScene(self) + # This function will do nothing but call other function + # to delete selected node... + # + # Ok... this is really redundancy... + # + ################################################################ + self.deSelectNode(self.nodeSelected) + messenger.send('SGE_Update Explorer',[render]) + + ##### Take care the even quest from Side Window + def lightToggle(self): + ################################################################ + # lightToggle(self) + # This function will do nothing but call other function + # to toggle the light... + ################################################################ + self.makeDirty() + AllScene.toggleLight() + return + + def sideWindowClose(self,worldColor,lightEnable,ParticleEnable, basedriveEnable,collision, + backface, texture, wireframe, grid, widgetVis, enableAutoCamera): + ################################################################ + # sideWindowClose(self,worldColor,lightEnable,ParticleEnable, basedriveEnable,collision, + # backface, texture, wireframe, grid, widgetVis, enableAutoCamera): + # This function will be called when user close the side window. + # Here we will restore all parameters about world setting back in the sceneEditor. + # So, when next time people recall the side window, it will still keep the same world setting. + ################################################################ + if self.sideWindowCount==1: + self.worldColor = worldColor + self.lightEnable = lightEnable + self.ParticleEnable = ParticleEnable + self.basedriveEnable = basedriveEnable + self.collision = collision + self.backface = backface + self.texture = texture + self.wireframe = wireframe + self.grid = grid + self.enableAutoCamera = enableAutoCamera + self.widgetVis = widgetVis + self.sideWindowCount=0 + self.sideWindow = None + self.menuPanel.entryconfig('Side Window', state=NORMAL) + return + + ## Process message from Duplication Window + def duplicationObj(self, nodePath, pos, hpr, scale, num): + ################################################################ + # duplicationObj(self, nodePath, pos, hpr, scale, num) + # This function will do nothing but call other function + # to duplicate selected node... + # + # Ok... this is really redundancy... + # + ################################################################ + AllScene.duplicateObj(nodePath, pos, hpr, scale, num) + return + + ## Process message from Animation Panel + def animationLoader(self, nodePath, Dic): + name = nodePath.getName() + AllScene.loadAnimation(name, Dic) + return + + def animationRemove(self, nodePath, name): + AllScene.removeAnimation(nodePath.getName(),name) + return + + def animPanelClose(self, name): + if self.animPanel.has_key(name): + del self.animPanel[name] + return + + ### Blend Animation Panel + def openBlendAnimPanel(self, nodePath=None): + ################################################################ + # openBlendAnimPanel(self, nodePath=None) + # This function will get the user defined blending animation data from dataHolder. + # And then open a blendAnimPanel by passing those data in. + ################################################################ + name = nodePath.getName() + if AllScene.isActor(name): + if self.animBlendPanel.has_key(name): + print '---- You already have an Blend Animation Panel for this Actor!' + return + else: + Actor = AllScene.getActor(name) + Dict = AllScene.getBlendAnimAsDict(name) + self.animBlendPanel[name] = BlendAnimPanel(aNode=Actor, blendDict=Dict) + pass + return + + def animBlendPanelSave(self, actorName, blendName, animNameA, animNameB, effect): + ################################################################ + # animBlendPanelSave(self, actorName, blendName, animNameA, animNameB, effect) + # This function will call dataHolder to save the blended animation. + # Then, it will reset the newest blended animation list back to animBlendPanel + ################################################################ + dict = AllScene.saveBlendAnim(actorName, blendName, animNameA, animNameB, effect) + self.animBlendPanel[actorName].setBlendAnimList(dict) + return + + def animBlendPanelRemove(self, actorName, blendName): + ################################################################ + # animBlendPanelRemove(self, actorName, blendName) + # This function will call dataHolder to remove the blended animation. + # Then, it will reset the newest blended animation list back to animBlendPanel + ################################################################ + dict = AllScene.removeBlendAnim(actorName, blendName) + self.animBlendPanel[actorName].setBlendAnimList(dict, True) + return + + def animBlendPanelRename(self, actorName, nName, oName, animNameA, animNameB, effect): + ################################################################ + # animBlendPanelRename(self, actorName, nName, oName, animNameA, animNameB, effect) + # This function will call dataHolder to rename the blended animation. + # Then, it will reset the newest blended animation list back to animBlendPanel + ################################################################ + dict = AllScene.renameBlendAnim(actorName, nName, oName, animNameA, animNameB, effect) + self.animBlendPanel[actorName].setBlendAnimList(dict) + return + + def animBlendPanelClose(self, name): + ################################################################ + # animBlendPanelClose(self, name) + # This function will be called when Blend panel has been closed. + # Here we will reset the reference dictionary so it can be open again. + ################################################################ + if self.animBlendPanel.has_key(name): + del self.animBlendPanel[name] + return + + ## Process message from SEditor object + def toggleWidgetVis(self): + ################################################################ + # toggleWidgetVis(self) + # This function will be called when user use the hot-key to change the + # world setting. (From seSession) + # In this function we will restore the change and let side window know + # the hot-key ahs been pushed. + ################################################################ + if self.sideWindow != None: + self.sideWindow.toggleWidgetVisFromMainW() + else: + self.widgetVis = (self.widgetVis+1)%2 + + def toggleBackface(self): + ################################################################ + # toggleBackface(self) + # This function will be called when user use the hot-key to change the + # world setting. (From seSession) + # In this function we will restore the change and let side window know + # the hot-key ahs been pushed. + ################################################################ + if self.sideWindow != None: + self.sideWindow.toggleBackfaceFromMainW() + else: + self.backface = (self.backface+1)%2 + + def toggleTexture(self): + ################################################################ + # toggleTexture(self) + # This function will be called when user use the hot-key to change the + # world setting. (From seSession) + # In this function we will restore the change and let side window know + # the hot-key ahs been pushed. + ################################################################ + if self.sideWindow != None: + self.sideWindow.toggleTextureFromMainW() + else: + self.texture = (self.texture+1)%2 + + def toggleWireframe(self): + ################################################################ + # toggleWireframe(self) + # This function will be called when user use the hot-key to change the + # world setting. (From seSession) + # In this function we will restore the change and let side window know + # the hot-key ahs been pushed. + ################################################################ + if self.sideWindow != None: + self.sideWindow.toggleWireframeFromMainW() + else: + self.wireframe = (self.wireframe+1)%2 + + def openAlignPanel(self, nodePath=None): + name = nodePath.getName() + if not self.alignPanelDict.has_key(name): + list = AllScene.getAllObjNameAsList() + if name in list: + list.remove(name) + else: + return + self.alignPanelDict[name] = AlignTool(nodePath = nodePath, list = list) + return + + def closeAlignPanel(self, name=None): + if self.alignPanelDict.has_key(name): + del self.alignPanelDict[name] + + def alignObject(self, nodePath, name, list): + target = AllScene.getObjFromSceneByName(name) + pos = target.getPos() + hpr = target.getHpr() + scale = target.getScale() + if list[0]: # Align X + nodePath.setX(pos.getX()) + if list[1]: # Align Y + nodePath.setY(pos.getY()) + if list[2]: # Align Z + nodePath.setZ(pos.getZ()) + if list[3]: # Align H + nodePath.setH(hpr.getX()) + if list[4]: # Align P + nodePath.setP(hpr.getY()) + if list[5]: # Align R + nodePath.setR(hpr.getZ()) + if list[6]: # Scale X + nodePath.setSx(scale.getX()) + if list[7]: # Scale Y + nodePath.setSy(scale.getY()) + if list[8]: # Scale Z + nodePath.setSz(scale.getZ()) + return + + ### Event from Motion Path Panel + def requestCurveList(self, nodePath,name): + curveList = AllScene.getCurveList(nodePath) + messenger.send('curveListFor'+name, [curveList]) + + + ## Steal from DirectSession... + def flash(self, nodePath = 'None Given'): + """ Highlight an object by setting it red for a few seconds """ + # Clean up any existing task + taskMgr.remove('flashNodePath') + # Spawn new task if appropriate + if nodePath == 'None Given': + # If nothing specified, try selected node path + nodePath = self.selected.last + if nodePath: + if nodePath.hasColor(): + doneColor = nodePath.getColor() + flashColor = VBase4(1) - doneColor + flashColor.setW(1) + else: + doneColor = None + flashColor = VBase4(1,0,0,1) + # Temporarily set node path color + nodePath.setColor(flashColor) + # Clean up color in a few seconds + t = taskMgr.doMethodLater(1.5, + # This is just a dummy task + self.flashDummy, + 'flashNodePath') + t.nodePath = nodePath + t.doneColor = doneColor + # This really does all the work + t.uponDeath = self.flashDone + + def flashDummy(self, state): + # Real work is done in upon death function + return Task.done + + def flashDone(self,state): + # Return node Path to original state + if state.nodePath.isEmpty(): + # Node path doesn't exist anymore, bail + return + if state.doneColor: + state.nodePath.setColor(state.doneColor) + else: + state.nodePath.clearColor() + + + + +editor = myLevelEditor(parent = tkroot) + +run() diff --git a/doc/SceneEditor/seAnimPanel.py b/doc/SceneEditor/seAnimPanel.py new file mode 100644 index 0000000000..009ef70631 --- /dev/null +++ b/doc/SceneEditor/seAnimPanel.py @@ -0,0 +1,608 @@ +################################################################# +# collisionWindow.py +# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +################################################################# +# Import Tkinter, Pmw, and the floater code from this directory tree. +from AppShell import * +from TkGlobal import * +from tkSimpleDialog import askfloat +import string +import math +import types +import Task + +FRAMES = 0 +SECONDS = 1 + +class AnimPanel(AppShell): + ################################################################# + # This class will generate a animation panel for an actor + # which user assigned. Inside this panel, instead of using actorInterval + # or just simply calling the play function in Actor, we create a task to + # set animation frame by frame using setPose. + ################################################################# + # Override class variables + appname = 'Anim Panel' + frameWidth = 575 + frameHeight = 250 + usecommandarea = 0 + usestatusarea = 0 + index = 0 + dragMode = False + rateList= ['1/24.0', '0.1', '0.5', '1.0', '2.0', '5.0' , '10.0'] + + + def __init__(self, aNode = None, parent = None, **kw): + INITOPT = Pmw.INITOPT + self.id = 'AnimPanel '+ aNode.getName() + self.appname = self.id + optiondefs = ( + ('title', self.appname, None), + ('actor', aNode, None), + ('animList', [], None), + ) + self.defineoptions(kw, optiondefs) + + self.frameHeight = 300 + self.id = 'AnimPanel '+ aNode.getName() + self.nodeName = aNode.getName() + # Initialize the superclass + AppShell.__init__(self) + + # Execute option callbacks + self.initialiseoptions(AnimPanel) + + self.currTime = 0.0 # Initialize the start time + self.animName = None + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + def createInterface(self): + # Handle to the toplevels interior + interior = self.interior() + menuBar = self.menuBar + + menuBar.addmenu('Anim', 'Anim Panel Operations') + + # Reset all actor controls + menuBar.addmenuitem('File', 'command', + 'Load Animation', + label = 'Load Animation', + command = self.loadAnimation) + menuBar.addmenuitem('Anim', 'command', + 'Set actor controls to t = 0.0', + label = 'Jump all to zero', + command = self.resetAllToZero) + menuBar.addmenuitem('Anim', 'command', + 'Set Actor controls to end time', + label = 'Jump all to end time', + command = self.resetAllToEnd) + menuBar.addmenuitem('Anim', 'separator') + menuBar.addmenuitem('Anim', 'command', + 'Play Current Animation', + label = 'Play', + command = self.play) + menuBar.addmenuitem('Anim', 'command', + 'Stop Current Animation', + label = 'stop', + command = self.stop) + + # Create a frame to hold all the actor controls + actorFrame = Frame(interior) + name_label = Label(actorFrame, text= self.nodeName,font=('MSSansSerif', 16), + relief = SUNKEN, borderwidth=3) + name_label.place(x=5,y=5,anchor=NW) + Label(actorFrame, text= "Animation:", font=('MSSansSerif', 12)).place(x=140,y=5,anchor=NW) + Label(actorFrame, text= "Play Rate:", font=('MSSansSerif', 12)).place(x=140,y=35,anchor=NW) + self['animList'] = self['actor'].getAnimNames() + self.AnimEntry = self.createcomponent( + 'AnimationMenu', (), None, + Pmw.ComboBox, (actorFrame,), + labelpos = W, entry_width = 20, selectioncommand = self.setAnimation, + scrolledlist_items = self['animList']) + self.AnimEntry.place(x=240,y=10,anchor=NW) + + + self.playRateEntry = self.createcomponent( + 'playRateMenu', (), None, + Pmw.ComboBox, (actorFrame,), + labelpos = W, entry_width = 20, selectioncommand = self.setPlayRate, + scrolledlist_items = self.rateList) + self.playRateEntry.place(x=240,y=40,anchor=NW) + self.playRateEntry.selectitem('1.0') + + ### Loop checkbox + Label(actorFrame, text= "Loop:", font=('MSSansSerif', 12)).place(x=420,y=05,anchor=NW) + + self.loopVar = IntVar() + self.loopVar.set(0) + self.loopButton = self.createcomponent( + 'loopButton', (), None, + Checkbutton, (actorFrame,), + variable = self.loopVar) + self.loopButton.place(x=470,y=7,anchor=NW) + + ### Display Frames/Seconds + Label(actorFrame, text= "Frame/Second:", font=('MSSansSerif', 11)).place(x=5,y=75,anchor=NW) + + self.unitsVar = IntVar() + self.unitsVar.set(FRAMES) + self.displayButton = self.createcomponent( + 'displayButton', (), None, + Checkbutton, (actorFrame,), + command = self.updateDisplay, + variable = self.unitsVar) + self.displayButton.place(x=120,y=77,anchor=NW) + + ## scale control + frameFrame = Frame(actorFrame, relief = SUNKEN, bd = 1) + self.minLabel = self.createcomponent( + 'minLabel', (), 'sLabel', + Label, (frameFrame,), + text = 0) + self.minLabel.pack(side = LEFT) + + self.frameControl = self.createcomponent( + 'scale', (), None, + Scale, (frameFrame,), + from_ = 0, to = 24, resolution = 1.0, + command = self.goTo, length = 500, + orient = HORIZONTAL, showvalue = 1) + self.frameControl.pack(side = LEFT, expand = 1) + self.frameControl.bind('', self.onPress) + self.frameControl.bind('', self.onRelease) + + self.maxLabel = self.createcomponent( + 'maxLabel', (), 'sLabel', + Label, (frameFrame,), + text = 24) + self.maxLabel.pack(side = LEFT) + frameFrame.pack(side = LEFT, expand = 1, fill = X) + + ## button contorl + ButtomFrame = Frame(actorFrame, relief = SUNKEN, bd = 1,borderwidth=5) + self.toStartButton = self.createcomponent( + 'toStart', (), None, + Button, (ButtomFrame,), + text = '<<', + width = 8, + command = self.resetAllToZero) + self.toStartButton.pack(side = LEFT, expand = 1, fill = X) + + self.playButton = self.createcomponent( + 'playButton', (), None, + Button, (ButtomFrame,), + text = 'Play', width = 8, + command = self.play) + self.playButton.pack(side = LEFT, expand = 1, fill = X) + + self.stopButton = self.createcomponent( + 'stopButton', (), None, + Button, (ButtomFrame,), + text = 'Stop', width = 8, state=DISABLED, + command = self.stop) + self.stopButton.pack(side = LEFT, expand = 1, fill = X) + + self.toEndButton = self.createcomponent( + 'toEnd', (), None, + Button, (ButtomFrame,), + text = '>>', + width = 8, + command = self.resetAllToEnd) + self.toEndButton.pack(side = LEFT, expand = 1, fill = X) + + ButtomFrame.place(anchor=NW,x=5,y=165) + + self.removeButton = self.createcomponent( + 'Remove Animation', (), None, + Button, (actorFrame,), + text = 'Remove This Animation', width = 20, + command = self.removeAnim) + self.removeButton.place(anchor=NW,x=5,y=220) + + self.loadButton = self.createcomponent( + 'Load Animation', (), None, + Button, (actorFrame,), + text = 'Load New Animation', width = 20, + command = self.loadAnimation) + self.loadButton.place(anchor=NW,x=180,y=220) + + # Now pack the actor frame + actorFrame.pack(expand = 1, fill = BOTH) + + def updateList(self): + ################################################################# + # updateList(self) + # This function will update the list of animations that the Actor + # currently has into the combo box widget. + ################################################################# + self.ignore('DataH_loadFinish'+self.nodeName) + del self.loaderWindow + self['animList'] = self['actor'].getAnimNames() + animL = self['actor'].getAnimNames() + self.AnimEntry.setlist(animL) + + + def removeAnim(self): + ################################################################# + # removeAnim(self) + # This function will stop the animation and get the name of animation + # which user wish to remove from the panel. Then, it will send out + # a message to dataHolder to remove the target animation. + # And in the same time, it will start waiting a return message to + # make sure that target animation has been removed. + ################################################################# + name = self.AnimEntry.get() + if taskMgr.hasTaskNamed(self.id + '_UpdateTask'): + self.stop() + self.accept('DataH_removeAnimFinish'+self.nodeName,self.afterRemove) + messenger.send('AW_removeAnim',[self['actor'],name]) + return + + def afterRemove(self): + ################################################################# + # afterRemove(self) + # This function will be called once panel has received the return + # message from dataHolder. This function will call setList to + # reset the list of Animations + ################################################################# + self.ignore('DataH_removeAnimFinish'+self.nodeName) + self['animList'] = self['actor'].getAnimNames() + animL = self['actor'].getAnimNames() + self.AnimEntry.setlist(animL) + print '-----',animL + return + + def loadAnimation(self): + ################################################################# + # loadAnimation(self) + # This function will open a dialog window to require user to input + # the animation he wants to load in for this actor. + ################################################################# + self.loaderWindow = LoadAnimPanel(aNode=self['actor']) + self.accept('DataH_loadFinish'+self.nodeName,self.updateList) + return + + def play(self): + ################################################################# + # play(self) + # This function will be called when user click on the "play" button. + # First, this function will initialize all parameter that the actual + # play task need to run and add the play task into the taskMgr. + ################################################################# + self.animName = self.AnimEntry.get() + if self.animName in self['animList']: + animName = self.AnimEntry.get() + self.playButton.config(state=DISABLED) + self.lastT = globalClock.getFrameTime() + taskMgr.add(self.playTask, self.id + '_UpdateTask') + self.stopButton.config(state=NORMAL) + else: + print '----Illegal Animaion name!!', self.animName + return + + def playTask(self, task): + ################################################################# + # playTask(self, task) + # This task will record time by each frame + # In fact it is just a clock keeper. + # If the current frame time over the max long of the animation, + # it will reset the timer. + # Anyway, this function will call gotoT by each frame. + ################################################################# + fLoop = self.loopVar.get() + currT = globalClock.getFrameTime() + deltaT = currT - self.lastT + self.lastT = currT + if self.dragMode: + return Task.cont + self.currTime = self.currTime + deltaT + if (self.currTime > self.maxSeconds): + if fLoop: + self.currTime = self.currTime%self.duration + self.gotoT(self.currTime) + else: + self.currTime = 0.0 + self.gotoT(0.0) + self.playButton.config(state=NORMAL) + self.stopButton.config(state=DISABLED) + return Task.done + else: + self.gotoT(self.currTime) + return Task.cont + + def stop(self): + ################################################################# + # stop(self) + # This function will remove the play task from taskMgr when user + # click on the "Stop" button + ################################################################# + taskMgr.remove(self.id + '_UpdateTask') + self.playButton.config(state=NORMAL) + self.stopButton.config(state=DISABLED) + return + + def setAnimation(self, animation): + ################################################################# + # setAnimation(self, animation) + # This function will be called each time when user change + # the current animation. Most important thing this function do is + # to recalculate all variables to fit the selected animation + ################################################################# + self.animName = self.AnimEntry.get() + playRate = '%0.1f' % self['actor'].getPlayRate(self.animName) + if playRate not in self.rateList: + def strCmp(a, b): + return cmp(eval(a), eval(b)) + self.rateList.append(playRate) + self.rateList.sort(strCmp) + self.playRateEntry.reset(self.rateList) + self.playRateEntry.selectitem(playRate) + self.currTime = 0.0 + self.frameControl.set(0) + self.updateDisplay() + return + + def setPlayRate(self,rate): + ################################################################# + # setPlayRate(self, rate) + # This function will be called each time when user changes the current play rate. + ################################################################# + self.animName = self.AnimEntry.get() + if self.animName in self['animList']: + self['actor'].setPlayRate(eval(rate), self.animName) + self.updateDisplay() + return + + def updateDisplay(self): + ################################################################# + # updateDisplay(self) + # This function will be called whenever something has been changed + # on the panel. In here we will re-new all widgets on the panel to + # correct value. + ################################################################# + self.fps = self['actor'].getFrameRate(self.animName) + self.duration = self['actor'].getDuration(self.animName) + self.maxFrame = self['actor'].getNumFrames(self.animName) - 1 + self.maxSeconds = self.duration + if self.unitsVar.get() == FRAMES: + fromFrame = 0 + toFrame = self.maxFrame + self.minLabel['text'] = fromFrame + self.maxLabel['text'] = toFrame + self.frameControl.configure(from_ = fromFrame, + to = toFrame, + resolution = 1.0) + else: + self.minLabel['text'] = '0.0' + self.maxLabel['text'] = "%.2f" % self.duration + self.frameControl.configure(from_ = 0.0, + to = self.duration, + resolution = 0.01) + + def gotoT(self,time): + ################################################################# + # gotoT(self, time) + # calculate the right parameter which will be send to set Frame + # Control slider, which is the real place we play the animation. + ################################################################# + if self.unitsVar.get() == FRAMES: + self.frameControl.set(time * self.fps) + else: + self.frameControl.set(time) + return + + def goTo(self,frame): + ################################################################# + # goto(self, frame) + # Call back function for the frame control slider. + # This function will set the animation by the value on the slider. + # + # This function is the real function we "play" the animation. + # + ################################################################# + if self.animName in self['animList']: + # Convert scale value to float + frame = string.atof(frame) + # Now convert t to seconds for offset calculations + if self.unitsVar.get() == FRAMES: + frame = frame / self.fps + if self.dragMode: + # If user is clicking on the slider and is draging the bar, reset the global timer. + self.currTime = frame + self['actor'].pose(self.animName, + min(self.maxFrame, int(frame * self.fps))) + return + + def onRelease(self,frame): + ################################################################# + # onRelease(self, frame) + # disable the dragMode when user releases the bar on the slider. + ################################################################# + self.dragMode = False + return + + def onPress(self,frame): + ################################################################# + # onPress(self, frame) + # enable the dragMode when user press the bar on the slider. + ################################################################# + self.dragMode = True + return + + def resetAllToZero(self): + ################################################################# + # resetAllToZero(self) + # reset the global timer to zero and also move the slider to zero. + # This will also reset the actor to the zero frame of current animation + ################################################################# + self.currTime = 0.0 + self.gotoT(0) + return + + def resetAllToEnd(self): + ################################################################# + # resetAllToEnd(self) + # set the global timer to the end of current animation and + # also move the slider to the end. + ################################################################# + self.currTime = self.maxSeconds + self.gotoT(self.duration) + return + + def onDestroy(self, event): + if taskMgr.hasTaskNamed(self.id + '_UpdateTask'): + taskMgr.remove(self.id + '_UpdateTask') + self.ignore('DataH_loadFinish') + messenger.send('AW_close',[self.nodeName]) + ''' + If you have open any thing, please rewrite here! + ''' + pass + +class LoadAnimPanel(AppShell): + ################################################################# + # LoadAnimPanel(AppShell) + # This class will open a dialog to ask user to input names and + # file paths of animations + ################################################################# + # Override class variables + appname = 'Load Animation' + frameWidth = 575 + frameHeight = 200 + usecommandarea = 0 + usestatusarea = 0 + index = 0 + ## Anim name : File Path + + def __init__(self, aNode = None, parent = None, **kw): + INITOPT = Pmw.INITOPT + self.id = 'Load Animation '+ aNode.getName() + self.appname = self.id + self.animDic = {} + self.animList = [] + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + + self.frameHeight = 300 + self.nodeName = aNode.getName() + self.Actor = aNode + # Initialize the superclass + AppShell.__init__(self) + + # Execute option callbacks + self.initialiseoptions(LoadAnimPanel) + + def createInterface(self): + self.menuBar.destroy() + interior = self.interior() + mainFrame = Frame(interior) + self.inputZone = Pmw.Group(mainFrame, tag_text='File Setting') + self.inputZone.pack(fill='both',expand=1) + settingFrame = self.inputZone.interior() + Label(settingFrame,text='Anim Name').place(anchor=NW,x=60,y=5) + Label(settingFrame,text='File Path').place(anchor=NW,x=205,y=5) + self.AnimName_1 = self.createcomponent( + 'Anim Name List', (), None, + Pmw.ComboBox, (settingFrame,),label_text='Anim :', + labelpos = W, entry_width = 10, selectioncommand = self.selectAnim, + scrolledlist_items = self.animList) + self.AnimFile_1 = Pmw.EntryField(settingFrame,value='') + self.AnimFile_1.component('entry').config(width=20) + self.AnimName_1.place(anchor=NW,x=10,y=25) + self.AnimFile_1.place(anchor=NW,x=140,y=25) + self.Browse_1 = self.createcomponent( + 'File Browser1', (), None, + Button, (mainFrame,), + text = 'Browse...', + command = self.Browse_1) + self.Browse_1.place(anchor=NW,x=270,y=38) + + self.addIntoButton = self.createcomponent( + 'Load Add', (), None, + Button, (mainFrame,), + text = 'Add to Load', + command = self.addIntoList) + self.addIntoButton.place(anchor=NW,x=345,y=38) + + att_label = Label(mainFrame, font=('MSSansSerif', 10), + text= "Attention! Animations won't be loaded in before you press the 'OK' button below!") + att_label.place(anchor=NW,x=10,y=80) + + self.button_ok = Button(mainFrame, text="OK", command=self.ok_press,width=10) + self.button_ok.pack(fill=BOTH,expand=0,side=RIGHT) + + mainFrame.pack(expand = 1, fill = BOTH) + + + + def onDestroy(self, event): + messenger.send('AWL_close',[self.nodeName]) + ''' + If you have open any thing, please rewrite here! + ''' + pass + + def selectAnim(self,name): + ################################################################# + # selectAnim(self, name) + # This function will be called if user select an animation on the list. + ################################################################# + if name in self.animDic: + self.AnimFile_1.setvalue = self.animDic[name] + return + + def Browse_1(self): + ################################################################# + # Browse_1(self) + # when the browse button pused, this function will be called. + # Do nothing but open a file dialog for user to set the path to target file + # Then, set the path back to the entry on the panel. + ################################################################# + AnimFilename = askopenfilename( + defaultextension = '.egg', + filetypes = (('Egg Files', '*.egg'), + ('Bam Files', '*.bam'), + ('All files', '*')), + initialdir = '.', + title = 'File Path for Anim 1', + parent = self.parent) + if AnimFilename: + self.AnimFile_1.setvalue(AnimFilename) + return + + def addIntoList(self): + ################################################################# + # addIntoList(self) + # This function will be called each time when user click on the + # "Add to Load" button. This function will read the current data + # on the panel into a dictionary. then reset the list of the animation + # name list on this panel.(not the one in the animation panel...) + # + # This function won't load any animation.... + # + ################################################################# + name = self.AnimName_1.get() + self.animDic[name] = Filename.fromOsSpecific(self.AnimFile_1.getvalue()).getFullpath() + if name in self.animList: + pass + else: + self.animList.append(name) + self.AnimName_1.setlist(self.animList) + print self.animDic + return + + def ok_press(self): + ################################################################# + # ok_press(Self) + # This functiion will be called when user click on the "OK" + # button. This function will send a message along with the animation + # file we wish to load for this actor. + # Then, it will close the panel itself. + ################################################################# + messenger.send('AW_AnimationLoad',[self.Actor,self.animDic]) + #print self.animDic + self.quit() + return diff --git a/doc/SceneEditor/seBlendAnimPanel.py b/doc/SceneEditor/seBlendAnimPanel.py new file mode 100644 index 0000000000..61a7225853 --- /dev/null +++ b/doc/SceneEditor/seBlendAnimPanel.py @@ -0,0 +1,666 @@ +################################################################# +# collisionWindow.py +# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +################################################################# +# Import Tkinter, Pmw, and the floater code from this directory tree. +from AppShell import * +from TkGlobal import * +from tkSimpleDialog import askfloat +import string +import math +import types +import Task + +FRAMES = 0 +SECONDS = 1 + + +##################################################################################### +# BlendAnimPanel(AppShell) +# This Panel will allow user to blend tow animations +# that have already been loaded for this actor. +# user can play and manipulate this blended animation +# just like in the animation panel. And, they can save this blended animation. +##################################################################################### +class BlendAnimPanel(AppShell): + # Override class variables + appname = 'Blend Anim Panel' + frameWidth = 575 + frameHeight = 450 + usecommandarea = 0 + usestatusarea = 0 + index = 0 + dragMode = False + blendRatio = 0 + rateList= ['1/24.0', '0.1', '0.5', '1.0', '2.0', '5.0' , '10.0'] + enableBlend = False + currentBlendName = None + + + def __init__(self, aNode = None, blendDict={}, parent = None, **kw): + INITOPT = Pmw.INITOPT + self.id = 'BlendAnimPanel '+ aNode.getName() + self.appname = self.id + self.actorNode = aNode + self.blendDict = blendDict.copy() + if len(blendDict)>0: + self.blendList = blendDict.keys() + else: + self.blendList = [] + optiondefs = ( + ('title', self.appname, None), + ('actor', aNode, None), + ('animList', [], None), + ('blendAnimList', self.blendList, None), + ) + self.defineoptions(kw, optiondefs) + + self.id = 'Blend AnimPanel '+ aNode.getName() + self.nodeName = aNode.getName() + # Initialize the superclass + AppShell.__init__(self) + + # Execute option callbacks + self.initialiseoptions(BlendAnimPanel) + + self.currTime = 0.0 + self.animNameA = None + self.animNameB = None + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + def createInterface(self): + # Handle to the toplevels interior + interior = self.interior() + self.menuBar.destroy() + + # show the actor's name + actorFrame = Frame(interior) + name_label = Label(actorFrame, text= self.nodeName,font=('MSSansSerif', 14), + relief = SUNKEN, borderwidth=3) + name_label.pack(side = TOP, expand = False) + actorFrame.pack(side = TOP, expand = False, fill = X) + + # Create a frame to show is there any ore-blended animation and save, edit, rename button. + group = Pmw.Group(interior, tag_pyclass=None) + actorFrame = group.interior() + group.pack(side = TOP, expand = False, fill = X) + + Label(actorFrame, text= "Blended:", font=('MSSansSerif', 10)).pack(side=LEFT) + self.blendAnimEntry = self.createcomponent( + 'Blended Animation', (), None, + Pmw.ComboBox, (actorFrame,), + labelpos = W, entry_width = 20, selectioncommand = self.setBlendAnim, + scrolledlist_items = self['blendAnimList']) + self.blendAnimEntry.pack(side=LEFT) + + Label(actorFrame, text= " ", font=('MSSansSerif', 10)).pack(side=LEFT) + + button = Button(actorFrame, text="Save", font=('MSSansSerif', 10),width = 12, + command = self.saveButtonPushed).pack(side=LEFT) + button = Button(actorFrame, text="Remove", font=('MSSansSerif', 10),width = 12, + command = self.removeButtonPushed).pack(side=LEFT) + button = Button(actorFrame, text="Rename", font=('MSSansSerif', 10),width = 12, + command = self.renameButtonPushed).pack(side=LEFT) + + actorFrame.pack(side = TOP, expand = False, fill = X) + + # Create a frame to hold all the animation setting + group = Pmw.Group(interior, tag_pyclass=None) + actorFrame = group.interior() + group.pack(side = TOP, expand = False, fill = X) + Label(actorFrame, text= "Animation A:", font=('MSSansSerif', 10)).pack(side=LEFT) + self['animList'] = self['actor'].getAnimNames() + self.AnimEntryA = self.createcomponent( + 'AnimationMenuA', (), None, + Pmw.ComboBox, (actorFrame,), + labelpos = W, entry_width = 20, entry_state = DISABLED, + selectioncommand = lambda name, a = 'a' : self.setAnimation(name, AB=a), + scrolledlist_items = self['animList']) + self.AnimEntryA.pack(side=LEFT) + + Label(actorFrame, text= " ", font=('MSSansSerif', 10)).pack(side=LEFT,) + Label(actorFrame, text= "Animation B:", font=('MSSansSerif', 10)).pack(side=LEFT) + self['animList'] = self['actor'].getAnimNames() + self.AnimEntryB = self.createcomponent( + 'AnimationMenuB', (), None, + Pmw.ComboBox, (actorFrame,), + labelpos = W, entry_width = 20, entry_state = DISABLED, + selectioncommand = lambda name, a = 'b' : self.setAnimation(name, AB=a), + scrolledlist_items = self['animList']) + self.AnimEntryB.pack(side=LEFT) + actorFrame.pack(side = TOP, expand = False, fill = X) + + ### Blend Enable checkbox + actorFrame = Frame(interior, relief = SUNKEN, bd = 1) + Label(actorFrame, text= "Enable Blending:", font=('MSSansSerif', 10)).pack(side=LEFT,) + self.blendVar = IntVar() + self.blendVar.set(0) + self.blendButton = self.createcomponent( + 'blendButton', (), None, + Checkbutton, (actorFrame,), + variable = self.blendVar, + command = self.toggleBlend) + self.blendButton.pack(side=LEFT) + actorFrame.pack(side = TOP, expand = False, fill = X) + + ## Ratio control + actorFrame = Frame(interior) + frameFrame = Frame(actorFrame, relief = SUNKEN, bd = 1) + minRatioLabel = self.createcomponent( + 'minRatioLabel', (), 'sLabel', + Label, (frameFrame,), + text = 0.00) + minRatioLabel.pack(side = LEFT) + + self.ratioControl = self.createcomponent( + 'ratio', (), None, + Scale, (frameFrame,), + from_ = 0.0, to = 1.0, resolution = 0.01, + command = self.setRatio, length = 500, + orient = HORIZONTAL, showvalue = 1) + self.ratioControl.pack(side = LEFT, expand = 1) + self.ratioControl.set(1.0) + + self.maxRatioLabel = self.createcomponent( + 'maxRatioLabel', (), 'sLabel', + Label, (frameFrame,), + text = 1.00) + self.maxRatioLabel.pack(side = LEFT) + frameFrame.pack(side = LEFT, expand = 1, fill = X) + actorFrame.pack(side = TOP, expand = True, fill = X) + + ################################################################################### + ################################################################################### + actorFrame = Frame(interior) + Label(actorFrame, text= "Play Rate:", font=('MSSansSerif', 10)).pack(side=LEFT) + self.playRateEntry = self.createcomponent( + 'playRateMenu', (), None, + Pmw.ComboBox, (actorFrame,), + labelpos = W, entry_width = 20, selectioncommand = self.setPlayRate, + scrolledlist_items = self.rateList) + self.playRateEntry.pack(side=LEFT) + self.playRateEntry.selectitem('1.0') + + ### Loop checkbox + Label(actorFrame, text= " ", font=('MSSansSerif', 10)).pack(side=LEFT,) + Label(actorFrame, text= "Loop:", font=('MSSansSerif', 10)).pack(side=LEFT,) + + self.loopVar = IntVar() + self.loopVar.set(0) + self.loopButton = self.createcomponent( + 'loopButton', (), None, + Checkbutton, (actorFrame,), + variable = self.loopVar) + self.loopButton.pack(side=LEFT) + + actorFrame.pack(side = TOP, expand = True, fill = X) + + + + ### Display Frames/Seconds + actorFrame = Frame(interior) + + Label(actorFrame, text= "Frame/Second:", font=('MSSansSerif', 10)).pack(side=LEFT) + + self.unitsVar = IntVar() + self.unitsVar.set(FRAMES) + self.displayButton = self.createcomponent( + 'displayButton', (), None, + Checkbutton, (actorFrame,), + command = self.updateDisplay, + variable = self.unitsVar) + self.displayButton.pack(side=LEFT) + + actorFrame.pack(side = TOP, expand = True, fill = X) + + ## scale control + actorFrame = Frame(interior) + frameFrame = Frame(actorFrame, relief = SUNKEN, bd = 1) + self.minLabel = self.createcomponent( + 'minLabel', (), 'sLabel', + Label, (frameFrame,), + text = 0) + self.minLabel.pack(side = LEFT) + + self.frameControl = self.createcomponent( + 'scale', (), None, + Scale, (frameFrame,), + from_ = 0, to = 24, resolution = 1.0, + command = self.goTo, length = 500, + orient = HORIZONTAL, showvalue = 1) + self.frameControl.pack(side = LEFT, expand = 1) + self.frameControl.bind('', self.onPress) + self.frameControl.bind('', self.onRelease) + + self.maxLabel = self.createcomponent( + 'maxLabel', (), 'sLabel', + Label, (frameFrame,), + text = 24) + self.maxLabel.pack(side = LEFT) + frameFrame.pack(side = LEFT, expand = 1, fill = X) + actorFrame.pack(side = TOP, expand = True, fill = X) + + ## button contorl + actorFrame = Frame(interior) + ButtomFrame = Frame(actorFrame, relief = SUNKEN, bd = 1,borderwidth=5) + self.toStartButton = self.createcomponent( + 'toStart', (), None, + Button, (ButtomFrame,), + text = '<<', + width = 8, + command = self.resetAllToZero) + self.toStartButton.pack(side = LEFT, expand = 1, fill = X) + + self.playButton = self.createcomponent( + 'playButton', (), None, + Button, (ButtomFrame,), + text = 'Play', width = 8, + command = self.play) + self.playButton.pack(side = LEFT, expand = 1, fill = X) + + self.stopButton = self.createcomponent( + 'stopButton', (), None, + Button, (ButtomFrame,), + text = 'Stop', width = 8, state=DISABLED, + command = self.stop) + self.stopButton.pack(side = LEFT, expand = 1, fill = X) + + self.toEndButton = self.createcomponent( + 'toEnd', (), None, + Button, (ButtomFrame,), + text = '>>', + width = 8, + command = self.resetAllToEnd) + self.toEndButton.pack(side = LEFT, expand = 1, fill = X) + + ButtomFrame.pack(side = TOP, expand = True, fill = X) + actorFrame.pack(expand = 1, fill = BOTH) + + def updateList(self): + ################################################################# + # updateList(self) + # This will reset the list of all animations that this actor has + # to the animation entry A and B. + ################################################################# + self['animList'] = self['actor'].getAnimNames() + animL = self['actor'].getAnimNames() + self.AnimEntryA.setlist(animL) + self.AnimEntryB.setlist(animL) + + def play(self): + ################################################################# + # play(self) + # It works pretty much like what we have in the Animation Panel. + # The only different now is that we set two "pose" here. + # When you do the blending animation by setPose, you don't have + # to set them simultaneously. + ################################################################# + self.animNameA = self.AnimEntryA.get() + self.animNameB = self.AnimEntryB.get() + if (self.animNameA in self['animList'])and(self.animNameB in self['animList']): + self.playButton.config(state=DISABLED) + self.lastT = globalClock.getFrameTime() + taskMgr.add(self.playTask, self.id + '_UpdateTask') + self.stopButton.config(state=NORMAL) + else: + print '----Illegal Animaion name!!', self.animNameA + ', '+ self.animNameB + return + + def playTask(self, task): + ################################################################# + # playTask(self, task) + # see play(self) + ################################################################# + fLoop = self.loopVar.get() + currT = globalClock.getFrameTime() + deltaT = currT - self.lastT + self.lastT = currT + if self.dragMode: + return Task.cont + self.currTime = self.currTime + deltaT + if (self.currTime > self.maxSeconds): + if fLoop: + self.currTime = self.currTime%self.duration + self.gotoT(self.currTime) + else: + self.currTime = 0.0 + self.gotoT(0.0) + self.playButton.config(state=NORMAL) + self.stopButton.config(state=DISABLED) + return Task.done + else: + self.gotoT(self.currTime) + return Task.cont + + def stop(self): + ################################################################# + # stop(self) + # see play(self) + ################################################################# + taskMgr.remove(self.id + '_UpdateTask') + self.playButton.config(state=NORMAL) + self.stopButton.config(state=DISABLED) + return + + def setAnimation(self, animation, AB = 'a'): + ################################################################# + # setAnimation(self, animation, AB = 'a') + # see play(self) + ################################################################# + print 'OK!!!' + if AB == 'a': + if self.animNameA != None: + self['actor'].setControlEffect(self.animNameA, 1.0, 'modelRoot','lodRoot') + self.animNameA = self.AnimEntryA.get() + else: + if self.animNameB != None: + self['actor'].setControlEffect(self.animNameB, 1.0, 'modelRoot','lodRoot') + self.animNameB = self.AnimEntryB.get() + self.currTime = 0.0 + self.frameControl.set(0) + self.updateDisplay() + self.setRatio(self.blendRatio) + return + + def setPlayRate(self,rate): + ################################################################# + # setPlayRate(self,rate) + # see play(self) + ################################################################# + self.animNameA = self.AnimEntryA.get() + if self.animNameA in self['animList']: + self['actor'].setPlayRate(eval(rate), self.animNameA) + self.updateDisplay() + if self.animNameB in self['animList']: + self['actor'].setPlayRate(eval(rate), self.animNameB) + self.updateDisplay() + return + + def updateDisplay(self): + ################################################################# + # updateDisplay(self) + # see play(self) + ################################################################# + if not (self.animNameA in self['animList']): + return + self.fps = self['actor'].getFrameRate(self.animNameA) + self.duration = self['actor'].getDuration(self.animNameA) + self.maxFrame = self['actor'].getNumFrames(self.animNameA) - 1 + if not (self.animNameB in self['animList']): + return + if self.duration > self['actor'].getDuration(self.animNameB): + self.duration = self['actor'].getDuration(self.animNameB) + if self.maxFrame > self['actor'].getNumFrames(self.animNameB) - 1: + self.maxFrame = self['actor'].getNumFrames(self.animNameB) - 1 + self.maxSeconds = self.duration + if self.unitsVar.get() == FRAMES: + fromFrame = 0 + toFrame = self.maxFrame + self.minLabel['text'] = fromFrame + self.maxLabel['text'] = toFrame + self.frameControl.configure(from_ = fromFrame, + to = toFrame, + resolution = 1.0) + else: + self.minLabel['text'] = '0.0' + self.maxLabel['text'] = "%.2f" % self.duration + self.frameControl.configure(from_ = 0.0, + to = self.duration, + resolution = 0.01) + + def gotoT(self,time): + ################################################################# + # gotoT(self,time) + # see play(self) + ################################################################# + if self.unitsVar.get() == FRAMES: + self.frameControl.set(time * self.fps) + else: + self.frameControl.set(time) + return + + def goTo(self,frame): + ################################################################# + # goTo(self,frame) + # see play(self) + ################################################################# + if (self.animNameA in self['animList'])and(self.animNameB in self['animList']): + # Convert scale value to float + frame = string.atof(frame) + # Now convert t to seconds for offset calculations + if self.unitsVar.get() == FRAMES: + frame = frame / self.fps + if self.dragMode: + self.currTime = frame + self['actor'].pose(self.animNameA, + min(self.maxFrame, int(frame * self.fps))) + self['actor'].pose(self.animNameB, + min(self.maxFrame, int(frame * self.fps))) + return + + def onRelease(self,frame): + ################################################################# + # onRelease(self,frame) + # see play(self) + ################################################################# + self.dragMode = False + return + + def onPress(self,frame): + ################################################################# + # onPress(self,frame) + # see play(self) + ################################################################# + self.dragMode = True + return + + def resetAllToZero(self): + ################################################################# + # resetAllToZero(self) + # see play(self) + ################################################################# + self.currTime = 0.0 + self.gotoT(0) + return + + def resetAllToEnd(self): + ################################################################# + # resetAllToEnd(self) + # see play(self) + ################################################################# + self.currTime = self.maxSeconds + self.gotoT(self.duration) + return + + def toggleBlend(self): + ################################################################# + # toggleBlend(self) + # This function will enable the blending option for the actor. + # and call set ratio function to set the blending animation mixing in + # current ratio. + # + # This blending enable will not be keep when you close the window! + # + ################################################################# + if self.blendVar.get(): + self.enableBlend = True + self['actor'].enableBlend() + self.setRatio(self.blendRatio) + else: + self.enableBlend = False + self['actor'].disableBlend() + return + + def setRatio(self, ratio): + ################################################################# + # setRatio(self, ratio) + # callback funtion + # This one will be called each time when user drag the blend ratio + # slider on the panel. This will set the blening ratio to both animation. + # (Which is "setControlEffect") + ################################################################# + self.blendRatio = float(ratio) + if self.enableBlend: + if self.animNameA in self['animList']: + self['actor'].setControlEffect(self.animNameA, self.blendRatio, 'modelRoot','lodRoot') + if self.animNameB in self['animList']: + self['actor'].setControlEffect(self.animNameB, 1-self.blendRatio, 'modelRoot','lodRoot') + return + + def setBlendAnim(self, name): + ################################################################# + # setBlendAnim(self, name) + # This function will be called each time when user try to select + # a existing blending animation from the comboBox on the panel + # This function will re-set every varaibles on the panel to what + # it should be. For example, when user choose blending anim "R," + # which was blended by anim "a" and "b" with ratio "c," + # then this function will set Animation A to "a" and animation B + # to "b" and set the ratio slider to "c" position. + ################################################################# + if self.blendDict.has_key(name): + self.currentBlendName = name + animA = self.blendDict[name][0] + animB = self.blendDict[name][1] + ratio = self.blendDict[name][2] + self.AnimEntryA.selectitem(animA) + self.AnimEntryB.selectitem(animB) + self.setAnimation(animA, AB = 'a') + self.setAnimation(animB, AB = 'b') + self.ratioControl.set(ratio) + return + + def setBlendAnimList(self, dict, select=False): + ################################################################# + # setBlendAnimList(self, dict, select=False) + # This function will be called when we need to reset the dropdown list + # of "Blend Anim." + # About "selec" option, this now is mainly used when we remove + # a blended animation from the actor. When it has been specified to True, + # the function will not only reset the list, but will also automatically + # select one from the top of list, if it is not empty. + ################################################################# + self.blendDict.clear() + del self.blendDict + self.blendDict = dict.copy() + print self.blendDict + if len(self.blendDict)>0: + self.blendList = self.blendDict.keys() + else: + self.blendList = [] + self.blendAnimEntry.setlist(self.blendList) + if select: + if len(self.blendList)>0: + self.blendAnimEntry.selectitem(self.blendList[0]) + self.setBlendAnim(self.blendList[0]) + self.currentBlendName = self.blendList[0] + else: + self.blendAnimEntry.clear() + self.currentBlendName = None + return + + def saveButtonPushed(self): + ################################################################# + # saveButtonPushed(self) + # This function will be called when user clicked on the "Save" button + # This functiont will collect all data on the panel and send them with + # a message to sceneEditor to save the current blending animation + # into the dataHolder. + ################################################################# + name = self.blendAnimEntry.get() + if name=='': + Pmw.MessageDialog(None, title='Caution!', + message_text = 'You have to give the blending animation a name first!', + iconpos='s', + defaultbutton = 'Close' + ) + return + elif (not(self.animNameA in self['animList']))or(not(self.animNameB in self['animList'])): + Pmw.MessageDialog(None, title='Caution!', + message_text = 'The Animations you have selected are not exist!', + iconpos='s', + defaultbutton = 'Close' + ) + return + else: + messenger.send('BAW_saveBlendAnim', [self['actor'].getName(), + name, + self.animNameA, + self.animNameB, + self.blendRatio]) + self.currentBlendName = name + return + + def removeButtonPushed(self): + ################################################################# + # removeButtonPushed(self) + # remove the current seleted blended animation from the actor. + # This will send out a message to sceneEditor to delete the data inside + # the dataHolder and then reset the list of here. + ################################################################# + name = self.blendAnimEntry.get() + messenger.send('BAW_removeBlendAnim', [self['actor'].getName(),name]) + return + + def renameButtonPushed(self): + ################################################################# + # renameButtonPushed(self) + # this function will be called when user click on the "Rename" button. + # This function will collect all data on the panel and send them out + # with a message to sceneEditor to rename and re-save all setting about + # current animation. + ################################################################# + oName = self.currentBlendName + name = self.blendAnimEntry.get() + if self.currentBlendName == None: + Pmw.MessageDialog(None, title='Caution!', + message_text = "You haven't select any blended animation!!", + iconpos='s', + defaultbutton = 'Close' + ) + return + elif name=='': + Pmw.MessageDialog(None, title='Caution!', + message_text = 'You have to give the blending animation a name first!', + iconpos='s', + defaultbutton = 'Close' + ) + return + elif (not(self.animNameA in self['animList']))or(not(self.animNameB in self['animList'])): + Pmw.MessageDialog(None, title='Caution!', + message_text = 'The Animations you have selected are not exist!', + iconpos='s', + defaultbutton = 'Close' + ) + return + else: + messenger.send('BAW_renameBlendAnim', [self['actor'].getName(), + name, + oName, + self.animNameA, + self.animNameB, + self.blendRatio] + ) + self.currentBlendName = name + return + + def onDestroy(self, event): + ################################################################# + # onDestroy(self, event) + # This function will be call when user try to close the window. + # In here we will stop all tasks we have opend and disable the + # blend setting of actor. + # If we didn't disable the blend option, the next time you play + # the animation via animation panel will cause some error. + ################################################################# + if taskMgr.hasTaskNamed(self.id + '_UpdateTask'): + taskMgr.remove(self.id + '_UpdateTask') + messenger.send('BAW_close',[self.nodeName]) + self.actorNode.setControlEffect(self.animNameA, 1.0, 'modelRoot','lodRoot') + self.actorNode.setControlEffect(self.animNameB, 1.0, 'modelRoot','lodRoot') + self.actorNode.disableBlend() + ''' + If you have open any thing, please rewrite here! + ''' + pass diff --git a/doc/SceneEditor/seCameraControl.py b/doc/SceneEditor/seCameraControl.py new file mode 100644 index 0000000000..7616056419 --- /dev/null +++ b/doc/SceneEditor/seCameraControl.py @@ -0,0 +1,670 @@ +################################################################# +# seCameraControl.py +# Originally from DirectCameraControl.py +# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +# +# We didn't change anything essential. +# Just because we customized the seSession from DirectSession, +# So we need related files can follow the change. +# However, we don't want to change anything inside the original directool +# to let them can work with our scene editor. +# (If we do change original directools, it will force user has to install the latest version of OUR Panda) +# +################################################################# +from PandaObject import * +from DirectUtil import * +from seGeometry import * +from DirectGlobals import * +import Task + +CAM_MOVE_DURATION = 1.2 +COA_MARKER_SF = 0.0075 +Y_AXIS = Vec3(0,1,0) + +class DirectCameraControl(PandaObject): + def __init__(self): + # Create the grid + self.startT = 0.0 + self.startF = 0 + self.orthoViewRoll = 0.0 + self.lastView = 0 + self.coa = Point3(0,100,0) + self.coaMarker = loader.loadModel('models/misc/sphere') + self.coaMarker.setName('DirectCameraCOAMarker') + self.coaMarker.setTransparency(1) + self.coaMarker.setColor(1,0,0,0) + self.coaMarker.setPos(0,100,0) + useDirectRenderStyle(self.coaMarker) + self.coaMarkerPos = Point3(0) + self.fLockCOA = 0 + self.nullHitPointCount = 0 + self.cqEntries = [] + self.coaMarkerRef = SEditor.group.attachNewNode('coaMarkerRef') + self.camManipRef = SEditor.group.attachNewNode('camManipRef') + t = CAM_MOVE_DURATION + self.actionEvents = [ + ['DIRECT-mouse2', self.mouseFlyStart], + ['DIRECT-mouse2Up', self.mouseFlyStop], + ] + self.keyEvents = [ + ['c', self.centerCamIn, 0.5], + ['f', self.fitOnWidget], + ['h', self.homeCam], + ['shift-v', self.toggleMarkerVis], + ['m', self.moveToFit], + ['n', self.pickNextCOA], + ['u', self.orbitUprightCam], + ['shift-u', self.uprightCam], + [`1`, self.spawnMoveToView, 1], + [`2`, self.spawnMoveToView, 2], + [`3`, self.spawnMoveToView, 3], + [`4`, self.spawnMoveToView, 4], + [`5`, self.spawnMoveToView, 5], + [`6`, self.spawnMoveToView, 6], + [`7`, self.spawnMoveToView, 7], + [`8`, self.spawnMoveToView, 8], + ['9', self.swingCamAboutWidget, -90.0, t], + ['0', self.swingCamAboutWidget, 90.0, t], + ['`', self.removeManipulateCameraTask], + ['=', self.zoomCam, 0.5, t], + ['+', self.zoomCam, 0.5, t], + ['-', self.zoomCam, -2.0, t], + ['_', self.zoomCam, -2.0, t], + ] + + def toggleMarkerVis(self): + if SEditor.cameraControl.coaMarker.isHidden(): + SEditor.cameraControl.coaMarker.show() + else: + SEditor.cameraControl.coaMarker.hide() + + def mouseFlyStart(self, modifiers): + # Record undo point + SEditor.pushUndo([SEditor.camera]) + # Where are we in the display region? + if ((abs(SEditor.dr.mouseX) < 0.9) and (abs(SEditor.dr.mouseY) < 0.9)): + # MOUSE IS IN CENTRAL REGION + # Hide the marker for this kind of motion + self.coaMarker.hide() + # Record time of start of mouse interaction + self.startT= globalClock.getFrameTime() + self.startF = globalClock.getFrameCount() + # Start manipulation + self.spawnXZTranslateOrHPanYZoom() + # END MOUSE IN CENTRAL REGION + else: + if ((abs(SEditor.dr.mouseX) > 0.9) and + (abs(SEditor.dr.mouseY) > 0.9)): + # Mouse is in corners, spawn roll task + self.spawnMouseRollTask() + else: + # Mouse is in outer frame, spawn mouseRotateTask + self.spawnMouseRotateTask() + + def mouseFlyStop(self): + taskMgr.remove('manipulateCamera') + stopT = globalClock.getFrameTime() + deltaT = stopT - self.startT + stopF = globalClock.getFrameCount() + deltaF = stopF - self.startF + if (deltaT <= 0.25) or (deltaF <= 1): + # Check for a hit point based on + # current mouse position + # Allow intersection with unpickable objects + # And then spawn task to determine mouse mode + # Don't intersect with hidden or backfacing objects + skipFlags = SKIP_HIDDEN | SKIP_BACKFACE + # Skip camera (and its children), unless control key is pressed + skipFlags |= SKIP_CAMERA * (1 - base.getControl()) + self.computeCOA(SEditor.iRay.pickGeom(skipFlags = skipFlags)) + # Record reference point + self.coaMarkerRef.iPosHprScale(base.cam) + # Record entries + self.cqEntries = [] + for i in range(SEditor.iRay.getNumEntries()): + self.cqEntries.append(SEditor.iRay.getEntry(i)) + # Show the marker + self.coaMarker.show() + # Resize it + self.updateCoaMarkerSize() + + def spawnXZTranslateOrHPanYZoom(self): + # Kill any existing tasks + taskMgr.remove('manipulateCamera') + # Spawn the new task + t = Task.Task(self.XZTranslateOrHPanYZoomTask) + # For HPanYZoom + t.zoomSF = Vec3(self.coaMarker.getPos(SEditor.camera)).length() + taskMgr.add(t, 'manipulateCamera') + + def spawnXZTranslateOrHPPan(self): + # Kill any existing tasks + taskMgr.remove('manipulateCamera') + # Spawn new task + taskMgr.add(self.XZTranslateOrHPPanTask, + 'manipulateCamera') + + def spawnXZTranslate(self): + # Kill any existing tasks + taskMgr.remove('manipulateCamera') + # Spawn new task + taskMgr.add(self.XZTranslateTask, 'manipulateCamera') + + def spawnHPanYZoom(self): + # Kill any existing tasks + taskMgr.remove('manipulateCamera') + # Spawn new task + t = Task.Task(self.HPanYZoomTask) + t.zoomSF = Vec3(self.coaMarker.getPos(SEditor.camera)).length() + taskMgr.add(t, 'manipulateCamera') + + def spawnHPPan(self): + # Kill any existing tasks + taskMgr.remove('manipulateCamera') + # Spawn new task + taskMgr.add(self.HPPanTask, 'manipulateCamera') + + def XZTranslateOrHPanYZoomTask(self, state): + if SEditor.fShift: + return self.XZTranslateTask(state) + else: + return self.HPanYZoomTask(state) + + def XZTranslateOrHPPanTask(self, state): + if SEditor.fShift: + # Panning action + return self.HPPanTask(state) + else: + # Translation action + return self.XZTranslateTask(state) + + def XZTranslateTask(self,state): + coaDist = Vec3(self.coaMarker.getPos(SEditor.camera)).length() + xlateSF = (coaDist / SEditor.dr.near) + SEditor.camera.setPos(SEditor.camera, + (-0.5 * SEditor.dr.mouseDeltaX * + SEditor.dr.nearWidth * + xlateSF), + 0.0, + (-0.5 * SEditor.dr.mouseDeltaY * + SEditor.dr.nearHeight * + xlateSF)) + return Task.cont + + def HPanYZoomTask(self,state): + if SEditor.fControl: + moveDir = Vec3(self.coaMarker.getPos(SEditor.camera)) + # If marker is behind camera invert vector + if moveDir[1] < 0.0: + moveDir.assign(moveDir * -1) + moveDir.normalize() + else: + moveDir = Vec3(Y_AXIS) + moveDir.assign(moveDir * (-1.0 * SEditor.dr.mouseDeltaY * + state.zoomSF)) + if SEditor.dr.mouseDeltaY > 0.0: + moveDir.setY(moveDir[1] * 1.0) + SEditor.camera.setPosHpr(SEditor.camera, + moveDir[0], + moveDir[1], + moveDir[2], + (0.5 * SEditor.dr.mouseDeltaX * + SEditor.dr.fovH), + 0.0, 0.0) + return Task.cont + + def HPPanTask(self, state): + SEditor.camera.setHpr(SEditor.camera, + (0.5 * SEditor.dr.mouseDeltaX * + SEditor.dr.fovH), + (-0.5 * SEditor.dr.mouseDeltaY * + SEditor.dr.fovV), + 0.0) + return Task.cont + + def spawnMouseRotateTask(self): + # Kill any existing tasks + taskMgr.remove('manipulateCamera') + # Set at markers position in render coordinates + self.camManipRef.setPos(self.coaMarkerPos) + self.camManipRef.setHpr(SEditor.camera, ZERO_POINT) + t = Task.Task(self.mouseRotateTask) + if abs(SEditor.dr.mouseX) > 0.9: + t.constrainedDir = 'y' + else: + t.constrainedDir = 'x' + taskMgr.add(t, 'manipulateCamera') + + def mouseRotateTask(self, state): + # If moving outside of center, ignore motion perpendicular to edge + if ((state.constrainedDir == 'y') and (abs(SEditor.dr.mouseX) > 0.9)): + deltaX = 0 + deltaY = SEditor.dr.mouseDeltaY + elif ((state.constrainedDir == 'x') and (abs(SEditor.dr.mouseY) > 0.9)): + deltaX = SEditor.dr.mouseDeltaX + deltaY = 0 + else: + deltaX = SEditor.dr.mouseDeltaX + deltaY = SEditor.dr.mouseDeltaY + if SEditor.fShift: + SEditor.camera.setHpr(SEditor.camera, + (deltaX * SEditor.dr.fovH), + (-deltaY * SEditor.dr.fovV), + 0.0) + self.camManipRef.setPos(self.coaMarkerPos) + self.camManipRef.setHpr(SEditor.camera, ZERO_POINT) + else: + wrt = SEditor.camera.getTransform( self.camManipRef ) + self.camManipRef.setHpr(self.camManipRef, + (-1 * deltaX * 180.0), + (deltaY * 180.0), + 0.0) + SEditor.camera.setTransform(self.camManipRef, wrt) + return Task.cont + + def spawnMouseRollTask(self): + # Kill any existing tasks + taskMgr.remove('manipulateCamera') + # Set at markers position in render coordinates + self.camManipRef.setPos(self.coaMarkerPos) + self.camManipRef.setHpr(SEditor.camera, ZERO_POINT) + t = Task.Task(self.mouseRollTask) + t.coaCenter = getScreenXY(self.coaMarker) + t.lastAngle = getCrankAngle(t.coaCenter) + # Store the camera/manipRef offset transform + t.wrt = SEditor.camera.getTransform( self.camManipRef ) + taskMgr.add(t, 'manipulateCamera') + + def mouseRollTask(self, state): + wrt = state.wrt + angle = getCrankAngle(state.coaCenter) + deltaAngle = angle - state.lastAngle + state.lastAngle = angle + if base.config.GetBool('temp-hpr-fix',0): + self.camManipRef.setHpr(self.camManipRef, 0, 0, deltaAngle) + else: + self.camManipRef.setHpr(self.camManipRef, 0, 0, -deltaAngle) + SEditor.camera.setTransform(self.camManipRef, wrt) + return Task.cont + + def lockCOA(self): + self.fLockCOA = 1 + SEditor.message('COA Lock On') + + def unlockCOA(self): + self.fLockCOA = 0 + SEditor.message('COA Lock Off') + + def toggleCOALock(self): + self.fLockCOA = 1 - self.fLockCOA + if self.fLockCOA: + SEditor.message('COA Lock On') + else: + SEditor.message('COA Lock Off') + + def pickNextCOA(self): + """ Cycle through collision handler entries """ + if self.cqEntries: + # Get next entry and rotate entries + entry = self.cqEntries[0] + self.cqEntries = self.cqEntries[1:] + self.cqEntries[:1] + # Filter out object's under camera + nodePath = entry.getIntoNodePath() + if SEditor.camera not in nodePath.getAncestry(): + # Compute new hit point + hitPt = entry.getFromIntersectionPoint() + # Move coa marker to new point + self.updateCoa(hitPt, ref = self.coaMarkerRef) + else: + # Remove offending entry + self.cqEntries = self.cqEntries[:-1] + self.pickNextCOA() + + def computeCOA(self, entry): + coa = Point3(0) + dr = SEditor.drList.getCurrentDr() + if self.fLockCOA: + # COA is locked, use existing point + # Use existing point + coa.assign(self.coaMarker.getPos(SEditor.camera)) + # Reset hit point count + self.nullHitPointCount = 0 + elif entry: + # Got a hit point (hit point is in camera coordinates) + # Set center of action + hitPt = entry.getFromIntersectionPoint() + hitPtDist = Vec3(hitPt).length() + coa.assign(hitPt) + # Handle case of bad coa point (too close or too far) + if ((hitPtDist < (1.1 * dr.near)) or + (hitPtDist > dr.far)): + # Just use existing point + coa.assign(self.coaMarker.getPos(SEditor.camera)) + # Reset hit point count + self.nullHitPointCount = 0 + else: + # Increment null hit point count + self.nullHitPointCount = (self.nullHitPointCount + 1) % 7 + # No COA lock and no intersection point + # Use a point out in front of camera + # Distance to point increases on multiple null hit points + # MRM: Would be nice to be able to control this + # At least display it + dist = pow(10.0, self.nullHitPointCount) + SEditor.message('COA Distance: ' + `dist`) + coa.set(0,dist,0) + # Compute COA Dist + coaDist = Vec3(coa - ZERO_POINT).length() + if coaDist < (1.1 * dr.near): + coa.set(0,100,0) + coaDist = 100 + # Update coa and marker + self.updateCoa(coa, coaDist = coaDist) + + def updateCoa(self, ref2point, coaDist = None, ref = None): + self.coa.set(ref2point[0], ref2point[1], ref2point[2]) + if not coaDist: + coaDist = Vec3(self.coa - ZERO_POINT).length() + # Place the marker in render space + if ref == None: + # KEH: use the current display region + # ref = base.cam + ref = SEditor.drList.getCurrentDr().cam + self.coaMarker.setPos(ref, self.coa) + pos = self.coaMarker.getPos() + self.coaMarker.setPosHprScale(pos, Vec3(0), Vec3(1)) + # Resize it + self.updateCoaMarkerSize(coaDist) + # Record marker pos in render space + self.coaMarkerPos.assign(self.coaMarker.getPos()) + + def updateCoaMarkerSizeOnDeath(self, state): + # Needed because tasks pass in state as first arg + self.updateCoaMarkerSize() + + def updateCoaMarkerSize(self, coaDist = None): + if not coaDist: + coaDist = Vec3(self.coaMarker.getPos( SEditor.camera )).length() + # KEH: use current display region for fov + # sf = COA_MARKER_SF * coaDist * math.tan(deg2Rad(SEditor.dr.fovV)) + sf = COA_MARKER_SF * coaDist * math.tan(deg2Rad(SEditor.drList.getCurrentDr().fovV)) + if sf == 0.0: + sf = 0.1 + self.coaMarker.setScale(sf) + # Lerp color to fade out + self.coaMarker.lerpColor(VBase4(1,0,0,1), VBase4(1,0,0,0), 3.0, + task = 'fadeAway') + + def homeCam(self): + # Record undo point + SEditor.pushUndo([SEditor.camera]) + SEditor.camera.reparentTo(render) + SEditor.camera.clearMat() + # Resize coa marker + self.updateCoaMarkerSize() + + def uprightCam(self): + taskMgr.remove('manipulateCamera') + # Record undo point + SEditor.pushUndo([SEditor.camera]) + # Pitch camera till upright + currH = SEditor.camera.getH() + SEditor.camera.lerpHpr(currH, 0, 0, + CAM_MOVE_DURATION, + other = render, + blendType = 'easeInOut', + task = 'manipulateCamera') + + def orbitUprightCam(self): + taskMgr.remove('manipulateCamera') + # Record undo point + SEditor.pushUndo([SEditor.camera]) + # Transform camera z axis to render space + mCam2Render = Mat4() + mCam2Render.assign(SEditor.camera.getMat(render)) + zAxis = Vec3(mCam2Render.xformVec(Z_AXIS)) + zAxis.normalize() + # Compute rotation angle needed to upright cam + orbitAngle = rad2Deg(math.acos(CLAMP(zAxis.dot(Z_AXIS),-1,1))) + # Check angle + if orbitAngle < 0.1: + # Already upright + return + # Compute orthogonal axis of rotation + rotAxis = Vec3(zAxis.cross(Z_AXIS)) + rotAxis.normalize() + # Find angle between rot Axis and render X_AXIS + rotAngle = rad2Deg(math.acos(CLAMP(rotAxis.dot(X_AXIS),-1,1))) + # Determine sign or rotation angle + if rotAxis[1] < 0: + rotAngle *= -1 + # Position ref CS at coa marker with xaxis aligned with rot axis + self.camManipRef.setPos(self.coaMarker, Vec3(0)) + self.camManipRef.setHpr(render, rotAngle, 0, 0) + # Reparent Cam to ref Coordinate system + parent = SEditor.camera.getParent() + SEditor.camera.wrtReparentTo(self.camManipRef) + # Rotate ref CS to final orientation + t = self.camManipRef.lerpHpr(rotAngle, orbitAngle, 0, + CAM_MOVE_DURATION, + other = render, + blendType = 'easeInOut', + task = 'manipulateCamera') + # Upon death, reparent Cam to parent + t.parent = parent + t.uponDeath = self.reparentCam + + def centerCam(self): + self.centerCamIn(1.0) + + def centerCamNow(self): + self.centerCamIn(0.) + + def centerCamIn(self, t): + taskMgr.remove('manipulateCamera') + # Record undo point + SEditor.pushUndo([SEditor.camera]) + # Determine marker location + markerToCam = self.coaMarker.getPos( SEditor.camera ) + dist = Vec3(markerToCam - ZERO_POINT).length() + scaledCenterVec = Y_AXIS * dist + delta = markerToCam - scaledCenterVec + self.camManipRef.setPosHpr(SEditor.camera, Point3(0), Point3(0)) + t = SEditor.camera.lerpPos(Point3(delta), + CAM_MOVE_DURATION, + other = self.camManipRef, + blendType = 'easeInOut', + task = 'manipulateCamera') + t.uponDeath = self.updateCoaMarkerSizeOnDeath + + def zoomCam(self, zoomFactor, t): + taskMgr.remove('manipulateCamera') + # Record undo point + SEditor.pushUndo([SEditor.camera]) + # Find a point zoom factor times the current separation + # of the widget and cam + zoomPtToCam = self.coaMarker.getPos(SEditor.camera) * zoomFactor + # Put a target nodePath there + self.camManipRef.setPos(SEditor.camera, zoomPtToCam) + # Move to that point + t = SEditor.camera.lerpPos(ZERO_POINT, + CAM_MOVE_DURATION, + other = self.camManipRef, + blendType = 'easeInOut', + task = 'manipulateCamera') + t.uponDeath = self.updateCoaMarkerSizeOnDeath + + def spawnMoveToView(self, view): + # Kill any existing tasks + taskMgr.remove('manipulateCamera') + # Record undo point + SEditor.pushUndo([SEditor.camera]) + # Calc hprOffset + hprOffset = VBase3() + if view == 8: + # Try the next roll angle + self.orthoViewRoll = (self.orthoViewRoll + 90.0) % 360.0 + # but use the last view + view = self.lastView + else: + self.orthoViewRoll = 0.0 + # Adjust offset based on specified view + if view == 1: + hprOffset.set(180., 0., 0.) + elif view == 2: + hprOffset.set(0., 0., 0.) + elif view == 3: + hprOffset.set(90., 0., 0.) + elif view == 4: + hprOffset.set(-90., 0., 0.) + elif view == 5: + hprOffset.set(0., -90., 0.) + elif view == 6: + hprOffset.set(0., 90., 0.) + elif view == 7: + hprOffset.set(135., -35.264, 0.) + # Position target + self.camManipRef.setPosHpr(self.coaMarker, ZERO_VEC, + hprOffset) + # Scale center vec by current distance to target + offsetDistance = Vec3(SEditor.camera.getPos(self.camManipRef) - + ZERO_POINT).length() + scaledCenterVec = Y_AXIS * (-1.0 * offsetDistance) + # Now put the camManipRef at that point + self.camManipRef.setPosHpr(self.camManipRef, + scaledCenterVec, + ZERO_VEC) + # Record view for next time around + self.lastView = view + t = SEditor.camera.lerpPosHpr(ZERO_POINT, + VBase3(0,0,self.orthoViewRoll), + CAM_MOVE_DURATION, + other = self.camManipRef, + blendType = 'easeInOut', + task = 'manipulateCamera') + t.uponDeath = self.updateCoaMarkerSizeOnDeath + + + def swingCamAboutWidget(self, degrees, t): + # Remove existing camera manipulation task + taskMgr.remove('manipulateCamera') + + # Record undo point + SEditor.pushUndo([SEditor.camera]) + + # Coincident with widget + self.camManipRef.setPos(self.coaMarker, ZERO_POINT) + # But aligned with render space + self.camManipRef.setHpr(ZERO_POINT) + + parent = SEditor.camera.getParent() + SEditor.camera.wrtReparentTo(self.camManipRef) + + manipTask = self.camManipRef.lerpHpr(VBase3(degrees,0,0), + CAM_MOVE_DURATION, + blendType = 'easeInOut', + task = 'manipulateCamera') + # Upon death, reparent Cam to parent + manipTask.parent = parent + manipTask.uponDeath = self.reparentCam + + def reparentCam(self, state): + SEditor.camera.wrtReparentTo(state.parent) + self.updateCoaMarkerSize() + + def fitOnWidget(self, nodePath = 'None Given'): + # Fit the node on the screen + # stop any ongoing tasks + taskMgr.remove('manipulateCamera') + # How big is the node? + nodeScale = SEditor.widget.scalingNode.getScale(render) + maxScale = max(nodeScale[0],nodeScale[1],nodeScale[2]) + maxDim = min(SEditor.dr.nearWidth, SEditor.dr.nearHeight) + + # At what distance does the object fill 30% of the screen? + # Assuming radius of 1 on widget + camY = SEditor.dr.near * (2.0 * maxScale)/(0.3 * maxDim) + + # What is the vector through the center of the screen? + centerVec = Y_AXIS * camY + + # Where is the node relative to the viewpoint + vWidget2Camera = SEditor.widget.getPos(SEditor.camera) + + # How far do you move the camera to be this distance from the node? + deltaMove = vWidget2Camera - centerVec + + # Move a target there + self.camManipRef.setPos(SEditor.camera, deltaMove) + + parent = SEditor.camera.getParent() + SEditor.camera.wrtReparentTo(self.camManipRef) + fitTask = SEditor.camera.lerpPos(Point3(0,0,0), + CAM_MOVE_DURATION, + blendType = 'easeInOut', + task = 'manipulateCamera') + # Upon death, reparent Cam to parent + fitTask.parent = parent + fitTask.uponDeath = self.reparentCam + + def moveToFit(self): + # How bit is the active widget? + widgetScale = SEditor.widget.scalingNode.getScale(render) + maxScale = max(widgetScale[0], widgetScale[1], widgetScale[2]) + # At what distance does the widget fill 50% of the screen? + camY = ((2 * SEditor.dr.near * (1.5 * maxScale)) / + min(SEditor.dr.nearWidth, SEditor.dr.nearHeight)) + # Find a point this distance along the Y axis + # MRM: This needs to be generalized to support non uniform frusta + centerVec = Y_AXIS * camY + # Before moving, record the relationship between the selected nodes + # and the widget, so that this can be maintained + SEditor.selected.getWrtAll() + # Push state onto undo stack + SEditor.pushUndo(SEditor.selected) + # Remove the task to keep the widget attached to the object + taskMgr.remove('followSelectedNodePath') + # Spawn a task to keep the selected objects with the widget + taskMgr.add(self.stickToWidgetTask, 'stickToWidget') + # Spawn a task to move the widget + t = SEditor.widget.lerpPos(Point3(centerVec), + CAM_MOVE_DURATION, + other = SEditor.camera, + blendType = 'easeInOut', + task = 'moveToFitTask') + t.uponDeath = lambda state: taskMgr.remove('stickToWidget') + + def stickToWidgetTask(self, state): + # Move the objects with the widget + SEditor.selected.moveWrtWidgetAll() + # Continue + return Task.cont + + def enableMouseFly(self, fKeyEvents = 1): + # disable C++ fly interface + base.disableMouse() + # Enable events + for event in self.actionEvents: + self.accept(event[0], event[1], extraArgs = event[2:]) + if fKeyEvents: + for event in self.keyEvents: + self.accept(event[0], event[1], extraArgs = event[2:]) + # Show marker + self.coaMarker.reparentTo(SEditor.group) + + def disableMouseFly(self): + # Hide the marker + self.coaMarker.reparentTo(hidden) + # Ignore events + for event in self.actionEvents: + self.ignore(event[0]) + for event in self.keyEvents: + self.ignore(event[0]) + # Kill tasks + self.removeManipulateCameraTask() + taskMgr.remove('stickToWidget') + base.enableMouse() + + def removeManipulateCameraTask(self): + taskMgr.remove('manipulateCamera') + diff --git a/doc/SceneEditor/seColorEntry.py b/doc/SceneEditor/seColorEntry.py new file mode 100644 index 0000000000..79144fcc7d --- /dev/null +++ b/doc/SceneEditor/seColorEntry.py @@ -0,0 +1,51 @@ +################################################################# +# seColorEntry.py +# Originally from VectorWidgets.py +# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +# +# Here we need some widget to handle some color input. +# There is some colorEntry in the VectorWidgets.py, but they didn't +# work as we need (we don't need alpha here. so the dim option should be set to 3). +# So we make one from them. +# +################################################################# +from TkGlobal import * +import Valuator +import Floater +import Slider +import string +import tkColorChooser +from VectorWidgets import VectorEntry + +class seColorEntry(VectorEntry): + def __init__(self, parent = None, **kw): + # Initialize options for the class (overriding some superclass options) + optiondefs = ( + ('dim', 3, Pmw.INITOPT), + ('type', 'slider', Pmw.INITOPT), + ('fGroup_labels', ('R','G','B'), None), + ('min', 0.0, None), + ('max', 255.0, None), + ('nuDigits', 0, None), + ('valuator_resolution', 1.0, None), + ) + self.defineoptions(kw, optiondefs) + + # Initialize the superclass, make sure dim makes it to superclass + VectorEntry.__init__(self, parent, dim = self['dim']) + # Add menu item to popup color picker + self.addMenuItem( + 'Popup color picker', + command = lambda s = self: s.popupColorPicker()) + # Needed because this method checks if self.__class__ is myClass + # where myClass is the argument passed into inialiseoptions + self.initialiseoptions(seColorEntry) + + def popupColorPicker(self): + # Can pass in current color with: color = (255, 0, 0) + color = tkColorChooser.askcolor( + parent = self.interior(), + # Initialize it to current color + initialcolor = tuple(self.get()[:3]))[0] + if color: + self.set((color[0], color[1], color[2])) diff --git a/doc/SceneEditor/seFileSaver.py b/doc/SceneEditor/seFileSaver.py new file mode 100644 index 0000000000..045aa8419b --- /dev/null +++ b/doc/SceneEditor/seFileSaver.py @@ -0,0 +1,913 @@ +#################################################################################################################################################### +# File Saving +# This code saves the scene out as python code... the scene is stored in the various dictionaries in "dataHolder.py" ...the class "AllScene" +# +#################################################################################################################################################### +from PandaModules import * + +from ShowBaseGlobal import * +import os +import shutil +from EggData import * +import string + +#################################################################################################################################################### +#### These modules are modified versions of Disney's equivalent modules +#### We need to figure out a way to inherit their modules and overwrite what we need changed +import seParticlePanel +import seParticles +import seParticleEffect +import seForceGroup +#################################################################################################################################################### + +class FileSaver: + + #################################################################################################################################################### + # This class saves out the scene built with the scene editor as python code + # There are dictionaries saved out to save the state of the scene for reloading it with the editor + # Currently saving is supported for Models, Animations, Lights, Dummy Nodes + # Attributes like parenting are also saved out + # This class is actually instantiated in sceneEditor.py in the saveScene() method + #################################################################################################################################################### + + def __init(self): + pass + + def SaveFile(self,AllScene,filename,dirname,reSaveFlag=0): + + ################################################################################################################################################ + # This function takes the "dataHolder" instance "AllScene" which has dictionaries containing scene information + # The filename is where the scene will be written to + ################################################################################################################################################ + + i1=" " # indentation + i2=i1+i1 # double indentation + out_file = open(filename,"w") + print "dirname:" + dirname + if( not os.path.isdir(dirname)): + os.mkdir(dirname) + savepathname=Filename(filename) + self.savepath=savepathname.getBasenameWoExtension() + out_file.write("##########################################################################################################\n") + out_file.write("# Auto Generated Code by Scene Editor\n") + out_file.write("# Edit with caution\n") + out_file.write("# Using this file in your code:\n") + out_file.write("# For example, if you have named this file as \"myscene.py\"\n") + out_file.write("# Do the following:\n") + out_file.write("# from myscene import * \n") + out_file.write("# theScene=SavedScene() #instantiate the class\n") + out_file.write("# IMPORTANT: All the documentation below refers to \"theScene\" as the instance of SavedScene()\n") + out_file.write("##########################################################################################################\n\n") + + out_file.write("##########################################################################################################\n") + out_file.write("# Import Panda Modules\n") + out_file.write("##########################################################################################################\n") + out_file.write("from ShowBaseGlobal import * # Core functionality for running the \"show\"\n") + out_file.write("import Actor # Importing models with animations\n") + out_file.write("import Mopath # Motion Paths\n") + out_file.write("import MopathInterval # Motion Paths\n") + out_file.write("from IntervalGlobal import * # Intervals for interpolation, sequencing and parallelization\n") + out_file.write("import ParticleEffect # Particle Systems\n") + out_file.write("import ForceGroup # Forces acting on Particles\n") + out_file.write("import Particles\n\n") + out_file.write("##########################################################################################################\n") + out_file.write("# This class stores the entire scene\n") + out_file.write("##########################################################################################################\n\n") + out_file.write("class SavedScene(DirectObject): # We inherit from DirectObject so that we can use self.accept method to catch messages\n") + out_file.write("\n") + out_file.write(i1+"# These dictionaries are required for re-loading a scene in the editor\n") + out_file.write(i1+"# They can be used to access the objects as well\n\n") + out_file.write(i1+"ModelDic={}# Stores all the models and static geometry\n") + out_file.write(i1+"ModelRefDic={}# Stores the paths to the models\n") + out_file.write("\n") + out_file.write(i1+"ActorDic={}# Stores all the actors\n") + out_file.write(i1+"ActorRefDic={}# Stores the paths to the actors\n") + out_file.write(i1+"ActorAnimsDic={}# Stores the animations for each actor\n") + out_file.write(i1+"blendAnimDict={}# Stores all the blended animations\n") + out_file.write("\n") + out_file.write(i1+"LightDict={}# Stores all the lights\n") + out_file.write(i1+"LightTypes={}# Stores types for the lights\n") + out_file.write(i1+"LightNodes={}# Stores the actual nodes for the lights\n") + out_file.write("\n") + out_file.write(i1+"dummyDict={}# Stores dummies\n") + out_file.write("\n") + out_file.write(i1+"collisionDict={}# Stores Collision information\n") + out_file.write("\n") + out_file.write(i1+"curveDict={}# Stores Mopath information\n") + out_file.write(i1+"curveIntervals=[]# Stores list of mopath intervals\n") + out_file.write(i1+"curveRefColl=[]# Stores paths to mopaths\n") + out_file.write(i1+"curveIntervalsDict={}# Stores mopath intervals\n") + out_file.write("\n") + out_file.write(i1+"particleDict={}# Stores particles\n") + out_file.write(i1+"particleNodes={}# Stores particle nodes\n") + out_file.write("\n") + out_file.write(i1+"#Light Count\n") + out_file.write(i1+"ambientCount=0\n") + out_file.write(i1+"directionalCount=0\n") + out_file.write(i1+"pointCount=0\n") + out_file.write(i1+"spotCount=0\n") + out_file.write("\n") + out_file.write(i1+"#Lighting Attribute\n") + out_file.write(i1+"lightAttrib = LightAttrib.makeAllOff()# Initialize lighting\n") + out_file.write("\n") + out_file.write(i1+"CollisionHandler=CollisionHandlerEvent()# Setup a Collision Handler\n") + + out_file.write(i1+"##########################################################################################################\n") + out_file.write(i1+"# Constructor: this is run first when you instantiate the SavedScene class\n") + out_file.write(i1+"##########################################################################################################\n") + out_file.write(i1+"def __init__(self,loadmode=1,seParticleEffect=None,seParticles=None,executionpath=None):# loadmode 0 specifies that this file is being loaded by the scene editor and it passes its own versions of the particle fx modules\n") + out_file.write("\n") + out_file.write(i2+"self.loadmode=loadmode\n") + out_file.write(i2+"self.seParticleEffect=seParticleEffect\n") + out_file.write(i2+"self.seParticles=seParticles\n") + out_file.write(i2+"self.executionpath=executionpath\n") + out_file.write("\n") + out_file.write(i2+"base.enableParticles()# Enable Particle effects\n") + out_file.write("\n") + out_file.write(i2+"self.cTrav = CollisionTraverser() # Setup a traverser for collisions\n") + out_file.write(i2+"base.cTrav = self.cTrav\n") + out_file.write(i2+"self.CollisionHandler.setInPattern(\"enter%in\")# The message to be raised when something enters a collision node\n") + out_file.write(i2+"self.CollisionHandler.setOutPattern(\"exit%in\")# The message to be raised when something exits a collision node\n") + out_file.write("\n") + #################################################################################################################################################### + # Save Models + #################################################################################################################################################### + + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Code for all the models\n") + out_file.write(i2+"# To access these models:\n") + out_file.write(i2+"# theScene.ModelDic[\"'Model_Name\"']\n") + out_file.write(i2+"# where theScene is the SavedScene class instance\n") + out_file.write(i2+"# Properties saved include:\n") + out_file.write(i2+"# Transformations\n") + out_file.write(i2+"# Alpha and color\n") + out_file.write(i2+"# Parent and child information\n") + out_file.write(i2+"##########################################################################################################\n") + + for model in AllScene.ModelDic: + out_file.write("\n") + modelS=str(model) + + if(1): # This is kept for now... perhaps later some sort of check might have to be enforced based on loadMode + #Loading Code + out_file.write(i2+"# Loading model's egg file\n") + #out_file.write(i2+ "self."+ modelS + "=loader.loadModel(\'" + AllScene.ModelRefDic[model].getFullpath() + "\')\n")#Absolute Paths + + newpath = dirname + "/" + AllScene.ModelRefDic[model].getBasename() + newpathF=Filename(newpath) + newpathSpecific=newpathF.toOsSpecific() + + # Copy all the textures referenced by this file over to the relative directory + fnamelist=[] + modelData=EggData() + modelData.read(AllScene.ModelRefDic[model]) + textures=EggTextureCollection() + textures.findUsedTextures(modelData) + for index in range(textures.getNumTextures()): + texture=textures.getTexture(index) + texfilename=texture.getFilename() + fnamelist.append(texfilename.getFullpath()) + oldFilename=Filename(Filename(AllScene.ModelRefDic[model].getDirname()),texfilename) + if(not oldFilename.isRegularFile()): + if(texfilename.resolveFilename(getTexturePath(),"")): + oldFilename=texfilename + oldtexpath=oldFilename.toOsSpecific(); + + newtexpath=dirname + "/" + texfilename.getBasename() + newtexpathF=Filename(newtexpath) + newtexpathSpecific=newtexpathF.toOsSpecific() + + print "TEXTURE SAVER:: copying" + oldtexpath + " to " + newtexpathSpecific + if(oldtexpath != newtexpathSpecific): + shutil.copyfile(oldtexpath,newtexpathSpecific) + + + + + + + # Copy the file over to the relative directory + oldModelpath=AllScene.ModelRefDic[model].toOsSpecific() + print "FILESAVER:: copying from " + AllScene.ModelRefDic[model].toOsSpecific() + "to" + newpathSpecific + if(oldModelpath!=newpathSpecific): + shutil.copyfile(oldModelpath,newpathSpecific) + + + e=EggData() + e.read(AllScene.ModelRefDic[model]) + etc=EggTextureCollection() + etc.extractTextures(e) + for index in range(len(fnamelist)): + print fnamelist[index] + tex=etc.findFilename(Filename(fnamelist[index])) + fn=Filename(tex.getFilename()) + fn.setDirname("") + tex.setFilename(fn) + e.writeEgg(Filename.fromOsSpecific(newpathSpecific)) + + + + out_file.write(i2+"if(self.loadmode==1):\n") + out_file.write(i2+i1+ "self."+ modelS + "=loader.loadModel(\'" + self.savepath + "/" + AllScene.ModelRefDic[model].getBasename() + "')\n")#Relative Path + out_file.write(i2+"else:\n") + out_file.write(i2+i1+ "self."+ modelS + "=loader.loadModel(self.executionpath + \'/" + AllScene.ModelRefDic[model].getBasename() + "')\n")#Relative Path with execution point specified by the invoking-level-editor + + #Transformation Code + out_file.write("\n") + out_file.write(i2+"# Transforming the model\n") + out_file.write(i2+ "self."+ modelS + ".setPosHprScale(%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f)\n"% (AllScene.ModelDic[model].getX(),AllScene.ModelDic[model].getY(),AllScene.ModelDic[model].getZ(),AllScene.ModelDic[model].getH(),AllScene.ModelDic[model].getP(),AllScene.ModelDic[model].getR(),AllScene.ModelDic[model].getSx(),AllScene.ModelDic[model].getSy(),AllScene.ModelDic[model].getSz())) + + if(AllScene.ModelDic[model].hasTransparency()): + out_file.write("\n") + out_file.write(i2+"# Alpha\n") + out_file.write(i2+ "self."+ modelS + ".setTransparency(1)\n") + clr=AllScene.ModelDic[model].getColor() + out_file.write(i2+ "self."+ modelS + ".setColor(%.4f,%.4f,%.4f,%.4f)\n"%(clr.getX(),clr.getY(),clr.getZ(),clr.getW())) + + out_file.write("\n") + out_file.write(i2+ "# Reparent To Render for now and later we update all the parentings\n") + out_file.write(i2+ "self."+ modelS + ".reparentTo(render)\n") + out_file.write("\n") + out_file.write(i2+ "# Save Metadata...can be retrieved by doing theScene.ModelDic[\"Model_Name\"].getTag(\"Metadata\")\n") + out_file.write(i2+ "self."+ modelS + ".setTag(\"Metadata\",\"" + AllScene.ModelDic[model].getTag("Metadata") + "\")\n") + out_file.write("\n") + out_file.write(i2+ "# Fill in the dictionaries which are used by level Ed to reload state\n") + out_file.write(i2+ "self.ModelDic[\'" + modelS + "\']=self." + AllScene.ModelDic[model].getName()+"\n") + #out_file.write(i2+ "self.ModelRefDic[\'" + modelS + "\']=Filename(\'"+ AllScene.ModelRefDic[model].getFullpath() +"\')\n")# The old Absolute Path way + out_file.write(i2+ "self.ModelRefDic[\'" + modelS + "\']=\'"+ AllScene.ModelRefDic[model].getBasename() +"\'\n")# Relative paths + out_file.write(i2+ "self.ModelDic[\'"+ modelS + "\'].setName(\'"+ modelS +"\')\n") + out_file.write("\n") + + #################################################################################################################################################### + # Save Dummies + #################################################################################################################################################### + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Code for all the Dummy Objects\n") + out_file.write(i2+"# To access the dummies\n") + out_file.write(i2+"# theScene.dummyDict['Dummy_Name']\n") + out_file.write(i2+"##########################################################################################################\n") + for dummy in AllScene.dummyDict: + out_file.write("\n") + dummyS=str(dummy) + + if(1): # This is kept for now... perhaps later some sort of check might have to be enforced based on loadMode + out_file.write(i2+ "self."+ dummyS + "=loader.loadModelCopy(\"misc/sphere\")\n") + #Transformation Code + out_file.write(i2+"# Transforming the Dummy\n") + out_file.write(i2+ "self."+ dummyS + ".setPosHprScale(%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f)\n"% (AllScene.dummyDict[dummy].getX(),AllScene.dummyDict[dummy].getY(),AllScene.dummyDict[dummy].getZ(),AllScene.dummyDict[dummy].getH(),AllScene.dummyDict[dummy].getP(),AllScene.dummyDict[dummy].getR(),AllScene.dummyDict[dummy].getSx(),AllScene.dummyDict[dummy].getSy(),AllScene.dummyDict[dummy].getSz())) + out_file.write("\n") + out_file.write(i2+ "# Fill in the dictionaries which are used by level Ed to reload state\n") + out_file.write(i2+ "self.dummyDict[\'" + dummyS + "\']=self." + AllScene.dummyDict[dummy].getName()+"\n") + out_file.write(i2+ "self.dummyDict[\'"+ dummyS + "\'].setName(\'"+ dummyS +"\')\n") + out_file.write("\n") + out_file.write(i2+ "# Save Metadata...can be retrieved by doing theScene.dummyDict[\"Dummy_Name\"].getTag(\"Metadata\")\n") + out_file.write(i2+ "self."+ dummyS + ".setTag(\"Metadata\",\"" + AllScene.dummyDict[dummy].getTag("Metadata") + "\")\n") + out_file.write("\n") + + #################################################################################################################################################### + # Saving Actors and their animations + #################################################################################################################################################### + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Code for all the Actors and animations\n") + out_file.write(i2+"# To access the Actors\n") + out_file.write(i2+"# theScene.ActorDic[\'Actor_Name\']\n") + out_file.write(i2+"# theScene.ActorDic[\'Actor_Name\'].play(\'Animation_Name\')\n") + out_file.write(i2+"##########################################################################################################\n") + for actor in AllScene.ActorDic: + out_file.write("\n") + actorS=str(actor) + + if(1): # This is kept for now... perhaps later some sort of check might have to be enforced based on loadMode + #out_file.write(i2+ "self."+ actorS + "=Actor.Actor(\'"+ AllScene.ActorRefDic[actor].getFullpath() + "\')\n")# The old way with absolute paths + + + newpath = dirname + "/" + AllScene.ActorRefDic[actor].getBasename() + newpathF=Filename(newpath) + newpathSpecific=newpathF.toOsSpecific() + + # Copy all the textures referenced by this file over to the relative directory + actorfnamelist=[] + actorData=EggData() + actorData.read(AllScene.ActorRefDic[actor]) + textures=EggTextureCollection() + textures.findUsedTextures(actorData) + for index in range(textures.getNumTextures()): + texture=textures.getTexture(index) + texfilename=texture.getFilename() + actorfnamelist.append(texfilename.getFullpath()) + + oldFilename=Filename(Filename(AllScene.ActorRefDic[actor].getDirname()),texfilename) + if(not oldFilename.isRegularFile()): + if(texfilename.resolveFilename(getTexturePath(),"")): + oldFilename=texfilename + oldtexpath=oldFilename.toOsSpecific(); + + + newtexpath=dirname + "/" + texfilename.getBasename() + newtexpathF=Filename(newtexpath) + newtexpathSpecific=newtexpathF.toOsSpecific() + print "TEXTURE SAVER:: copying" + oldtexpath + " to " + newtexpathSpecific + if(oldtexpath != newtexpathSpecific): + shutil.copyfile(oldtexpath,newtexpathSpecific) + + + # Copy the file over to the relative directory + oldActorpath=AllScene.ActorRefDic[actor].toOsSpecific() + print "FILESAVER:: copying from " + AllScene.ActorRefDic[actor].toOsSpecific() + "to" + newpathSpecific + if(oldActorpath!=newpathSpecific): + shutil.copyfile(oldActorpath,newpathSpecific) + + + e=EggData() + e.read(AllScene.ActorRefDic[actor]) + etc=EggTextureCollection() + etc.extractTextures(e) + for index in range(len(actorfnamelist)): + print actorfnamelist[index] + tex=etc.findFilename(Filename(actorfnamelist[index])) + fn=Filename(tex.getFilename()) + fn.setDirname("") + tex.setFilename(fn) + e.writeEgg(Filename.fromOsSpecific(newpathSpecific)) + + + + out_file.write(i2+"if(self.loadmode==1):\n") + out_file.write(i2+i1+ "self."+ actorS + "=Actor.Actor(\'" + self.savepath + "/" + AllScene.ActorRefDic[actor].getBasename() + "')\n")#Relative Path + out_file.write(i2+"else:\n") + out_file.write(i2+i1+ "self."+ actorS + "=Actor.Actor(self.executionpath + \'/" + AllScene.ActorRefDic[actor].getBasename() + "')\n")#Relative Path with execution point specified by the invoking-level-editor + + #Transformation Code + out_file.write(i2+"# Transforming the Actor\n") + out_file.write(i2+ "self."+ actorS + ".setPosHprScale(%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f)\n"% (AllScene.ActorDic[actor].getX(),AllScene.ActorDic[actor].getY(),AllScene.ActorDic[actor].getZ(),AllScene.ActorDic[actor].getH(),AllScene.ActorDic[actor].getP(),AllScene.ActorDic[actor].getR(),AllScene.ActorDic[actor].getSx(),AllScene.ActorDic[actor].getSy(),AllScene.ActorDic[actor].getSz())) + + if(AllScene.ActorDic[actor].hasTransparency()): + out_file.write(i2+"# Alpha\n") + out_file.write(i2+ "self."+ actorS + ".setTransparency(1)\n") + clr=AllScene.ActorDic[actor].getColor() + out_file.write(i2+ "self."+ actorS + ".setColor(%.4f,%.4f,%.4f,%.4f)\n"%(clr.getX(),clr.getY(),clr.getZ(),clr.getW())) + + out_file.write(i2+ "self."+ actorS + ".reparentTo(render)\n") + + out_file.write("\n") + out_file.write(i2+ "# Save Metadata...can be retrieved by doing theScene.ActorDic[\"Actor_Name\"].getTag(\"Metadata\")\n") + out_file.write(i2+ "self."+ actorS + ".setTag(\"Metadata\",\"" + AllScene.ActorDic[actor].getTag("Metadata") + "\")\n") + + out_file.write("\n") + out_file.write(i2+ "# Fill in the dictionaries which are used by level Ed to reload state\n") + ActorAnimations=AllScene.getAnimationDictFromActor(actor) + ActorAnimationsInvoke={} + + if(ActorAnimations!={}): #Check if a dictionary of animations exists for this actor + for animation in ActorAnimations: + #out_file.write(i2+ "self."+ actorS + ".loadAnims(" + str(ActorAnimations) +")\n") # Old way with absolute paths + print "ACTOR ANIMATIONS" + ActorAnimations[animation] + oldAnimPath=Filename(ActorAnimations[animation]) + oldAnim=oldAnimPath.toOsSpecific() + dirOS=Filename(dirname) + newAnim=dirOS.toOsSpecific() + "\\" + oldAnimPath.getBasename() + print "ACTOR ANIM SAVER:: Comparing" + oldAnim +"and" + newAnim + if(oldAnim!=newAnim): + shutil.copyfile(oldAnim,newAnim) + newAnimF=Filename.fromOsSpecific(newAnim) + ActorAnimationsInvoke[animation]="self.executionpath +" + "/" +newAnimF.getBasename() + ActorAnimations[animation]= self.savepath + "/" + newAnimF.getBasename() + + + out_file.write(i2+"if(self.loadmode==1):\n") + out_file.write(i2+ i1+"self."+ actorS + ".loadAnims(" + str(ActorAnimations) +")\n") # Now with new relative paths + out_file.write(i2+"else:\n") + theloadAnimString=str(ActorAnimationsInvoke)# We hack the "self.executionpath" part into the dictionary as a variable using string replace + print "LOAD ANIM STRING BEFORE" + theloadAnimString + theloadAnimString=theloadAnimString.replace('\'self.executionpath +','self.executionpath + \'') + print "LOAD ANIM STRING AFTER" + theloadAnimString + out_file.write(i2+ i1+"self."+ actorS + ".loadAnims(" + theloadAnimString +")\n") # Now with new relative paths based on editor invocation + + out_file.write(i2+ "self.ActorDic[\'" + actorS + "\']=self." + AllScene.ActorDic[actor].getName()+"\n") + #out_file.write(i2+ "self.ActorRefDic[\'" + actorS + "\']=Filename(\'"+AllScene.ActorRefDic[actor].getFullpath() +"\')\n") # Old way with absolute paths + out_file.write(i2+ "self.ActorRefDic[\'" + actorS + "\']=\'"+ AllScene.ActorRefDic[actor].getBasename() +"\'\n")# Relative paths + out_file.write(i2+ "self.ActorDic[\'"+ actorS + "\'].setName(\'"+ actorS +"\')\n") + if(AllScene.blendAnimDict.has_key(actor)): # Check if a dictionary of blended animations exists + out_file.write(i2+ "self.blendAnimDict[\"" + actorS +"\"]=" + str(AllScene.blendAnimDict[actor]) + "\n") + + + out_file.write("\n") + + #################################################################################################################################################### + # Collsion Node Saving + #################################################################################################################################################### + + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Code for setting up Collision Nodes\n") + out_file.write(i2+"# To use collision detection:\n") + out_file.write(i2+"# You must set up your own bitmasking and event handlers, the traverser \"cTrav\" is created for you at the top\n") + out_file.write(i2+"# The collision nodes are stored in collisionDict\n") + out_file.write(i2+"##########################################################################################################\n\n") + for collnode in AllScene.collisionDict: + collnodeS=str(collnode) + solid=AllScene.collisionDict[collnode].node().getSolid(0) + nodetype=solid.getType().getName() + + if(nodetype=="CollisionSphere"): #Save Collison Sphere + out_file.write(i2+"collSolid=CollisionSphere(%.3f,%.3f,%.3f,%.3f)\n"%(solid.getCenter().getX(),solid.getCenter().getY(),solid.getCenter().getZ(),solid.getRadius())) + pass + elif(nodetype=="CollisionPolygon"): #Save Collison Polygon + + ax=AllScene.collisionDict[collnode].getTag("A_X") + ay=AllScene.collisionDict[collnode].getTag("A_Y") + az=AllScene.collisionDict[collnode].getTag("A_Z") + + bx=AllScene.collisionDict[collnode].getTag("B_X") + by=AllScene.collisionDict[collnode].getTag("B_Y") + bz=AllScene.collisionDict[collnode].getTag("B_Z") + + cx=AllScene.collisionDict[collnode].getTag("C_X") + cy=AllScene.collisionDict[collnode].getTag("C_Y") + cz=AllScene.collisionDict[collnode].getTag("C_Z") + + out_file.write(i2+"pointA = Point3(" + ax + "," + ay + "," + az + ")\n") + out_file.write(i2+"pointB = Point3(" + bx + "," + by + "," + bz + ")\n") + out_file.write(i2+"pointC = Point3(" + cx + "," + cy + "," + cz + ")\n") + out_file.write(i2+"collSolid=CollisionPolygon(pointA, pointB, pointC)\n") + + pass + + elif(nodetype=="CollisionSegment"): #Save Collison Segment + A=AllScene.collisionDict[collnode].node().getSolid(0).getPointA() + B=AllScene.collisionDict[collnode].node().getSolid(0).getPointB() + + out_file.write(i2+"pointA = Point3(%.3f,%.3f,%.3f)\n"%(A.getX(),A.getY(),A.getZ())) + out_file.write(i2+"pointB = Point3(%.3f,%.3f,%.3f)\n"%(B.getX(),B.getY(),B.getZ())) + out_file.write(i2+"collSolid=CollisionSegment()\n") + out_file.write(i2+"collSolid.setPointA(pointA)\n") + out_file.write(i2+"collSolid.setFromLens(base.cam.node(),Point2(-1,1))\n") + out_file.write(i2+"collSolid.setPointB(pointB)\n") + + pass + + elif(nodetype=="CollisionRay"): #Save Collison Ray + P = AllScene.collisionDict[collnode].node().getSolid(0).getOrigin() + V = AllScene.collisionDict[collnode].node().getSolid(0).getDirection() + + out_file.write(i2+"point=Point3(%.3f,%.3f,%.3f)\n"%(P.getX(),P.getY(),P.getZ())) + out_file.write(i2+"vector=Vec3(%.3f,%.3f,%.3f)\n"%(V.getX(),V.getY(),V.getZ())) + out_file.write(i2+"collSolid=CollisionRay()\n") + out_file.write(i2+"collSolid.setOrigin(point)\n") + out_file.write(i2+"collSolid.setDirection(vector)\n") + + pass + else: + print "Invalid Collision Node: " + nodetype + out_file.write("\n") + + + out_file.write(i2+"self." + collnodeS + "_Node" + "=CollisionNode(\""+collnodeS+"\")\n") + out_file.write(i2+"self." + collnodeS + "_Node" + ".addSolid(collSolid)\n") + out_file.write(i2+"base.cTrav.addCollider(self." + collnodeS + "_Node,self.CollisionHandler)\n") + out_file.write("\n") + + + + + #################################################################################################################################################### + # Light Saving + #################################################################################################################################################### + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Code for Lighting\n") + out_file.write(i2+"# To manipulated lights:\n") + out_file.write(i2+"# Manipulate the light node in theScene.LightNodes[\'Light_Name\']\n") + out_file.write(i2+"##########################################################################################################\n\n") + + LightList=AllScene.lightManager.getLightNodeList() + for light in LightList: + type = light.getType() + if type == 'ambient': + out_file.write(i2+"# Ambient Light\n") + out_file.write (i2+ "self.ambientCount += 1\n") + out_file.write (i2+ "alight = AmbientLight(\'"+ light.getName() +"\')\n") + out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n") + out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") + out_file.write (i2+ "self."+light.getName()+"= render.attachNewNode(alight.upcastToPandaNode())\n") + out_file.write (i2+ "self."+light.getName()+".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") + out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n") + out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n") + out_file.write (i2+ "self.LightNodes[\'" + light.getName() + "\']=self." + light.getName() + "\n") + out_file.write ("\n") + elif type == 'directional': + out_file.write(i2+"# Directional Light\n") + out_file.write (i2+ "self.directionalCount += 1\n") + out_file.write (i2+ "alight = DirectionalLight(\'"+ light.getName() + "\')\n") + out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n") + #out_file.write (i2+ "alight.setDirection(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n") + #out_file.write (i2+ "alight.setPoint(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") + out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n") + out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") + out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToPandaNode())\n") + out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") + out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n") + out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") + #out_file.write (i2+ "alight.setPos + out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n") + out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n") + out_file.write (i2+ "self.LightNodes[\'" + light.getName() + "\']=self." + light.getName() + "\n") + out_file.write ("\n") + elif type == 'point': + out_file.write(i2+"# Point Light\n") + out_file.write (i2+ "self.pointCount += 1\n") + out_file.write (i2+ "alight = PointLight(\'"+ light.getName() +"\')\n") + out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n") + #out_file.write (i2+ "alight.setPoint(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") + out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n") + out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n") + out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") + out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToPandaNode())\n") + out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") + out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") + out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n") + out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n") + out_file.write (i2+ "self.LightNodes[\'" + light.getName() + "\']=self." + light.getName() + "\n") + out_file.write ("\n") + elif type == 'spot': + out_file.write(i2+"# Spot Light\n") + out_file.write (i2+ "self.spotCount += 1\n") + out_file.write (i2+ "alight = Spotlight(\'"+ light.getName() + "\')\n") + out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n") + out_file.write (i2+ "alens = PerspectiveLens()\n") + out_file.write (i2+ "alight.setLens(alens)\n") + out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n") + out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n") + out_file.write (i2+ "alight.setExponent(" +str(light.getExponent()) +")\n") + out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n") + out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToLensNode())\n") + out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n") + out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n") + out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n") + out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n") + out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n") + out_file.write (i2+ "self.LightNodes[\'" + light.getName() + "\']=self." + light.getName() + "\n") + out_file.write ("\n") + else: + out_file.write (i2+ "print \'Invalid light type\'") + out_file.write (i2+ "return None") + out_file.write("\n") + + + + #################################################################################################################################################### + # Enable Lighting + #################################################################################################################################################### + + out_file.write(i2+ "# Enable Ligthing\n") + out_file.write(i2+ "render.node().setAttrib(self.lightAttrib)\n") + out_file.write("\n") + + + + #################################################################################################################################################### + # Initialize Particles for non scene editor mode + #################################################################################################################################################### + + out_file.write(i2+"# Load Particle Effects. The parameters to this function are to allow us to use our modified versions of the Particle Effects modules when loading this file with the level editor\n") + out_file.write(i2+"self.starteffects(self.loadmode,self.seParticleEffect,self.seParticles)\n") + out_file.write("\n") + + #################################################################################################################################################### + # Save Camera Settings + #################################################################################################################################################### + + out_file.write("\n") + out_file.write(i2+ "# Save Camera Settings\n") + out_file.write(i2+ "camera.setX(" + str(camera.getX()) + ")\n") + out_file.write(i2+ "camera.setY(" + str(camera.getY()) + ")\n") + out_file.write(i2+ "camera.setZ(" + str(camera.getZ()) + ")\n") + out_file.write(i2+ "camera.setH(" + str(camera.getH()) + ")\n") + out_file.write(i2+ "camera.setP(" + str(camera.getP()) + ")\n") + out_file.write(i2+ "camera.setR(" + str(camera.getR()) + ")\n") + out_file.write(i2+ "camera.getChild(0).node().getLens().setNear(" + str(camera.getChild(0).node().getLens().getNear()) + ")\n") + out_file.write(i2+ "camera.getChild(0).node().getLens().setFar(" + str(camera.getChild(0).node().getLens().getFar()) + ")\n") + out_file.write(i2+ "camera.getChild(0).node().getLens().setFov(VBase2(%.5f,%.5f))\n"% (camera.getChild(0).node().getLens().getHfov(),camera.getChild(0).node().getLens().getVfov())) + FilmSize=camera.getChild(0).node().getLens().getFilmSize() + out_file.write(i2+ "camera.getChild(0).node().getLens().setFilmSize(%.3f,%.3f)\n"%(FilmSize.getX(),FilmSize.getY())) + out_file.write(i2+ "camera.getChild(0).node().getLens().setFocalLength(" + str(camera.getChild(0).node().getLens().getFocalLength()) + ")\n") + out_file.write(i2+ "camera.setTag(\"Metadata\",\"" + camera.getTag("Metadata") + "\")\n") + out_file.write(i2+ "camera.reparentTo(render)\n") + out_file.write(i2+ "base.disableMouse()\n") + self.bgColor=base.getBackgroundColor() + out_file.write(i2+ "base.setBackgroundColor(%.3f,%.3f,%.3f)\n"%(self.bgColor.getX(),self.bgColor.getY(),self.bgColor.getZ())) + out_file.write("\n") + + + #################################################################################################################################################### + # Mopath Saving + #################################################################################################################################################### + + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Motion Paths\n") + out_file.write(i2+"# Using Mopaths:\n") + out_file.write(i2+"# theScene.curveIntervals[0].start() or .loop() will play curve with index 0\n") + out_file.write(i2+"##########################################################################################################\n\n") + + for node in AllScene.curveDict: + curveCollection=AllScene.curveDict[node] + curvenumber=0 + for curve in curveCollection: + filestring=dirname+ "\\" + str(node)+"_curve_"+str(curvenumber)+".egg" + f=Filename.fromOsSpecific(filestring) + #filestring=f.getFullpath()# The old absolute path way + filestring=f.getBasename() # The new relative path way + curve.writeEgg(f) + out_file.write(i2+"m=Mopath.Mopath()\n") + + out_file.write(i2+"if(self.loadmode==1):\n") + out_file.write(i2+i1+"m.loadFile(\"" + self.savepath +"/"+ filestring + "\")\n") # If just normally executed + out_file.write(i2+"else:\n") + out_file.write(i2+i1+"m.loadFile(self.executionpath + \"/"+ filestring + "\")\n") # If being invoked by level Ed + + out_file.write(i2+"mp=MopathInterval(m,self." + str(node) + ")\n") + out_file.write(i2+"self.curveIntervals.append(mp)\n") + + out_file.write(i2+"if(self.loadmode==1):\n") + out_file.write(i2+i1+"self.curveRefColl.append(\"" + self.savepath +"/"+ filestring +"\")\n") + out_file.write(i2+"else:\n") + out_file.write(i2+i1+"self.curveRefColl.append(self.executionpath + \"/"+ filestring +"\")\n") + + curvenumber=curvenumber+1 + out_file.write(i2+"self.curveIntervalsDict[\"" + str(node) + "\"]=self.curveIntervals\n") + out_file.write(i2+"self.curveDict[\"" + str(node) + "\"]=self.curveRefColl\n") + + + #################################################################################################################################################### + # Lets do all the reparenting here so as to make sure everything that needed to load was loaded + #################################################################################################################################################### + + + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Reparenting\n") + out_file.write(i2+"# A final pass is done on setting all the scenegraph hierarchy after all objects are laoded\n") + out_file.write(i2+"##########################################################################################################\n\n") + + for model in AllScene.ModelDic: + modelS=str(model) + parent=AllScene.ModelDic[model].getParent().getName() + if(parent=="render" or parent=="camera"): + out_file.write(i2+ "self."+ modelS + ".reparentTo(" + parent + ")\n") + else: + if(AllScene.particleDict.has_key(parent)): + out_file.write(i2+ "self."+ modelS + ".reparentTo(self." + parent + ".getEffect())\n") + else: + out_file.write(i2+ "self."+ modelS + ".reparentTo(self." + parent + ")\n") + out_file.write(i2+ "self.ModelDic[\'" + modelS + "\']=self." + AllScene.ModelDic[model].getName()+"\n") + out_file.write(i2+"\n") + + for dummy in AllScene.dummyDict: + dummyS=str(dummy) + parent=AllScene.dummyDict[dummy].getParent().getName() + if(parent=="render" or parent=="camera"): + out_file.write(i2+ "self."+ dummyS + ".reparentTo(" + parent + ")\n") + else: + if(AllScene.particleDict.has_key(parent)): + out_file.write(i2+ "self."+ dummyS + ".reparentTo(self." + parent + ".getEffect())\n") + else: + out_file.write(i2+ "self."+ dummyS + ".reparentTo(self." + parent + ")\n") + + out_file.write(i2+ "self.dummyDict[\'" + dummyS + "\']=self." + AllScene.dummyDict[dummy].getName()+"\n") + out_file.write(i2+"\n") + + for actor in AllScene.ActorDic: + actorS=str(actor) + parent=AllScene.ActorDic[actor].getParent().getName() + if(parent=="render" or parent=="camera"): + out_file.write(i2+ "self."+ actorS + ".reparentTo(" + parent + ")\n") + else: + if(AllScene.particleDict.has_key(parent)): + out_file.write(i2+ "self."+ actorS + ".reparentTo(self." + parent + ".getEffect())\n") + else: + out_file.write(i2+ "self."+ actorS + ".reparentTo(self." + parent + ")\n") + + out_file.write(i2+ "self.ActorDic[\'" + actorS + "\']=self." + AllScene.ActorDic[actor].getName()+"\n") + out_file.write(i2+"\n") + + + for collnode in AllScene.collisionDict: + collnodeS=str(collnode) + solid=AllScene.collisionDict[collnode].node().getSolid(0) + nodetype=solid.getType().getName() + parentname=AllScene.collisionDict[collnode].getParent().getName() + if(parentname=="render" or parentname =="camera"): + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"]="+ parentname + ".attachNewNode(self." + collnodeS + "_Node)\n") + else: + if(AllScene.particleDict.has_key(parent)): + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"]=self."+ parentname + "getEffect().attachNewNode(self." + collnodeS + "_Node)\n") + else: + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"]=self."+ parentname + ".attachNewNode(self." + collnodeS + "_Node)\n") + dictelem=AllScene.collisionDict[collnode] + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setPosHprScale(%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f)\n"%(dictelem.getX(),dictelem.getY(),dictelem.getZ(),dictelem.getH(),dictelem.getP(),dictelem.getR(),dictelem.getSx(),dictelem.getSy(),dictelem.getSz())) + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag(\"Metadata\",\"" + AllScene.collisionDict[collnode].getTag("Metadata") + "\")\n") + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].show()\n") + if(nodetype=="CollisionPolygon"): #Save Collison Polygon... the reason we need to use setTag here is because there is no inbuilt way of saving transforms for collision polys + + ax=float(AllScene.collisionDict[collnode].getTag("A_X")) + ay=float(AllScene.collisionDict[collnode].getTag("A_Y")) + az=float(AllScene.collisionDict[collnode].getTag("A_Z")) + + bx=float(AllScene.collisionDict[collnode].getTag("B_X")) + by=float(AllScene.collisionDict[collnode].getTag("B_Y")) + bz=float(AllScene.collisionDict[collnode].getTag("B_Z")) + + cx=float(AllScene.collisionDict[collnode].getTag("C_X")) + cy=float(AllScene.collisionDict[collnode].getTag("C_Y")) + cz=float(AllScene.collisionDict[collnode].getTag("C_Z")) + + out_file.write(i2+"pointA=Point3(%.3f,%.3f,%.3f)\n"%(ax,ay,az)) + out_file.write(i2+"pointB=Point3(%.3f,%.3f,%.3f)\n"%(bx,by,bz)) + out_file.write(i2+"pointC=Point3(%.3f,%.3f,%.3f)\n"%(cx,cy,cz)) + + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('A_X','%f'%pointA.getX())\n") + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('A_Y','%f'%pointA.getY())\n") + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('A_Z','%f'%pointA.getZ())\n") + + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('B_X','%f'%pointB.getX())\n") + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('B_Y','%f'%pointB.getY())\n") + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('B_Z','%f'%pointB.getZ())\n") + + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('C_X','%f'%pointC.getX())\n") + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('C_Y','%f'%pointC.getY())\n") + out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"].setTag('C_Z','%f'%pointC.getZ())\n") + out_file.write(i2+"\n") + + + for effect in AllScene.particleDict: + parent=AllScene.particleNodes[effect].getParent().getName() + if(parent=="render" or parent=="camera"): + out_file.write(i2+"self.particleDict[\""+ str(effect) +"\"].reparentTo(" + parent + ")\n") + else: + out_file.write(i2+"self.particleDict[\""+ str(effect) +"\"].reparentTo(self." + parent + ")\n") + out_file.write(i2+"\n") + + + #################################################################################################################################################### + # Particle Saving + #################################################################################################################################################### + out_file.write("\n") + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Particle Effects\n") + out_file.write(i2+"# Using Particles:\n") + out_file.write(i2+"# theScene.enableeffect(\"Effect_Name\")\n") + out_file.write(i2+"##########################################################################################################\n\n") + out_file.write(i1+"def starteffects(self,mode,seParticleEffect=None,seParticles=None):\n") + for effect in AllScene.particleDict: + effectS=str(effect) + out_file.write(i2+ "self." + effectS + "=" + effectS + "(mode,seParticleEffect,seParticles)\n") + out_file.write(i2+ "effect=self."+ effectS + ".getEffect()\n") + out_file.write(i2+ "self.particleDict[\"" + effectS + "\"]=effect\n") + out_file.write(i2+ "effect.reparentTo(render)\n") + thenode=AllScene.particleNodes[effect] + out_file.write(i2+ "self.particleDict[\"" + effectS + "\"].setPosHprScale(%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f)\n"%(thenode.getX(),thenode.getY(),thenode.getZ(),thenode.getH(),thenode.getP(),thenode.getR(),thenode.getSx(),thenode.getSy(),thenode.getSz())) + out_file.write("\n") + out_file.write(i2+"return\n") + out_file.write("\n") + out_file.write(i1+"def enableeffect(self,effect_name):\n") + out_file.write(i2+"self.particleDict[effect_name].enable()\n") + out_file.write(i2+"return\n") + out_file.write("\n") + out_file.write(i1+"def disableeffect(self,effect_name):\n") + out_file.write(i2+"self.particleDict[effect_name].disable()\n") + out_file.write(i2+"return\n") + out_file.write("\n") + + + #################################################################################################################################################### + # Animation Blending Methods + #################################################################################################################################################### + out_file.write("\n") + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Animation Blending\n") + out_file.write(i2+"# Using blending:\n") + out_file.write(i2+"# theScene.playBlendAnim(actor,blendname)\n") + out_file.write(i2+"# theScene.stopBlendAnim(actor,blendname)\n") + out_file.write(i2+"# theScene.changeBlendAnim(actor,blendname,blend_amount)\n") + out_file.write(i2+"##########################################################################################################\n\n") + out_file.write(i1+"def playBlendAnim(self,actor,blendName,loop=0):\n") + out_file.write(i2+"actor.enableBlend()\n") + out_file.write(i2+"blendDicts=self.blendAnimDict[actor.getName()]\n") + out_file.write(i2+"blendList=blendDicts[blendName]\n") + out_file.write(i2+"actor.setControlEffect(blendList[0],blendList[2])\n") + out_file.write(i2+"actor.setControlEffect(blendList[1],1.0-blendList[2])\n") + out_file.write(i2+"if(loop):\n") + out_file.write(i2+i1+"actor.loop(blendList[0])\n") + out_file.write(i2+i1+"actor.loop(blendList[1])\n") + out_file.write(i2+"else:\n") + out_file.write(i2+i1+"actor.start(blendList[0])\n") + out_file.write(i2+i1+"actor.start(blendList[1])\n") + out_file.write("\n") + + + out_file.write(i1+"def stopBlendAnim(self,actor,blendName):\n") + out_file.write(i2+"blendDicts=self.blendAnimDict[actor.getName()]\n") + out_file.write(i2+"blendList=blendDicts[blendName]\n") + out_file.write(i2+"actor.stop(blendList[0])\n") + out_file.write(i2+"actor.stop(blendList[1])\n") + out_file.write("\n") + + out_file.write(i1+"def changeBlending(self,actor,blendName,blending):\n") + out_file.write(i2+"blendDicts=self.blendAnimDict[actor.getName()]\n") + out_file.write(i2+"blendList=blendDicts[blendName]\n") + out_file.write(i2+"blendList[2]=blending\n") + out_file.write(i2+"self.blendAnimDict[actor.getName()]={blendName:[blendList[0],blendList[1],blending]}\n") + out_file.write("\n") + + + + #################################################################################################################################################### + # Hide and Show Methods + #################################################################################################################################################### + + out_file.write("\n") + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Hide and Show Methods\n") + out_file.write(i2+"# These will help you hide/show dummies, collision solids, effect nodes etc.\n") + out_file.write(i2+"##########################################################################################################\n\n") + + + out_file.write("\n") + out_file.write(i1+"def hideDummies(self):\n") + out_file.write("\n") + out_file.write(i2+"for dummy in self.dummyDict:\n") + out_file.write(i2+i1+"self.dummyDict[dummy].reparentTo(hidden)\n") + + + out_file.write("\n") + out_file.write(i1+"def hideCollSolids(self):\n") + out_file.write("\n") + out_file.write(i2+"for collSolid in self.collisionDict:\n") + out_file.write(i2+i1+"self.collisionDict[collSolid].hide()\n") + + + out_file.write("\n") + out_file.write(i1+"def hideEffectNodes(self):\n") + out_file.write("\n") + out_file.write(i2+"for effectnode in self.particleNodes:\n") + out_file.write(i2+i1+"self.particleNodes[effectnode].hide()\n") + + + out_file.write("\n") + out_file.write(i1+"def showDummies(self):\n") + out_file.write("\n") + out_file.write(i2+"for dummy in self.dummyDict:\n") + out_file.write(i2+i1+"self.dummyDict[dummy].reparentTo(hidden)\n") + + + out_file.write("\n") + out_file.write(i1+"def showCollSolids(self):\n") + out_file.write("\n") + out_file.write(i2+"for collSolid in self.collisionDict:\n") + out_file.write(i2+i1+"self.collisionDict[collSolid].show()\n") + + + out_file.write("\n") + out_file.write(i1+"def showEffectNodes(self):\n") + out_file.write("\n") + out_file.write(i2+"for effectnode in self.particleNodes:\n") + out_file.write(i2+i1+"self.particleNodes[effectnode].show()\n\n") + + + ########################################################################################################## + # Saving Particle Parameters as a Class + ########################################################################################################## + + out_file.write("\n") + out_file.write(i2+"##########################################################################################################\n") + out_file.write(i2+"# Particle Effects\n") + out_file.write(i2+"# This is where effect parameters are saved in a class\n") + out_file.write(i2+"# The class is then instantiated in the starteffects method and appended to the dictionaries\n") + out_file.write(i2+"##########################################################################################################\n\n") + + for effect in AllScene.particleDict: + + out_file.write("\n\n") + out_file.write("class " + str(effect) + ":\n") + out_file.write(i1+"def __init__(self,mode=1,seParticleEffect=None,seParticles=None):\n") + out_file.write(i2+"if(mode==0):\n") + out_file.write(i2+i1+"self.effect=seParticleEffect.ParticleEffect()\n") + out_file.write(i2+"else:\n") + out_file.write(i2+i1+"self.effect=ParticleEffect.ParticleEffect()\n") + AllScene.particleDict[effect].AppendConfig(out_file) + #out_file.write(i2+"return self.effect\n") + out_file.write("\n\n") + out_file.write(i1+"def starteffect(self):\n") + out_file.write(i2+"pass\n") + out_file.write("\n\n") + out_file.write(i1+"def stopeffect(self):\n") + out_file.write(i2+"pass\n\n") + out_file.write(i1+"def getEffect(self):\n") + out_file.write(i2+"return self.effect\n\n") + + + + #Un-comment the lines below to make this a stand-alone file + #out_file.write("Scene=SavedScene()\n") + #out_file.write("run()\n") + + out_file.close() + + diff --git a/doc/SceneEditor/seForceGroup.py b/doc/SceneEditor/seForceGroup.py new file mode 100644 index 0000000000..33e347a8d4 --- /dev/null +++ b/doc/SceneEditor/seForceGroup.py @@ -0,0 +1,139 @@ +from PandaModules import * +from DirectObject import * +from PhysicsManagerGlobal import * + +import ForceNode +import DirectNotifyGlobal +import sys + +class ForceGroup(DirectObject): + + notify = DirectNotifyGlobal.directNotify.newCategory('ForceGroup') + id = 1 + + def __init__(self, name=None): + """__init__(self)""" + + if (name == None): + self.name = 'ForceGroup-%d' % ForceGroup.id + ForceGroup.id += 1 + else: + self.name = name + + self.node = ForceNode.ForceNode(self.name) + self.nodePath = NodePath(self.node) + self.fEnabled = 0 + + self.particleEffect = None + + def cleanup(self): + self.node.clear() + self.nodePath.removeNode() + del self.nodePath + del self.node + del self.particleEffect + + def enable(self): + """enable(self) + Convenience function to enable all forces in force group""" + for i in range(self.node.getNumForces()): + f = self.node.getForce(i) + f.setActive(1) + self.fEnabled = 1 + + def disable(self): + """disable(self) + Convenience function to disable all forces in force group""" + for i in range(self.node.getNumForces()): + f = self.node.getForce(i) + f.setActive(0) + self.fEnabled = 0 + + def isEnabled(self): + return self.fEnabled + + def addForce(self, force): + """addForce(self, force)""" + self.node.addForce(force) + if (self.particleEffect): + self.particleEffect.addForce(force) + + def removeForce(self, force): + """removeForce(self, force)""" + self.node.removeForce(force) + if (self.particleEffect != None): + self.particleEffect.removeForce(force) + + # Get/set + def getName(self): + """getName(self)""" + return self.name + def getNode(self): + """getNode(self)""" + return self.node + def getNodePath(self): + """getNodePath(self)""" + return self.nodePath + + # Utility functions + def __getitem__(self, index): + """__getItem__(self, index)""" + numForces = self.node.getNumForces() + if ((index < 0) or (index >= numForces)): + raise IndexError + return self.node.getForce(index) + + def __len__(self): + """__len__(self)""" + return self.node.getNumForces() + + def asList(self): + """asList(self)""" + l = [] + for i in range(self.node.getNumForces()): + l.append(self.node.getForce(i)) + return l + + def printParams(self, file = sys.stdout, targ = 'self'): + """printParams(file, targ)""" + i1=" " + i2=i1+i1 + file.write(i2+'# Force parameters\n') + for i in range(self.node.getNumForces()): + f = self.node.getForce(i) + fname = 'force%d' % i + if isinstance(f, LinearForce): + amplitude = f.getAmplitude() + massDependent = f.getMassDependent() + if isinstance(f, LinearCylinderVortexForce): + file.write(i2+fname + ' = LinearCylinderVortexForce(%.4f, %.4f, %.4f, %.4f, %d)\n' % (f.getRadius(), f.getLength(), f.getCoef(), amplitude, massDependent)) + elif isinstance(f, LinearDistanceForce): + radius = f.getRadius() + falloffType = f.getFalloffType() + ftype = 'FTONEOVERR' + if (falloffType == LinearDistanceForce.FTONEOVERR): + ftype = 'FTONEOVERR' + elif (falloffType == LinearDistanceForce.FTONEOVERRSQUARED): + ftype = 'FTONEOVERRSQUARED' + elif (falloffType == LinearDistanceForce.FTONEOVERRCUBED): + ftype = 'FTONEOVERRCUBED' + forceCenter = f.getForceCenter() + if isinstance(f, LinearSinkForce): + file.write(i2+fname + ' = LinearSinkForce(Point3(%.4f, %.4f, %.4f), LinearDistanceForce.%s, %.4f, %.4f, %d)\n' % (forceCenter[0], forceCenter[1], forceCenter[2], ftype, radius, amplitude, massDependent)) + elif isinstance(f, LinearSourceForce): + file.write(i2+fname + ' = LinearSourceForce(Point3(%.4f, %.4f, %.4f), LinearDistanceForce.%s, %.4f, %.4f, %d)\n' % (forceCenter[0], forceCenter[1], forceCenter[2], ftype, radius, amplitude, massDependent)) + elif isinstance(f, LinearFrictionForce): + file.write(i2+fname + ' = LinearFrictionForce(%.4f, %.4f, %d)\n' % (f.getCoef(), amplitude, massDependent)) + elif isinstance(f, LinearJitterForce): + file.write(i2+fname + ' = LinearJitterForce(%.4f, %d)\n' % (amplitude, massDependent)) + elif isinstance(f, LinearNoiseForce): + file.write(i2+fname + ' = LinearNoiseForce(%.4f, %d)\n' % (amplitude, massDependent)) + elif isinstance(f, LinearVectorForce): + vec = f.getLocalVector() + file.write(i2+fname + ' = LinearVectorForce(Vec3(%.4f, %.4f, %.4f), %.4f, %d)\n' % (vec[0], vec[1], vec[2], amplitude, massDependent)) + elif isinstance(f, AngularForce): + if isinstance(f, AngularVectorForce): + vec = f.getLocalVector() + file.write(i2+fname + ' = AngularVectorForce(Vec3(%.4f, %.4f, %.4f))\n' % (vec[0], vec[1], vec[2])) + file.write(i2+fname + '.setActive(%d)\n' % f.getActive()) + file.write(i2+targ + '.addForce(%s)\n' % fname) diff --git a/doc/SceneEditor/seGeometry.py b/doc/SceneEditor/seGeometry.py new file mode 100644 index 0000000000..e4dddc87ae --- /dev/null +++ b/doc/SceneEditor/seGeometry.py @@ -0,0 +1,257 @@ +################################################################# +# seGeometry.py +# Originally from DirectGeometry.py +# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +# +# We didn't change anything essential. +# Just because we customized the seSession from DirectSession, +# So we need related files can follow the change. +# However, we don't want to change anything inside the original directool +# to let them can work with our scene editor. +# (If we do change original directools, it will force user has to install the latest version of OUR Panda) +# +################################################################# + +from PandaModules import * +from PandaObject import * +from DirectGlobals import * +from DirectUtil import * +import math + +class LineNodePath(NodePath): + def __init__(self, parent = None, name = None, + thickness = 1.0, colorVec = VBase4(1)): + + # Initialize the superclass + NodePath.__init__(self) + + if parent is None: + parent = hidden + + # Attach a geomNode to the parent and set self to be + # the resulting node path + self.lineNode = GeomNode("lineNode") + self.assign(parent.attachNewNode( self.lineNode )) + if name: + self.setName(name) + + # Create a lineSegs object to hold the line + ls = self.lineSegs = LineSegs() + # Initialize the lineSegs parameters + ls.setThickness(thickness) + ls.setColor(colorVec) + + def moveTo( self, *_args ): + apply( self.lineSegs.moveTo, _args ) + + def drawTo( self, *_args ): + apply( self.lineSegs.drawTo, _args ) + + def create( self, frameAccurate = 0 ): + self.lineSegs.create( self.lineNode, frameAccurate ) + + def reset( self ): + self.lineSegs.reset() + self.lineNode.removeAllGeoms() + + def isEmpty( self ): + return self.lineSegs.isEmpty() + + def setThickness( self, thickness ): + self.lineSegs.setThickness( thickness ) + + def setColor( self, *_args ): + apply( self.lineSegs.setColor, _args ) + + def setVertex( self, *_args): + apply( self.lineSegs.setVertex, _args ) + + def setVertexColor( self, vertex, *_args ): + apply( self.lineSegs.setVertexColor, (vertex,) + _args ) + + def getCurrentPosition( self ): + return self.lineSegs.getCurrentPosition() + + def getNumVertices( self ): + return self.lineSegs.getNumVertices() + + def getVertex( self, index ): + return self.lineSegs.getVertex(index) + + def getVertexColor( self ): + return self.lineSegs.getVertexColor() + + def drawArrow(self, sv, ev, arrowAngle, arrowLength): + """ + Do the work of moving the cursor around to draw an arrow from + sv to ev. Hack: the arrows take the z value of the end point + """ + self.moveTo(sv) + self.drawTo(ev) + v = sv - ev + # Find the angle of the line + angle = math.atan2(v[1], v[0]) + # Get the arrow angles + a1 = angle + deg2Rad(arrowAngle) + a2 = angle - deg2Rad(arrowAngle) + # Get the arrow points + a1x = arrowLength * math.cos(a1) + a1y = arrowLength * math.sin(a1) + a2x = arrowLength * math.cos(a2) + a2y = arrowLength * math.sin(a2) + z = ev[2] + self.moveTo(ev) + self.drawTo(Point3(ev + Point3(a1x, a1y, z))) + self.moveTo(ev) + self.drawTo(Point3(ev + Point3(a2x, a2y, z))) + + def drawArrow2d(self, sv, ev, arrowAngle, arrowLength): + """ + Do the work of moving the cursor around to draw an arrow from + sv to ev. Hack: the arrows take the z value of the end point + """ + self.moveTo(sv) + self.drawTo(ev) + v = sv - ev + # Find the angle of the line + angle = math.atan2(v[2], v[0]) + # Get the arrow angles + a1 = angle + deg2Rad(arrowAngle) + a2 = angle - deg2Rad(arrowAngle) + # Get the arrow points + a1x = arrowLength * math.cos(a1) + a1y = arrowLength * math.sin(a1) + a2x = arrowLength * math.cos(a2) + a2y = arrowLength * math.sin(a2) + self.moveTo(ev) + self.drawTo(Point3(ev + Point3(a1x, 0.0, a1y))) + self.moveTo(ev) + self.drawTo(Point3(ev + Point3(a2x, 0.0, a2y))) + + def drawLines(self, lineList): + """ + Given a list of lists of points, draw a separate line for each list + """ + for pointList in lineList: + apply(self.moveTo, pointList[0]) + for point in pointList[1:]: + apply(self.drawTo, point) + +## +## Given a point in space, and a direction, find the point of intersection +## of that ray with a plane at the specified origin, with the specified normal +def planeIntersect (lineOrigin, lineDir, planeOrigin, normal): + t = 0 + offset = planeOrigin - lineOrigin + t = offset.dot(normal) / lineDir.dot(normal) + hitPt = lineDir * t + return hitPt + lineOrigin + +def getNearProjectionPoint(nodePath): + # Find the position of the projection of the specified node path + # on the near plane + origin = nodePath.getPos(SEditor.camera) + # project this onto near plane + if origin[1] != 0.0: + return origin * (SEditor.dr.near / origin[1]) + else: + # Object is coplaner with camera, just return something reasonable + return Point3(0, SEditor.dr.near, 0) + +def getScreenXY(nodePath): + # Where does the node path's projection fall on the near plane + nearVec = getNearProjectionPoint(nodePath) + # Clamp these coordinates to visible screen + nearX = CLAMP(nearVec[0], SEditor.dr.left, SEditor.dr.right) + nearY = CLAMP(nearVec[2], SEditor.dr.bottom, SEditor.dr.top) + # What percentage of the distance across the screen is this? + percentX = (nearX - SEditor.dr.left)/SEditor.dr.nearWidth + percentY = (nearY - SEditor.dr.bottom)/SEditor.dr.nearHeight + # Map this percentage to the same -1 to 1 space as the mouse + screenXY = Vec3((2 * percentX) - 1.0,nearVec[1],(2 * percentY) - 1.0) + # Return the resulting value + return screenXY + +def getCrankAngle(center): + # Used to compute current angle of mouse (relative to the coa's + # origin) in screen space + x = SEditor.dr.mouseX - center[0] + y = SEditor.dr.mouseY - center[2] + return (180 + rad2Deg(math.atan2(y,x))) + +def relHpr(nodePath, base, h, p, r): + # Compute nodePath2newNodePath relative to base coordinate system + # nodePath2base + mNodePath2Base = nodePath.getMat(base) + # delta scale, orientation, and position matrix + mBase2NewBase = Mat4() + composeMatrix(mBase2NewBase, UNIT_VEC, VBase3(h,p,r), ZERO_VEC, + CSDefault) + # base2nodePath + mBase2NodePath = base.getMat(nodePath) + # nodePath2 Parent + mNodePath2Parent = nodePath.getMat() + # Compose the result + resultMat = mNodePath2Base * mBase2NewBase + resultMat = resultMat * mBase2NodePath + resultMat = resultMat * mNodePath2Parent + # Extract and apply the hpr + hpr = Vec3(0) + decomposeMatrix(resultMat, VBase3(), hpr, VBase3(), + CSDefault) + nodePath.setHpr(hpr) + +# Quaternion interpolation +def qSlerp(startQuat, endQuat, t): + startQ = Quat(startQuat) + destQuat = Quat.identQuat() + # Calc dot product + cosOmega = (startQ.getI() * endQuat.getI() + + startQ.getJ() * endQuat.getJ() + + startQ.getK() * endQuat.getK() + + startQ.getR() * endQuat.getR()) + # If the above dot product is negative, it would be better to + # go between the negative of the initial and the final, so that + # we take the shorter path. + if ( cosOmega < 0.0 ): + cosOmega *= -1 + startQ.setI(-1 * startQ.getI()) + startQ.setJ(-1 * startQ.getJ()) + startQ.setK(-1 * startQ.getK()) + startQ.setR(-1 * startQ.getR()) + if ((1.0 + cosOmega) > Q_EPSILON): + # usual case + if ((1.0 - cosOmega) > Q_EPSILON): + # usual case + omega = math.acos(cosOmega) + sinOmega = math.sin(omega) + startScale = math.sin((1.0 - t) * omega)/sinOmega + endScale = math.sin(t * omega)/sinOmega + else: + # ends very close + startScale = 1.0 - t + endScale = t + destQuat.setI(startScale * startQ.getI() + + endScale * endQuat.getI()) + destQuat.setJ(startScale * startQ.getJ() + + endScale * endQuat.getJ()) + destQuat.setK(startScale * startQ.getK() + + endScale * endQuat.getK()) + destQuat.setR(startScale * startQ.getR() + + endScale * endQuat.getR()) + else: + # ends nearly opposite + destQuat.setI(-startQ.getJ()) + destQuat.setJ(startQ.getI()) + destQuat.setK(-startQ.getR()) + destQuat.setR(startQ.getK()) + startScale = math.sin((0.5 - t) * math.pi) + endScale = math.sin(t * math.pi) + destQuat.setI(startScale * startQ.getI() + + endScale * endQuat.getI()) + destQuat.setJ(startScale * startQ.getJ() + + endScale * endQuat.getJ()) + destQuat.setK(startScale * startQ.getK() + + endScale * endQuat.getK()) + return destQuat + diff --git a/doc/SceneEditor/seGrid.py b/doc/SceneEditor/seGrid.py new file mode 100644 index 0000000000..65d5221a6d --- /dev/null +++ b/doc/SceneEditor/seGrid.py @@ -0,0 +1,170 @@ +################################################################# +# seGrid.py +# Originally from DirectGrid.py +# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +# +# We didn't change anything essential. +# Just because we customized the seSession from DirectSession, +# So we need related files can follow the change. +# However, we don't want to change anything inside the original directool +# to let them can work with our scene editor. +# (If we do change original directools, it will force user has to install the latest version of OUR Panda) +# +################################################################# +from PandaObject import * +from DirectUtil import * +from seGeometry import * + +class DirectGrid(NodePath,PandaObject): + def __init__(self): + # Initialize superclass + NodePath.__init__(self) + self.assign(hidden.attachNewNode('DirectGrid')) + # Don't wireframe or light + useDirectRenderStyle(self) + + # Load up grid parts to initialize grid object + # Polygon used to mark grid plane + self.gridBack = loader.loadModel('models/misc/gridBack') + self.gridBack.reparentTo(self) + self.gridBack.setColor(0.5,0.5,0.5,0.5) + + # Grid Lines + self.lines = self.attachNewNode('gridLines') + self.minorLines = LineNodePath(self.lines) + self.minorLines.lineNode.setName('minorLines') + self.minorLines.setColor(VBase4(0.3,0.55,1,1)) + self.minorLines.setThickness(1) + + self.majorLines = LineNodePath(self.lines) + self.majorLines.lineNode.setName('majorLines') + self.majorLines.setColor(VBase4(0.3,0.55,1,1)) + self.majorLines.setThickness(5) + + self.centerLines = LineNodePath(self.lines) + self.centerLines.lineNode.setName('centerLines') + self.centerLines.setColor(VBase4(1,0,0,0)) + self.centerLines.setThickness(3) + + # Small marker to hilight snap-to-grid point + self.snapMarker = loader.loadModel('models/misc/sphere') + self.snapMarker.node().setName('gridSnapMarker') + self.snapMarker.reparentTo(self) + self.snapMarker.setColor(1,0,0,1) + self.snapMarker.setScale(0.3) + self.snapPos = Point3(0) + + # Initialize Grid characteristics + self.fXyzSnap = 1 + self.fHprSnap = 1 + self.gridSize = 100.0 + self.gridSpacing = 5.0 + self.snapAngle = 15.0 + self.enable() + + def enable(self): + self.reparentTo(SEditor.group) + self.updateGrid() + self.fEnabled = 1 + + def disable(self): + self.reparentTo(hidden) + self.fEnabled = 0 + + def toggleGrid(self): + if self.fEnabled: + self.disable() + else: + self.enable() + + def isEnabled(self): + return self.fEnabled + + def updateGrid(self): + # Update grid lines based upon current grid spacing and grid size + # First reset existing grid lines + self.minorLines.reset() + self.majorLines.reset() + self.centerLines.reset() + + # Now redraw lines + numLines = math.ceil(self.gridSize/self.gridSpacing) + scaledSize = numLines * self.gridSpacing + + center = self.centerLines + minor = self.minorLines + major = self.majorLines + for i in range(-numLines,numLines + 1): + if i == 0: + center.moveTo(i * self.gridSpacing, -scaledSize, 0) + center.drawTo(i * self.gridSpacing, scaledSize, 0) + center.moveTo(-scaledSize, i * self.gridSpacing, 0) + center.drawTo(scaledSize, i * self.gridSpacing, 0) + else: + if (i % 5) == 0: + major.moveTo(i * self.gridSpacing, -scaledSize, 0) + major.drawTo(i * self.gridSpacing, scaledSize, 0) + major.moveTo(-scaledSize, i * self.gridSpacing, 0) + major.drawTo(scaledSize, i * self.gridSpacing, 0) + else: + minor.moveTo(i * self.gridSpacing, -scaledSize, 0) + minor.drawTo(i * self.gridSpacing, scaledSize, 0) + minor.moveTo(-scaledSize, i * self.gridSpacing, 0) + minor.drawTo(scaledSize, i * self.gridSpacing, 0) + + center.create() + minor.create() + major.create() + self.gridBack.setScale(scaledSize) + + def setXyzSnap(self, fSnap): + self.fXyzSnap = fSnap + + def getXyzSnap(self): + return self.fXyzSnap + + def setHprSnap(self, fSnap): + self.fHprSnap = fSnap + + def getHprSnap(self): + return self.fHprSnap + + def computeSnapPoint(self, point): + # Start of with current point + self.snapPos.assign(point) + # Snap if necessary + if self.fXyzSnap: + self.snapPos.set( + ROUND_TO(self.snapPos[0], self.gridSpacing), + ROUND_TO(self.snapPos[1], self.gridSpacing), + ROUND_TO(self.snapPos[2], self.gridSpacing)) + + # Move snap marker to this point + self.snapMarker.setPos(self.snapPos) + + # Return the hit point + return self.snapPos + + def computeSnapAngle(self, angle): + return ROUND_TO(angle, self.snapAngle) + + def setSnapAngle(self, angle): + self.snapAngle = angle + + def getSnapAngle(self): + return self.snapAngle + + def setGridSpacing(self, spacing): + self.gridSpacing = spacing + self.updateGrid() + + def getGridSpacing(self): + return self.gridSpacing + + def setGridSize(self, size): + # Set size of grid back and redraw lines + self.gridSize = size + self.updateGrid() + + def getGridSize(self): + return self.gridSize diff --git a/doc/SceneEditor/seLights.py b/doc/SceneEditor/seLights.py new file mode 100644 index 0000000000..543e13d048 --- /dev/null +++ b/doc/SceneEditor/seLights.py @@ -0,0 +1,632 @@ +################################################################# +# seLights.py +# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +################################################################# +from PandaObject import * +from string import lower +import DirectUtil +import string + + +class seLight(NodePath): + ################################################################# + # seLight(NodePath) + # This is an object for keeping light data and let we can manipulate + # lights as otherNopaths. + # This idea basically is from directLight. + # But the way directLight object worked is not we want in our + # sceneEditor. So, we wrote one by ourself. + ################################################################# + def __init__(self, light, parent, type, + lightcolor=VBase4(0.3,0.3,0.3,1), + specularColor = VBase4(1), + position = Point3(0,0,0), + orientation = Vec3(1,0,0), + constant = 1.0, + linear = 0.0, + quadratic = 0.0, + exponent = 0.0, + tag="", + lence = None): + ################################################################# + # __init__(self, light, parent, type, + # lightcolor=VBase4(0.3,0.3,0.3,1), + # specularColor = VBase4(1), + # position = Point3(0,0,0), + # orientation = Vec3(1,0,0), + # constant = 1.0, + # linear = 0.0, + # quadratic = 0.0, + # exponent = 0.0, + # tag="", + # lence = None): + # This constructor will create a light node inside it and upcast + # this light node to itself as a nodePath. + # Also, it will load a model as a reference to itself on the secene so that + # user can easily recognize where is the light and can easily manipulate + # by mouse picking and widget control. + ################################################################# + # Initialize the super class + NodePath.__init__(self) + # Initialize and save values + self.light = light + self.type = type + self.lightcolor=lightcolor + self.specularColor = specularColor + self.position = position + self.orientation = orientation + self.constant = constant + self.linear = linear + self.quadratic = quadratic + self.exponent = exponent + self.lence = lence + self.active = True + + if isinstance(light, Spotlight): + node = light.upcastToLensNode() + else: + node = light.upcastToPandaNode() + + # Attach node to self + self.LightNode=parent.attachNewNode(node) + self.LightNode.setTag("Metadata",tag) + if(self.type=='spot'): + self.LightNode.setHpr(self.orientation) + self.LightNode.setPos(self.position) + else: + self.LightNode.setHpr(self.orientation) + self.LightNode.setPos(self.position) + + + self.assign(self.LightNode) + if(self.type=='spot'): + self.helpModel = loader.loadModelCopy( "Spotlight" ) + elif(self.type=='point'): + self.helpModel = loader.loadModelCopy( "Pointlight" ) + elif(self.type=='directional'): + self.helpModel = loader.loadModelCopy( "Dirlight" ) + else: + self.helpModel = loader.loadModelCopy( "misc/Sphere" ) + self.helpModel.setColor(self.lightcolor) + self.helpModel.reparentTo(self) + DirectUtil.useDirectRenderStyle(self.helpModel) + if not ((self.type == 'directional')or(self.type == 'point')or(self.type == 'spot')): + self.helpModel.hide() + + def getLight(self): + ################################################################# + # getLight(self) + # This function will return the light object it contains. + ################################################################# + + return self.light + + def getLightColor(self): + ################################################################# + # getLightColor(self) + # This function will return the color of the light color of this light node. + ################################################################# + return self.lightcolor + + def getName(self): + ################################################################# + # getName(self) + # This function will return the name of this light. + ################################################################# + return self.light.getName() + + def rename(self,name): + ################################################################# + # rename(self, name) + # User should specify a string object, name, as the new name of this + # light node. This function will rename itself and light object + # to this new name. + ################################################################# + self.light.setName(name) + self.setName(name) + + def getType(self): + ################################################################# + # getType(self) + # This function will return a string which is the type of this + # light node. (We only have four types of light) + ################################################################# + return self.type + + def setColor(self,color): + ################################################################# + # setColor(self, color) + # This function takes one input parameter, color, which is a + # VBase4 object. This function will simply set this object into + # light node it has to change the property of light. + # Also, if the light type is either "directional" or "point", which + # means it has a reference model with it, this function will also + # change the reference model's color to input value. + ################################################################# + self.light.setColor(color) + self.lightcolor = color + if (self.type == 'directional')or(self.type == 'point'): + self.helpModel.setColor(self.lightcolor) + return + + def getSpecColor(self): + ################################################################# + # getSpecColor(self) + # This function will return the specular color of the light. + # Although you can call this function for all kinds of light, + # it will only meanful if this light is not a ambient light. + ################################################################# + return self.specularColor + + def setSpecColor(self,color): + ################################################################# + # setSpecColor(self, color) + # This function can be used to set the specular color of the light. + # Although you can call this function for all kinds of light, + # it will only meanful if the light type is not "ambient" + ################################################################# + self.light.setSpecularColor(color) + self.specularcolor = color + return + + def getPosition(self): + ################################################################# + # getPosition(self) + # getPosition(self) + # This functioln will return a Point3 object which contains + # the x, y, z position data of this light node. + # It only has meaning for "point Light" and "Directional light" + ################################################################# + self.position = self.LightNode.getPos() + return self.position + + def setPosition(self, pos): + ################################################################# + # setPosition(self, pos) + # This function will take a Point3 object as a input. + # Then, this function will set the itself andd light node to the + # target point. + ################################################################# + self.LightNode.setPos(pos) + self.position = pos + return + + def getOrientation(self): + ################################################################# + # getOrientation(self) + # This function will return a Vec3-type object which contains the + # orientation data of this light node + # + # This function will only have meaning for point light and directional light. + # + ################################################################# + self.orientation = self.LightNode.getHpr() + return self.orientation + + def setOrientation(self,orient): + ################################################################# + # setOrientation(self, orient) + # This funtction will take a Vec3-type object as an input. + # Then this function will set itself nad light node to face + # the target orientation. + # + # This function only has meaning for point light and directional light + # type of lights. + # + ################################################################# + self.LightNode.setHpr(orient) + self.orientation = orient + return + + def getAttenuation(self): + ################################################################# + # getAttenuation(self) + # This function will return a Vec3 type of object which contains + # the constant, linear and quadratic attenuation for this light node. + # + # This function will only have meaning for point light and spot light + # tyoe of lights. + # + ################################################################# + return Vec3(self.constant,self.linear,self.quadratic) + + def setConstantAttenuation(self, value): + ################################################################# + # setConstantAttenuation(self, value) + # This function will take a float number as an input. + # Then, this function will set the Constant Atenuation value + # to this number. + ################################################################# + self.light.setAttenuation(Vec3(value, self.linear, self.quadratic)) + self.constant = value + return + + def setLinearAttenuation(self, value): + ################################################################# + # setLinearAttenuation(self, value) + # This function will take a float number as an input. + # Then, this function will set the Linear Atenuation value + # to this number. + ################################################################# + self.light.setAttenuation(Vec3(self.constant, value, self.quadratic)) + self.linear = value + return + + def setQuadraticAttenuation(self, value): + ################################################################# + # setQuadraticAttenuation(self, value) + # This function will take a float number as an input. + # Then, this function will set the Quadratic Atenuation value + # to this number. + ################################################################# + self.light.setAttenuation(Vec3(self.constant, self.linear, value)) + self.quadratic = value + return + + def getExponent(self): + ################################################################# + # getExponent(self) + # This function will return the value of the Exponent Attenuation + # of this light node. (float) + ################################################################# + return self.exponent + + def setExponent(self, value): + ################################################################# + # setExponent(self, value) + # This function will take a float number as an input. + # Then, this function will set the Exponent Atenuation value + # to this number. + ################################################################# + self.light.setExponent(value) + self.exponent = value + return + +class seLightManager(NodePath): + ################################################################# + # seLightManager(NodePath) + # This is the class we used to control al lightings in our sceneEditor. + ################################################################# + def __init__(self): + # Initialize the superclass + NodePath.__init__(self) + # Create a node for the lights + self.lnode=render.attachNewNode('Lights') + self.assign(self.lnode) + # Create a light attrib + self.lightAttrib = LightAttrib.makeAllOff() + self.lightDict = {} + self.ambientCount = 0 + self.directionalCount = 0 + self.pointCount = 0 + self.spotCount = 0 + # Originally, we don't do this load model thing. + # But the problem is, if we don't, then it will cause some + # Bounding calculation error... + self.helpModel = loader.loadModelCopy( "misc/sphere" ) + self.helpModel.reparentTo(self) + self.helpModel.hide() + + + + + def create(self, type = 'ambient', + lightcolor=VBase4(0.3,0.3,0.3,1), + specularColor = VBase4(1), + position = Point3(0,0,0), + orientation = Vec3(1,0,0), + constant = 1.0, + linear = 0.0, + quadratic = 0.0, + exponent = 0.0, + tag= "", + name='DEFAULT_NAME'): + ################################################################# + # create(self, type = 'ambient', + # lightcolor=VBase4(0.3,0.3,0.3,1), + # specularColor = VBase4(1), + # position = Point3(0,0,0), + # orientation = Vec3(1,0,0), + # constant = 1.0, + # linear = 0.0, + # quadratic = 0.0, + # exponent = 0.0, + # tag= "", + # name='DEFAULT_NAME') + # As you can see, once user call this function and specify those + # variables, this function will create a seLight node. + # In the default, the light which just has been created will be + # set to off. + ################################################################# + ### create the light + + lence = None + + if type == 'ambient': + self.ambientCount += 1 + if(name=='DEFAULT_NAME'): + light = AmbientLight('ambient_' + `self.ambientCount`) + else: + light = AmbientLight(name) + + light.setColor(lightcolor) + + elif type == 'directional': + self.directionalCount += 1 + if(name=='DEFAULT_NAME'): + light = DirectionalLight('directional_' + `self.directionalCount`) + else: + light = DirectionalLight(name) + + light.setColor(lightcolor) + light.setSpecularColor(specularColor) + + elif type == 'point': + self.pointCount += 1 + if(name=='DEFAULT_NAME'): + light = PointLight('point_' + `self.pointCount`) + else: + light = PointLight(name) + + light.setColor(lightcolor) + light.setSpecularColor(specularColor) + light.setAttenuation(Vec3(constant, linear, quadratic)) + + elif type == 'spot': + self.spotCount += 1 + if(name=='DEFAULT_NAME'): + light = Spotlight('spot_' + `self.spotCount`) + else: + light = Spotlight(name) + + light.setColor(lightcolor) + lence = PerspectiveLens() + light.setLens(lence) + light.setSpecularColor(specularColor) + light.setAttenuation(Vec3(constant, linear, quadratic)) + light.setExponent(exponent) + else: + print 'Invalid light type' + return None + + # Create the seLight objects and put the light object we just created into it. + lightNode = seLight(light,self,type, + lightcolor=lightcolor, + specularColor = specularColor, + position = position, + orientation = orientation, + constant = constant, + linear = linear, + quadratic = quadratic, + exponent = exponent, + tag=tag, + lence = lence + ) + self.lightDict[light.getName()] = lightNode + self.setOn(lightNode) + + + return self.lightDict.keys(),lightNode + + def addLight(self, light): + ################################################################# + # addLight(self, light) + # This function will put light in and save its properties in to a seLight Node + # Attention!! + # only Spotlight obj nneds to be specified a lens node first. i.e. setLens() first! + ################################################################# + type = lower(light.getType().getName()) + + light.upcastToNamable() + + specularColor = VBase4(1) + position = Point3(0,0,0) + orientation = Vec3(1,0,0) + constant = 1.0 + linear = 0.0 + quadratic = 0.0 + exponent = 0.0 + lence = None + + lightcolor = light.getColor() + if type == 'ambientlight': + type = 'ambient' + self.ambientCount += 1 + elif type == 'directionallight': + type = 'directional' + self.directionalCount += 1 + orientation = light.getDirection() + position = light.getPoint() + specularColor = light.getSpecularColor() + elif type == 'pointlight': + type = 'point' + self.pointCount += 1 + position = light.getPoint() + specularColor = light.getSpecularColor() + Attenuation = light.getAttenuation() + constant = Attenuation.getX() + linear = Attenuation.getY() + quadratic = Attenuation.getZ() + elif type == 'spotlight': + type = 'spot' + self.spotCount += 1 + specularColor = light.getSpecularColor() + Attenuation = light.getAttenuation() + constant = Attenuation.getX() + linear = Attenuation.getY() + quadratic = Attenuation.getZ() + exponent = light.getExponent() + else: + print 'Invalid light type' + return None + + lightNode = seLight(light,self,type, + lightcolor=lightcolor, + specularColor = specularColor, + position = position, + orientation = orientation, + constant = constant, + linear = linear, + quadratic = quadratic, + exponent = exponent, + lence = lence) + self.lightDict[light.getName()] = lightNode + self.setOn(lightNode) + + return self.lightDict.keys(),lightNode + + def delete(self, name, removeEntry = True): + ################################################################# + # delete(self, name, removeEntry = True) + # This function will remove light node had the same name with user input. + # Also, you can specify the removeEntry to decide to remove the entry from the lightDict or not. + # Normaly, you alway want to remove the entry from the dictionary. Thsi is only used for "deleteAll" function. + ################################################################# + type = self.lightDict[name].getType() + if type == 'ambient': + self.ambientCount -= 1 + elif type == 'directional': + self.directionalCount -= 1 + elif type == 'point': + self.pointCount -= 1 + elif type == 'spot': + self.spotCount -= 1 + self.setOff(self.lightDict[name]) + self.lightDict[name].removeChildren() + self.lightDict[name].removeNode() + if removeEntry: + del self.lightDict[name] + return self.lightDict.keys() + + def deleteAll(self): + ################################################################# + # deleteAll(self) + # This function will keep calling delete and put exist lights in + # until all lights have been eliminated. + ################################################################# + for name in self.lightDict: + self.delete(name, removeEntry = False) + + self.lightDict.clear() + + def isLight(self,name): + ################################################################# + # isLight(self.name) + # Use a string as a index to check if there existing a light named "name" + ################################################################# + return self.lightDict.has_key(name) + + def rename(self,oName,nName): + ################################################################# + # rename(self, oName, nName) + # This function will reanem the light named "oName(String)" to + # nName(String) + ################################################################# + if self.isLight(oName): + lightNode = self.lightDict[oName] + self.lightDict[nName] = lightNode + lightNode.rename(nName) + del self.lightDict[oName] + return self.lightDict.keys(),lightNode + else: + print '----Light Mnager: No such Light!' + + def getLightNodeList(self): + ################################################################# + # getLightNodeList(self) + # Return a list which contains all seLight nodes + ################################################################# + list = [] + for name in self.lightDict: + list.append(self.lightDict[name]) + return list + + def getLightNodeDict(self): + ################################################################# + # getLightNodeDict(self) + # Return the light dictionary itself. + # + # Attention! + # Because it doesn't make a copy when you return a dictionary, it + # means when you can change the value from outside and that change + # will directly reflect back to here. + # + ################################################################# + return self.lightDict + + def getLightList(self): + ################################################################# + # getLightList(self) + # Return a list which contains names of all lights. + ################################################################# + list = [] + for name in self.lightDict: + list.append(name) + return list + + def getLightNode(self,lightName): + ################################################################# + # getLightNode(self, lightName) + # This function will return a seLight Node using a string, lightName. as a index. + ################################################################# + if lightName in self.lightDict: + return self.lightDict[lightName] + + def allOn(self): + ################################################################# + # allOb(self) + # Enable the lighting system. + ################################################################# + # Turn on all lighting + render.node().setAttrib(self.lightAttrib) + # Make sure there is a default material + render.setMaterial(Material()) + + def allOff(self): + ################################################################# + # allOff(self) + # Disable whole lighting system + ################################################################# + # Turn off all lighting + render.node().clearAttrib(LightAttrib.getClassType()) + + def toggle(self): + ################################################################# + # toggle(self) + # Toggles light attribute, but doesn't toggle individual lights + ################################################################# + if render.node().hasAttrib(LightAttrib.getClassType()): + self.allOff() + else: + self.allOn() + + def setOn(self, lightNode): + ################################################################# + # setOn(lightNode) + # This function will enable the input seLight node. + # If the light system itself is down, activate it. + ################################################################# + self.lightAttrib = self.lightAttrib.addLight(lightNode.getLight()) + lightNode.active = True + if render.node().hasAttrib(LightAttrib.getClassType()): + render.node().setAttrib(self.lightAttrib) + + + def setOff(self, lightNode): + ################################################################# + # setOff(self, lightNode) + # This function will disable the input seLight node + # If the light system itself is down, activate it. + ################################################################# + lightNode.active = False + self.lightAttrib = self.lightAttrib.removeLight(lightNode.getLight()) + if render.node().hasAttrib(LightAttrib.getClassType()): + render.node().setAttrib(self.lightAttrib) + + def getList(self): + ################################################################# + # getList(self) + # This function actually has the same functionality with getLightList(), + # but this one should be more efficient. + ################################################################# + return self.lightDict.keys() diff --git a/doc/SceneEditor/seManipulation.py b/doc/SceneEditor/seManipulation.py new file mode 100644 index 0000000000..2cec90079a --- /dev/null +++ b/doc/SceneEditor/seManipulation.py @@ -0,0 +1,956 @@ +################################################################# +# seManipulation.py +# Originally from DirectManipulation.py +# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +# +# We didn't change anything essential. +# Just because we customized the seSession from DirectSession, +# So we need related files can follow the change. +# However, we don't want to change anything inside the original directool +# to let them can work with our scene editor. +# (If we do change original directools, it will force user has to install the latest version of OUR Panda) +# +################################################################# + +from PandaObject import * +from DirectGlobals import * +from DirectUtil import * +from seGeometry import * +import Task + +class DirectManipulationControl(PandaObject): + def __init__(self): + # Create the grid + self.objectHandles = ObjectHandles() + self.hitPt = Point3(0) + self.prevHit = Vec3(0) + self.rotationCenter = Point3(0) + self.initScaleMag = 1 + self.manipRef = SEditor.group.attachNewNode('manipRef') + self.hitPtDist = 0 + self.constraint = None + self.rotateAxis = 'x' + self.lastCrankAngle = 0 + self.fSetCoa = 0 + self.fHitInit = 1 + self.fScaleInit = 1 + self.fWidgetTop = 0 + self.fFreeManip = 1 + self.fScaling = 0 + self.mode = None + self.actionEvents = [ + ['DIRECT-mouse1', self.manipulationStart], + ['DIRECT-mouse1Up', self.manipulationStop], + ['tab', self.toggleObjectHandlesMode], + ['.', self.objectHandles.multiplyScalingFactorBy, 2.0], + ['>', self.objectHandles.multiplyScalingFactorBy, 2.0], + [',', self.objectHandles.multiplyScalingFactorBy, 0.5], + ['<', self.objectHandles.multiplyScalingFactorBy, 0.5], + ['shift-f', self.objectHandles.growToFit], + ['i', self.plantSelectedNodePath], + ] + + def manipulationStart(self, modifiers): + # Start out in select mode + self.mode = 'select' + # Check for a widget hit point + entry = SEditor.iRay.pickWidget() + # Did we hit a widget? + if entry: + # Yes! + self.hitPt.assign(entry.getFromIntersectionPoint()) + self.hitPtDist = Vec3(self.hitPt).length() + # Constraint determined by nodes name + self.constraint = entry.getIntoNodePath().getName() + else: + # Nope, off the widget, no constraint + self.constraint = None + # Check to see if we are moving the object + # We are moving the object if we either wait long enough + taskMgr.doMethodLater(MANIPULATION_MOVE_DELAY, + self.switchToMoveMode, + 'manip-move-wait') + # Or we move far enough + self.moveDir = None + watchMouseTask = Task.Task(self.watchMouseTask) + watchMouseTask.initX = SEditor.dr.mouseX + watchMouseTask.initY = SEditor.dr.mouseY + taskMgr.add(watchMouseTask, 'manip-watch-mouse') + + def switchToMoveMode(self, state): + taskMgr.remove('manip-watch-mouse') + self.mode = 'move' + self.manipulateObject() + return Task.done + + def watchMouseTask(self, state): + if (((abs (state.initX - SEditor.dr.mouseX)) > 0.01) or + ((abs (state.initY - SEditor.dr.mouseY)) > 0.01)): + taskMgr.remove('manip-move-wait') + self.mode = 'move' + self.manipulateObject() + return Task.done + else: + return Task.cont + + def manipulationStop(self,xy=[]): + taskMgr.remove('manipulateObject') + taskMgr.remove('manip-move-wait') + taskMgr.remove('manip-watch-mouse') + # depending on flag..... + if self.mode == 'select': + # Check for object under mouse + # Don't intersect with hidden or backfacing objects + skipFlags = SKIP_HIDDEN | SKIP_BACKFACE + # Skip camera (and its children), unless control key is pressed + skipFlags |= SKIP_CAMERA * (1 - base.getControl()) + entry = SEditor.iRay.pickGeom(skipFlags = skipFlags) + if entry: + # Record hit point information + self.hitPt.assign(entry.getFromIntersectionPoint()) + self.hitPtDist = Vec3(self.hitPt).length() + # Select it + SEditor.select(entry.getIntoNodePath(), SEditor.fShift) + else: + SEditor.deselectAll() + else: + self.manipulateObjectCleanup() + + def manipulateObjectCleanup(self): + if self.fScaling: + # We had been scaling, need to reset object handles + self.objectHandles.transferObjectHandlesScale() + self.fScaling = 0 + SEditor.selected.highlightAll() + self.objectHandles.showAllHandles() + self.objectHandles.hideGuides() + # Restart followSelectedNodePath task + self.spawnFollowSelectedNodePathTask() + messenger.send('DIRECT_manipulateObjectCleanup') + + def spawnFollowSelectedNodePathTask(self): + # If nothing selected, just return + if not SEditor.selected.last: + return + # Clear out old task to make sure + taskMgr.remove('followSelectedNodePath') + # Where are the object handles relative to the selected object + pos = VBase3(0) + hpr = VBase3(0) + decomposeMatrix(SEditor.selected.last.mCoa2Dnp, + VBase3(0), hpr, pos, CSDefault) + # Create the task + t = Task.Task(self.followSelectedNodePathTask) + # Update state variables + t.pos = pos + t.hpr = hpr + t.base = SEditor.selected.last + # Spawn the task + taskMgr.add(t, 'followSelectedNodePath') + + def followSelectedNodePathTask(self, state): + SEditor.widget.setPosHpr(state.base, state.pos, state.hpr) + return Task.cont + + def enableManipulation(self): + # Accept events + for event in self.actionEvents: + self.accept(event[0], event[1], extraArgs = event[2:]) + + def disableManipulation(self): + # Ignore events + for event in self.actionEvents: + self.ignore(event[0]) + self.removeManipulateObjectTask() + taskMgr.remove('manipulateObject') + taskMgr.remove('manip-move-wait') + taskMgr.remove('manip-watch-mouse') + taskMgr.remove('highlightWidgetTask') + taskMgr.remove('resizeObjectHandles') + + def toggleObjectHandlesMode(self): + self.fSetCoa = 1 - self.fSetCoa + if self.fSetCoa: + self.objectHandles.coaModeColor() + else: + self.objectHandles.manipModeColor() + + def removeManipulateObjectTask(self): + taskMgr.remove('manipulateObject') + + def manipulateObject(self): + # Only do this if something is selected + if SEditor.selected: + # Remove the task to keep the widget attached to the object + taskMgr.remove('followSelectedNodePath') + # and the task to highlight the widget + taskMgr.remove('highlightWidgetTask') + # Set manipulation flag + self.fManip = 1 + # Record undo point + SEditor.pushUndo(SEditor.selected) + # Update object handles visibility + self.objectHandles.showGuides() + self.objectHandles.hideAllHandles() + self.objectHandles.showHandle(self.constraint) + # Record relationship between selected nodes and widget + SEditor.selected.getWrtAll() + # hide the bbox of the selected objects during interaction + SEditor.selected.dehighlightAll() + # Send event to signal start of manipulation + messenger.send('DIRECT_manipulateObjectStart') + # Manipulate the real object with the constraint + # The constraint is passed as the name of the node + self.spawnManipulateObjectTask() + + def spawnManipulateObjectTask(self): + # reset hit-pt flag + self.fHitInit = 1 + self.fScaleInit = 1 + # record initial offset between widget and camera + t = Task.Task(self.manipulateObjectTask) + t.fMouseX = abs(SEditor.dr.mouseX) > 0.9 + t.fMouseY = abs(SEditor.dr.mouseY) > 0.9 + if t.fMouseX: + t.constrainedDir = 'y' + else: + t.constrainedDir = 'x' + # Compute widget's xy coords in screen space + t.coaCenter = getScreenXY(SEditor.widget) + # These are used to rotate about view vector + if t.fMouseX and t.fMouseY: + t.lastAngle = getCrankAngle(t.coaCenter) + taskMgr.add(t, 'manipulateObject') + + def manipulateObjectTask(self, state): + # Widget takes precedence + if self.constraint: + type = self.constraint[2:] + if type == 'post': + self.xlate1D(state) + elif type == 'disc': + self.xlate2D(state) + elif type == 'ring': + self.rotate1D(state) + # No widget interaction, determine free manip mode + elif self.fFreeManip: + # If we've been scaling and changed modes, reset object handles + if 0 and self.fScaling and (not SEditor.fAlt): + self.objectHandles.transferObjectHandlesScale() + self.fScaling = 0 + # Alt key switches to a scaling mode + if SEditor.fControl: + self.fScaling = 1 + self.scale3D(state) + # Otherwise, manip mode depends on where you started + elif state.fMouseX and state.fMouseY: + # In the corner, spin around camera's axis + self.rotateAboutViewVector(state) + elif state.fMouseX or state.fMouseY: + # Mouse started elsewhere in the outer frame, rotate + self.rotate2D(state) + else: + # Mouse started in central region, xlate + # Mode depends on shift key + if SEditor.fShift or SEditor.fControl: + self.xlateCamXY(state) + else: + self.xlateCamXZ(state) + if self.fSetCoa: + # Update coa based on current widget position + SEditor.selected.last.mCoa2Dnp.assign( + SEditor.widget.getMat(SEditor.selected.last)) + else: + # Move the objects with the widget + SEditor.selected.moveWrtWidgetAll() + # Continue + return Task.cont + + ### WIDGET MANIPULATION METHODS ### + def xlate1D(self, state): + # Constrained 1D Translation along widget axis + # Compute nearest hit point along axis and try to keep + # that point as close to the current mouse position as possible + # what point on the axis is the mouse pointing at? + self.hitPt.assign(self.objectHandles.getAxisIntersectPt( + self.constraint[:1])) + # use it to see how far to move the widget + if self.fHitInit: + # First time through, just record that point + self.fHitInit = 0 + self.prevHit.assign(self.hitPt) + else: + # Move widget to keep hit point as close to mouse as possible + offset = self.hitPt - self.prevHit + SEditor.widget.setPos(SEditor.widget, offset) + + def xlate2D(self, state): + # Constrained 2D (planar) translation + # Compute point of intersection of ray from eyepoint through cursor + # to one of the three orthogonal planes on the widget. + # This point tracks all subsequent mouse movements + self.hitPt.assign(self.objectHandles.getWidgetIntersectPt( + SEditor.widget, self.constraint[:1])) + # use it to see how far to move the widget + if self.fHitInit: + # First time through just record hit point + self.fHitInit = 0 + self.prevHit.assign(self.hitPt) + else: + offset = self.hitPt - self.prevHit + SEditor.widget.setPos(SEditor.widget, offset) + + def rotate1D(self, state): + # Constrained 1D rotation about the widget's main axis (X,Y, or Z) + # Rotation depends upon circular motion of the mouse about the + # projection of the widget's origin on the image plane + # A complete circle about the widget results in a change in + # orientation of 360 degrees. + + # First initialize hit point/rotation angle + if self.fHitInit: + self.fHitInit = 0 + self.rotateAxis = self.constraint[:1] + self.fWidgetTop = self.widgetCheck('top?') + self.rotationCenter = getScreenXY(SEditor.widget) + self.lastCrankAngle = getCrankAngle(self.rotationCenter) + + # Rotate widget based on how far cursor has swung around origin + newAngle = getCrankAngle(self.rotationCenter) + deltaAngle = self.lastCrankAngle - newAngle + if self.fWidgetTop: + deltaAngle = -1 * deltaAngle + if self.rotateAxis == 'x': + SEditor.widget.setP(SEditor.widget, deltaAngle) + elif self.rotateAxis == 'y': + if base.config.GetBool('temp-hpr-fix',0): + SEditor.widget.setR(SEditor.widget, deltaAngle) + else: + SEditor.widget.setR(SEditor.widget, -deltaAngle) + elif self.rotateAxis == 'z': + SEditor.widget.setH(SEditor.widget, deltaAngle) + # Record crank angle for next time around + self.lastCrankAngle = newAngle + + def widgetCheck(self,type): + # Utility to see if we are looking at the top or bottom of + # a 2D planar widget or if we are looking at a 2D planar widget + # edge on + # Based upon angle between view vector from eye through the + # widget's origin and one of the three principle axes + axis = self.constraint[:1] + # First compute vector from eye through widget origin + mWidget2Cam = SEditor.widget.getMat(SEditor.camera) + # And determine where the viewpoint is relative to widget + pos = VBase3(0) + decomposeMatrix(mWidget2Cam, VBase3(0), VBase3(0), pos, + CSDefault) + widgetDir = Vec3(pos) + widgetDir.normalize() + # Convert specified widget axis to view space + if axis == 'x': + widgetAxis = Vec3(mWidget2Cam.xformVec(X_AXIS)) + elif axis == 'y': + widgetAxis = Vec3(mWidget2Cam.xformVec(Y_AXIS)) + elif axis == 'z': + widgetAxis = Vec3(mWidget2Cam.xformVec(Z_AXIS)) + widgetAxis.normalize() + if type == 'top?': + # Check sign of angle between two vectors + return (widgetDir.dot(widgetAxis) < 0.) + elif type == 'edge?': + # Checking to see if we are viewing edge-on + # Check angle between two vectors + return(abs(widgetDir.dot(widgetAxis)) < .2) + + ### FREE MANIPULATION METHODS ### + def xlateCamXZ(self, state): + """Constrained 2D motion parallel to the camera's image plane + This moves the object in the camera's XZ plane""" + # reset fHitInit in case we later switch to manip mode + self.fHitInit = 1 + # Reset scaling init flag + self.fScaleInit = 1 + # Where is the widget relative to current camera view + vWidget2Camera = SEditor.widget.getPos(SEditor.camera) + x = vWidget2Camera[0] + y = vWidget2Camera[1] + z = vWidget2Camera[2] + # Move widget (and objects) based upon mouse motion + # Scaled up accordingly based upon widget distance + dr = SEditor.dr + SEditor.widget.setX( + SEditor.camera, + x + 0.5 * dr.mouseDeltaX * dr.nearWidth * (y/dr.near)) + SEditor.widget.setZ( + SEditor.camera, + z + 0.5 * dr.mouseDeltaY * dr.nearHeight * (y/dr.near)) + + def xlateCamXY(self, state): + """Constrained 2D motion perpendicular to camera's image plane + This moves the object in the camera's XY plane if shift is held + Moves object toward camera if control is held + """ + # Reset scaling init flag + self.fScaleInit = 1 + # Now, where is the widget relative to current camera view + vWidget2Camera = SEditor.widget.getPos(SEditor.camera) + # If this is first time around, record initial y distance + if self.fHitInit: + self.fHitInit = 0 + # Use distance to widget to scale motion along Y + self.xlateSF = Vec3(vWidget2Camera).length() + # Get widget's current xy coords in screen space + coaCenter = getNearProjectionPoint(SEditor.widget) + self.deltaNearX = coaCenter[0] - SEditor.dr.nearVec[0] + # Which way do we move the object? + if SEditor.fControl: + moveDir = Vec3(vWidget2Camera) + # If widget is behind camera invert vector + if moveDir[1] < 0.0: + moveDir.assign(moveDir * -1) + moveDir.normalize() + else: + moveDir = Vec3(Y_AXIS) + # Move selected objects + dr = SEditor.dr + # Scale move dir + moveDir.assign(moveDir * (2.0 * dr.mouseDeltaY * self.xlateSF)) + # Add it to current widget offset + vWidget2Camera += moveDir + # The object, however, stays at the same relative point to mouse in X + vWidget2Camera.setX((dr.nearVec[0] + self.deltaNearX) * + (vWidget2Camera[1]/dr.near)) + # Move widget + SEditor.widget.setPos(SEditor.camera, vWidget2Camera) + + def rotate2D(self, state): + """ Virtual trackball rotation of widget """ + # Reset init flag in case we switch to another mode + self.fHitInit = 1 + # Reset scaling init flag + self.fScaleInit = 1 + tumbleRate = 360 + # If moving outside of center, ignore motion perpendicular to edge + if ((state.constrainedDir == 'y') and (abs(SEditor.dr.mouseX) > 0.9)): + deltaX = 0 + deltaY = SEditor.dr.mouseDeltaY + elif ((state.constrainedDir == 'x') and (abs(SEditor.dr.mouseY) > 0.9)): + deltaX = SEditor.dr.mouseDeltaX + deltaY = 0 + else: + deltaX = SEditor.dr.mouseDeltaX + deltaY = SEditor.dr.mouseDeltaY + # Mouse motion edge to edge of display region results in one full turn + relHpr(SEditor.widget, SEditor.camera, deltaX * tumbleRate, + -deltaY * tumbleRate, 0) + + def rotateAboutViewVector(self, state): + # Reset init flag in case we switch to another mode + self.fHitInit = 1 + # Reset scaling init flag + self.fScaleInit = 1 + # Compute current angle + angle = getCrankAngle(state.coaCenter) + deltaAngle = angle - state.lastAngle + state.lastAngle = angle + # Mouse motion edge to edge of display region results in one full turn + if base.config.GetBool('temp-hpr-fix',0): + relHpr(SEditor.widget, SEditor.camera, 0, 0, -deltaAngle) + else: + relHpr(SEditor.widget, SEditor.camera, 0, 0, deltaAngle) + + def scale3D(self, state): + # Scale the selected node based upon up down mouse motion + # Mouse motion from edge to edge results in a factor of 4 scaling + # From midpoint to edge doubles or halves objects scale + if self.fScaleInit: + self.fScaleInit = 0 + self.manipRef.setPos(SEditor.widget, 0, 0, 0) + self.manipRef.setHpr(SEditor.camera, 0, 0, 0) + self.initScaleMag = Vec3( + self.objectHandles.getWidgetIntersectPt( + self.manipRef, 'y')).length() + # record initial scale + self.initScale = SEditor.widget.getScale() + # Reset fHitInitFlag + self.fHitInit = 1 + # Begin + # Scale factor is ratio current mag with init mag + currScale = ( + self.initScale * + (self.objectHandles.getWidgetIntersectPt( + self.manipRef, 'y').length() / + self.initScaleMag) + ) + SEditor.widget.setScale(currScale) + + ## Utility functions ## + def plantSelectedNodePath(self): + """ Move selected object to intersection point of cursor on scene """ + # Check for intersection + entry = SEditor.iRay.pickGeom( + skipFlags = SKIP_HIDDEN | SKIP_BACKFACE | SKIP_CAMERA) + # MRM: Need to handle moving COA + if (entry != None) and (SEditor.selected.last != None): + # Record undo point + SEditor.pushUndo(SEditor.selected) + # Record wrt matrix + SEditor.selected.getWrtAll() + # Move selected + SEditor.widget.setPos( + SEditor.camera,entry.getFromIntersectionPoint()) + # Move all the selected objects with widget + # Move the objects with the widget + SEditor.selected.moveWrtWidgetAll() + # Let everyone know that something was moved + messenger.send('DIRECT_manipulateObjectCleanup') + +class ObjectHandles(NodePath,PandaObject): + def __init__(self): + # Initialize the superclass + NodePath.__init__(self) + + # Load up object handles model and assign it to self + self.assign(loader.loadModel('models/misc/objectHandles')) + self.setName('objectHandles') + self.scalingNode = self.getChild(0) + self.scalingNode.setName('ohScalingNode') + self.ohScalingFactor = 1.0 + # To avoid recreating a vec every frame + self.hitPt = Vec3(0) + # Get a handle on the components + self.xHandles = self.find('**/X') + self.xPostGroup = self.xHandles.find('**/x-post-group') + self.xPostCollision = self.xHandles.find('**/x-post') + self.xRingGroup = self.xHandles.find('**/x-ring-group') + self.xRingCollision = self.xHandles.find('**/x-ring') + self.xDiscGroup = self.xHandles.find('**/x-disc-group') + self.xDisc = self.xHandles.find('**/x-disc-visible') + self.xDiscCollision = self.xHandles.find('**/x-disc') + + self.yHandles = self.find('**/Y') + self.yPostGroup = self.yHandles.find('**/y-post-group') + self.yPostCollision = self.yHandles.find('**/y-post') + self.yRingGroup = self.yHandles.find('**/y-ring-group') + self.yRingCollision = self.yHandles.find('**/y-ring') + self.yDiscGroup = self.yHandles.find('**/y-disc-group') + self.yDisc = self.yHandles.find('**/y-disc-visible') + self.yDiscCollision = self.yHandles.find('**/y-disc') + + self.zHandles = self.find('**/Z') + self.zPostGroup = self.zHandles.find('**/z-post-group') + self.zPostCollision = self.zHandles.find('**/z-post') + self.zRingGroup = self.zHandles.find('**/z-ring-group') + self.zRingCollision = self.zHandles.find('**/z-ring') + self.zDiscGroup = self.zHandles.find('**/z-disc-group') + self.zDisc = self.zHandles.find('**/z-disc-visible') + self.zDiscCollision = self.zHandles.find('**/z-disc') + + # Adjust visiblity, colors, and transparency + self.xPostCollision.hide() + self.xRingCollision.hide() + self.xDisc.setColor(1,0,0,.2) + self.yPostCollision.hide() + self.yRingCollision.hide() + self.yDisc.setColor(0,1,0,.2) + self.zPostCollision.hide() + self.zRingCollision.hide() + self.zDisc.setColor(0,0,1,.2) + # Augment geometry with lines + self.createObjectHandleLines() + # Create long markers to help line up in world + self.createGuideLines() + self.hideGuides() + + # Start with widget handles hidden + self.fActive = 1 + self.toggleWidget() + + # Make sure object handles are never lit or drawn in wireframe + useDirectRenderStyle(self) + + def coaModeColor(self): + self.setColor(.5,.5,.5,1) + + def manipModeColor(self): + self.clearColor() + + def toggleWidget(self): + if self.fActive: + self.deactivate() + else: + self.activate() + + def activate(self): + self.scalingNode.reparentTo(self) + self.fActive = 1 + + def deactivate(self): + self.scalingNode.reparentTo(hidden) + self.fActive = 0 + + def showWidgetIfActive(self): + if self.fActive: + self.reparentTo(SEditor.group) + + def showWidget(self): + self.reparentTo(SEditor.group) + + def hideWidget(self): + self.reparentTo(hidden) + + def enableHandles(self, handles): + if type(handles) == types.ListType: + for handle in handles: + self.enableHandle(handle) + elif handles == 'x': + self.enableHandles(['x-post','x-ring','x-disc']) + elif handles == 'y': + self.enableHandles(['y-post','y-ring','y-disc']) + elif handles == 'z': + self.enableHandles(['z-post','z-ring','z-disc']) + elif handles == 'post': + self.enableHandles(['x-post','y-post','z-post']) + elif handles == 'ring': + self.enableHandles(['x-ring','y-ring','z-ring']) + elif handles == 'disc': + self.enableHandles(['x-disc','y-disc','z-disc']) + elif handles == 'all': + self.enableHandles(['x-post','x-ring','x-disc', + 'y-post','y-ring','y-disc', + 'z-post','z-ring','z-disc']) + + def enableHandle(self, handle): + if handle == 'x-post': + self.xPostGroup.reparentTo(self.xHandles) + elif handle == 'x-ring': + self.xRingGroup.reparentTo(self.xHandles) + elif handle == 'x-disc': + self.xDiscGroup.reparentTo(self.xHandles) + if handle == 'y-post': + self.yPostGroup.reparentTo(self.yHandles) + elif handle == 'y-ring': + self.yRingGroup.reparentTo(self.yHandles) + elif handle == 'y-disc': + self.yDiscGroup.reparentTo(self.yHandles) + if handle == 'z-post': + self.zPostGroup.reparentTo(self.zHandles) + elif handle == 'z-ring': + self.zRingGroup.reparentTo(self.zHandles) + elif handle == 'z-disc': + self.zDiscGroup.reparentTo(self.zHandles) + + def disableHandles(self, handles): + if type(handles) == types.ListType: + for handle in handles: + self.disableHandle(handle) + elif handles == 'x': + self.disableHandles(['x-post','x-ring','x-disc']) + elif handles == 'y': + self.disableHandles(['y-post','y-ring','y-disc']) + elif handles == 'z': + self.disableHandles(['z-post','z-ring','z-disc']) + elif handles == 'post': + self.disableHandles(['x-post','y-post','z-post']) + elif handles == 'ring': + self.disableHandles(['x-ring','y-ring','z-ring']) + elif handles == 'disc': + self.disableHandles(['x-disc','y-disc','z-disc']) + elif handles == 'all': + self.disableHandles(['x-post','x-ring','x-disc', + 'y-post','y-ring','y-disc', + 'z-post','z-ring','z-disc']) + + def disableHandle(self, handle): + if handle == 'x-post': + self.xPostGroup.reparentTo(hidden) + elif handle == 'x-ring': + self.xRingGroup.reparentTo(hidden) + elif handle == 'x-disc': + self.xDiscGroup.reparentTo(hidden) + if handle == 'y-post': + self.yPostGroup.reparentTo(hidden) + elif handle == 'y-ring': + self.yRingGroup.reparentTo(hidden) + elif handle == 'y-disc': + self.yDiscGroup.reparentTo(hidden) + if handle == 'z-post': + self.zPostGroup.reparentTo(hidden) + elif handle == 'z-ring': + self.zRingGroup.reparentTo(hidden) + elif handle == 'z-disc': + self.zDiscGroup.reparentTo(hidden) + + def showAllHandles(self): + self.xPost.show() + self.xRing.show() + self.xDisc.show() + self.yPost.show() + self.yRing.show() + self.yDisc.show() + self.zPost.show() + self.zRing.show() + self.zDisc.show() + + def hideAllHandles(self): + self.xPost.hide() + self.xRing.hide() + self.xDisc.hide() + self.yPost.hide() + self.yRing.hide() + self.yDisc.hide() + self.zPost.hide() + self.zRing.hide() + self.zDisc.hide() + + def showHandle(self, handle): + if handle == 'x-post': + self.xPost.show() + elif handle == 'x-ring': + self.xRing.show() + elif handle == 'x-disc': + self.xDisc.show() + elif handle == 'y-post': + self.yPost.show() + elif handle == 'y-ring': + self.yRing.show() + elif handle == 'y-disc': + self.yDisc.show() + elif handle == 'z-post': + self.zPost.show() + elif handle == 'z-ring': + self.zRing.show() + elif handle == 'z-disc': + self.zDisc.show() + + def showGuides(self): + self.guideLines.show() + + def hideGuides(self): + self.guideLines.hide() + + def setScalingFactor(self, scaleFactor): + self.ohScalingFactor = scaleFactor + self.scalingNode.setScale(self.ohScalingFactor) + + def getScalingFactor(self): + return self.scalingNode.getScale() + + def transferObjectHandlesScale(self): + # see how much object handles have been scaled + ohs = self.getScale() + sns = self.scalingNode.getScale() + # Transfer this to the scaling node + self.scalingNode.setScale( + ohs[0] * sns[0], + ohs[1] * sns[1], + ohs[2] * sns[2]) + self.setScale(1) + + def multiplyScalingFactorBy(self, factor): + taskMgr.remove('resizeObjectHandles') + sf = self.ohScalingFactor = self.ohScalingFactor * factor + self.scalingNode.lerpScale(sf,sf,sf, 0.5, + blendType = 'easeInOut', + task = 'resizeObjectHandles') + + def growToFit(self): + taskMgr.remove('resizeObjectHandles') + # Increase handles scale until they cover 80% of the min dimension + # Originally, here is "cover 30% of the min dimension", we changed. + pos = SEditor.widget.getPos(SEditor.camera) + minDim = min(SEditor.dr.nearWidth, SEditor.dr.nearHeight) + sf = 0.4 * minDim * (pos[1]/SEditor.dr.near) + self.ohScalingFactor = sf + self.scalingNode.lerpScale(sf,sf,sf, 0.5, + blendType = 'easeInOut', + task = 'resizeObjectHandles') + + def createObjectHandleLines(self): + # X post + self.xPost = self.xPostGroup.attachNewNode('x-post-visible') + lines = LineNodePath(self.xPost) + lines.setColor(VBase4(1,0,0,1)) + lines.setThickness(5) + lines.moveTo(0,0,0) + lines.drawTo(1.5,0,0) + lines.create() + lines = LineNodePath(self.xPost) + lines.setColor(VBase4(1,0,0,1)) + lines.setThickness(1.5) + lines.moveTo(0,0,0) + lines.drawTo(-1.5,0,0) + lines.create() + + # X ring + self.xRing = self.xRingGroup.attachNewNode('x-ring-visible') + lines = LineNodePath(self.xRing) + lines.setColor(VBase4(1,0,0,1)) + lines.setThickness(3) + lines.moveTo(0,1,0) + for ang in range(15, 370, 15): + lines.drawTo(0, + math.cos(deg2Rad(ang)), + math.sin(deg2Rad(ang))) + lines.create() + + # Y post + self.yPost = self.yPostGroup.attachNewNode('y-post-visible') + lines = LineNodePath(self.yPost) + lines.setColor(VBase4(0,1,0,1)) + lines.setThickness(5) + lines.moveTo(0,0,0) + lines.drawTo(0,1.5,0) + lines.create() + lines = LineNodePath(self.yPost) + lines.setColor(VBase4(0,1,0,1)) + lines.setThickness(1.5) + lines.moveTo(0,0,0) + lines.drawTo(0,-1.5,0) + lines.create() + + # Y ring + self.yRing = self.yRingGroup.attachNewNode('y-ring-visible') + lines = LineNodePath(self.yRing) + lines.setColor(VBase4(0,1,0,1)) + lines.setThickness(3) + lines.moveTo(1,0,0) + for ang in range(15, 370, 15): + lines.drawTo(math.cos(deg2Rad(ang)), + 0, + math.sin(deg2Rad(ang))) + lines.create() + + # Z post + self.zPost = self.zPostGroup.attachNewNode('z-post-visible') + lines = LineNodePath(self.zPost) + lines.setColor(VBase4(0,0,1,1)) + lines.setThickness(5) + lines.moveTo(0,0,0) + lines.drawTo(0,0,1.5) + lines.create() + lines = LineNodePath(self.zPost) + lines.setColor(VBase4(0,0,1,1)) + lines.setThickness(1.5) + lines.moveTo(0,0,0) + lines.drawTo(0,0,-1.5) + lines.create() + + # Z ring + self.zRing = self.zRingGroup.attachNewNode('z-ring-visible') + lines = LineNodePath(self.zRing) + lines.setColor(VBase4(0,0,1,1)) + lines.setThickness(3) + lines.moveTo(1,0,0) + for ang in range(15, 370, 15): + lines.drawTo(math.cos(deg2Rad(ang)), + math.sin(deg2Rad(ang)), + 0) + lines.create() + + def createGuideLines(self): + self.guideLines = self.attachNewNode('guideLines') + # X guide lines + lines = LineNodePath(self.guideLines) + lines.setColor(VBase4(1,0,0,1)) + lines.setThickness(0.5) + lines.moveTo(-500,0,0) + lines.drawTo(500,0,0) + lines.create() + lines.setName('x-guide') + + # Y guide lines + lines = LineNodePath(self.guideLines) + lines.setColor(VBase4(0,1,0,1)) + lines.setThickness(0.5) + lines.moveTo(0,-500,0) + lines.drawTo(0,500,0) + lines.create() + lines.setName('y-guide') + + # Z guide lines + lines = LineNodePath(self.guideLines) + lines.setColor(VBase4(0,0,1,1)) + lines.setThickness(0.5) + lines.moveTo(0,0,-500) + lines.drawTo(0,0,500) + lines.create() + lines.setName('z-guide') + + def getAxisIntersectPt(self, axis): + # Calc the xfrom from camera to widget + mCam2Widget = SEditor.camera.getMat(SEditor.widget) + lineDir = Vec3(mCam2Widget.xformVec(SEditor.dr.nearVec)) + lineDir.normalize() + # And determine where the viewpoint is relative to widget + lineOrigin = VBase3(0) + decomposeMatrix(mCam2Widget, VBase3(0), VBase3(0), lineOrigin, + CSDefault) + # Now see where this hits the plane containing the 1D motion axis. + # Pick the intersection plane most normal to the intersection ray + # by comparing lineDir with plane normals. The plane with the + # largest dotProduct is most "normal" + if axis == 'x': + if (abs(lineDir.dot(Y_AXIS)) > abs(lineDir.dot(Z_AXIS))): + self.hitPt.assign( + planeIntersect(lineOrigin, lineDir, ORIGIN, Y_AXIS)) + else: + self.hitPt.assign( + planeIntersect(lineOrigin, lineDir, ORIGIN, Z_AXIS)) + # We really only care about the nearest point on the axis + self.hitPt.setY(0) + self.hitPt.setZ(0) + elif axis == 'y': + if (abs(lineDir.dot(X_AXIS)) > abs(lineDir.dot(Z_AXIS))): + self.hitPt.assign( + planeIntersect(lineOrigin, lineDir, ORIGIN, X_AXIS)) + else: + self.hitPt.assign( + planeIntersect(lineOrigin, lineDir, ORIGIN, Z_AXIS)) + # We really only care about the nearest point on the axis + self.hitPt.setX(0) + self.hitPt.setZ(0) + elif axis == 'z': + if (abs(lineDir.dot(X_AXIS)) > abs(lineDir.dot(Y_AXIS))): + self.hitPt.assign( + planeIntersect(lineOrigin, lineDir, ORIGIN, X_AXIS)) + else: + self.hitPt.assign( + planeIntersect(lineOrigin, lineDir, ORIGIN, Y_AXIS)) + # We really only care about the nearest point on the axis + self.hitPt.setX(0) + self.hitPt.setY(0) + return self.hitPt + + def getWidgetIntersectPt(self, nodePath, plane): + # Find out the point of interection of the ray passing though the mouse + # with the plane containing the 2D xlation or 1D rotation widgets + + # Calc the xfrom from camera to the nodePath + mCam2NodePath = SEditor.camera.getMat(nodePath) + + # And determine where the viewpoint is relative to widget + lineOrigin = VBase3(0) + decomposeMatrix(mCam2NodePath, VBase3(0), VBase3(0), lineOrigin, + CSDefault) + + # Next we find the vector from viewpoint to the widget through + # the mouse's position on near plane. + # This defines the intersection ray + lineDir = Vec3(mCam2NodePath.xformVec(SEditor.dr.nearVec)) + lineDir.normalize() + # Find the hit point + if plane == 'x': + self.hitPt.assign(planeIntersect( + lineOrigin, lineDir, ORIGIN, X_AXIS)) + elif plane == 'y': + self.hitPt.assign(planeIntersect( + lineOrigin, lineDir, ORIGIN, Y_AXIS)) + elif plane == 'z': + self.hitPt.assign(planeIntersect( + lineOrigin, lineDir, ORIGIN, Z_AXIS)) + return self.hitPt + + + diff --git a/doc/SceneEditor/seMopathRecorder.py b/doc/SceneEditor/seMopathRecorder.py new file mode 100644 index 0000000000..2e459b0bb0 --- /dev/null +++ b/doc/SceneEditor/seMopathRecorder.py @@ -0,0 +1,2073 @@ +################################################################# +# seMopathRecorder.py +# Originally from MopathRecorder.py +# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +# +# We have to change something about data flow and UI +# so the curves data can be saved into our dataHolder. +# Other things we have changed here is that we have added a copy model +# of target nodePath under the render when the recording begins. +# And, of course, we have removed it right after the recording ends. +# You can find it in toggleRecord function. +# +################################################################# + +# Import Tkinter, Pmw, and the dial code from this directory tree. +from PandaObject import * +from TkGlobal import * +from AppShell import * +from DirectGlobals import * +from DirectUtil import * +from SeGeometry import * +from SeSelection import * +from tkFileDialog import * +import os +import string +import Dial +import Floater +import Slider +import EntryScale +import VectorWidgets +import __builtin__ + +PRF_UTILITIES = [ + 'lambda: camera.lookAt(render)', + 'lambda: camera.setZ(render, 0.0)', + 'lambda s = self: s.playbackMarker.lookAt(render)', + 'lambda s = self: s.playbackMarker.setZ(render, 0.0)', + 'lambda s = self: s.followTerrain(10.0)'] + +class MopathRecorder(AppShell, PandaObject): + # Override class variables here + appname = 'Mopath Recorder Panel' + frameWidth = 450 + frameHeight = 550 + usecommandarea = 0 + usestatusarea = 0 + count = 0 + + def __init__(self, parent = None, **kw): + INITOPT = Pmw.INITOPT + name = 'recorder-%d' % MopathRecorder.count + MopathRecorder.count += 1 + optiondefs = ( + ('title', self.appname, None), + ('nodePath', None, None), + ('name', name, None) + ) + self.defineoptions(kw, optiondefs) + + # Call superclass initialization function + AppShell.__init__(self) + + self.initialiseoptions(MopathRecorder) + + self.selectNodePathNamed('camera') + + self.parent.resizable(False,False) ## Disable the ability to resize for this Window. + + def appInit(self): + self.mopathRecorderNode = render.attachNewNode("MopathRecorder") + self.name = self['name'] + # Dictionary of widgets + self.widgetDict = {} + self.variableDict = {} + # Initialize state + # The active node path + self.nodePath = self['nodePath'] + self.playbackNodePath = self.nodePath + # The active node path's parent + self.nodePathParent = render + # Top level node path + self.recorderNodePath = self.mopathRecorderNode.attachNewNode(self.name) + # Temp CS for use in refinement/path extension + self.tempCS = self.recorderNodePath.attachNewNode( + 'mopathRecorderTempCS') + # Marker for use in playback + self.playbackMarker = loader.loadModel('models/misc/sphere') ### + self.playbackMarker.setName('Playback Marker') + self.playbackMarker.reparentTo(self.recorderNodePath) + self.playbackMarkerIds = self.getChildIds( + self.playbackMarker.getChild(0)) + self.playbackMarker.hide() + # Tangent marker + self.tangentGroup = self.playbackMarker.attachNewNode('Tangent Group') + self.tangentGroup.hide() + self.tangentMarker = loader.loadModel('models/misc/sphere') + self.tangentMarker.reparentTo(self.tangentGroup) + self.tangentMarker.setScale(0.5) + self.tangentMarker.setColor(1,0,1,1) + self.tangentMarker.setName('Tangent Marker') + self.tangentMarkerIds = self.getChildIds( + self.tangentMarker.getChild(0)) + self.tangentLines = LineNodePath(self.tangentGroup) + self.tangentLines.setColor(VBase4(1,0,1,1)) + self.tangentLines.setThickness(1) + self.tangentLines.moveTo(0,0,0) + self.tangentLines.drawTo(0,0,0) + self.tangentLines.create() + # Active node path dictionary + self.nodePathDict = {} + self.nodePathDict['marker'] = self.playbackMarker + self.nodePathDict['camera'] = camera + self.nodePathDict['widget'] = SEditor.widget + self.nodePathDict['mopathRecorderTempCS'] = self.tempCS + self.nodePathNames = ['marker', 'camera', 'selected'] + # ID of selected object + self.manipulandumId = None + self.trace = LineNodePath(self.recorderNodePath) + self.oldPlaybackNodePath = None + # Count of point sets recorded + self.pointSet = [] + self.prePoints = [] + self.postPoints = [] + self.pointSetDict = {} + self.pointSetCount = 0 + self.pointSetName = self.name + '-ps-' + `self.pointSetCount` + # User callback to call before recording point + self.samplingMode = 'Continuous' + self.preRecordFunc = None + # Hook to start/stop recording + self.startStopHook = 'f6' + self.keyframeHook = 'f10' + # Curve fitter object + self.lastPos = Point3(0) + self.curveFitter = CurveFitter() + # Curve variables + # Number of ticks per parametric unit + self.numTicks = 1 + # Number of segments to represent each parametric unit + # This just affects the visual appearance of the curve + self.numSegs = 40 + # The nurbs curves + self.curveCollection = None + # Curve drawers + self.nurbsCurveDrawer = NurbsCurveDrawer() + self.nurbsCurveDrawer.setCurves(ParametricCurveCollection()) + self.nurbsCurveDrawer.setNumSegs(self.numSegs) + self.nurbsCurveDrawer.setShowHull(0) + self.nurbsCurveDrawer.setShowCvs(0) + self.nurbsCurveDrawer.setNumTicks(0) + self.nurbsCurveDrawer.setTickScale(5.0) + self.curveNodePath = self.recorderNodePath.attachNewNode( + self.nurbsCurveDrawer.getGeomNode()) + useDirectRenderStyle(self.curveNodePath) + # Playback variables + self.maxT = 0.0 + self.playbackTime = 0.0 + self.loopPlayback = 1 + self.playbackSF = 1.0 + # Sample variables + self.desampleFrequency = 1 + self.numSamples = 100 + self.recordStart = 0.0 + self.deltaTime = 0.0 + self.controlStart = 0.0 + self.controlStop = 0.0 + self.recordStop = 0.0 + self.cropFrom = 0.0 + self.cropTo = 0.0 + self.fAdjustingValues = 0 + # For terrain following + self.iRayCS = self.recorderNodePath.attachNewNode( + 'mopathRecorderIRayCS') + self.iRay = SelectionRay(self.iRayCS) + # Set up event hooks + self.actionEvents = [ + ('DIRECT_undo', self.undoHook), + ('DIRECT_pushUndo', self.pushUndoHook), + ('DIRECT_undoListEmpty', self.undoListEmptyHook), + ('DIRECT_redo', self.redoHook), + ('DIRECT_pushRedo', self.pushRedoHook), + ('DIRECT_redoListEmpty', self.redoListEmptyHook), + ('DIRECT_selectedNodePath', self.selectedNodePathHook), + ('DIRECT_deselectedNodePath', self.deselectedNodePathHook), + ('DIRECT_manipulateObjectStart', self.manipulateObjectStartHook), + ('DIRECT_manipulateObjectCleanup', + self.manipulateObjectCleanupHook), + ] + for event, method in self.actionEvents: + self.accept(event, method) + + def createInterface(self): + interior = self.interior() + # FILE MENU + # Get a handle on the file menu so commands can be inserted + # before quit item + fileMenu = self.menuBar.component('File-menu') + fileMenu.insert_command( + fileMenu.index('Quit'), + label = 'Load Curve', + command = self.loadCurveFromFile) + fileMenu.insert_command( + fileMenu.index('Quit'), + label = 'Save Curve', + command = self.saveCurveToFile) + + # Add mopath recorder commands to menubar + self.menuBar.addmenu('Recorder', 'Mopath Recorder Panel Operations') + self.menuBar.addmenuitem( + 'Recorder', 'command', + 'Save current curve as a new point set', + label = 'Save Point Set', + command = self.extractPointSetFromCurveCollection) + self.menuBar.addmenuitem( + 'Recorder', 'command', + 'Toggle widget visability', + label = 'Toggle Widget Vis', + command = self.toggleWidgetVis) + self.menuBar.addmenuitem( + 'Recorder', 'command', + 'Toggle widget manipulation mode', + label = 'Toggle Widget Mode', + command = SEditor.manipulationControl.toggleObjectHandlesMode) + + self.historyWidget = self.createComboBox(self.menuFrame, 'Mopath', 'Path:', + 'Select input points to fit curve to', '', + self.selectPointSetNamed, expand = 1) + + + self.undoButton = Button(self.menuFrame, text = 'Undo', + command = SEditor.undo) + if SEditor.undoList: + self.undoButton['state'] = 'normal' + else: + self.undoButton['state'] = 'disabled' + self.undoButton.pack(side = LEFT, expand = 0) + self.bind(self.undoButton, 'Undo last operation') + + self.redoButton = Button(self.menuFrame, text = 'Redo', + command = SEditor.redo) + if SEditor.redoList: + self.redoButton['state'] = 'normal' + else: + self.redoButton['state'] = 'disabled' + self.redoButton.pack(side = LEFT, expand = 0) + self.bind(self.redoButton, 'Redo last operation') + + # Record button + mainFrame = Frame(interior, relief = SUNKEN, borderwidth = 2) + frame = Frame(mainFrame) + # Active node path + # Button to select active node path + widget = self.createButton(frame, 'Recording', 'Node Path:', + 'Select Active Mopath Node Path', + lambda s = self: SEditor.select(s.nodePath), + side = LEFT, expand = 0) + widget['relief'] = FLAT + self.nodePathMenu = Pmw.ComboBox( + frame, entry_width = 20, + selectioncommand = self.selectNodePathNamed, + scrolledlist_items = self.nodePathNames) + self.nodePathMenu.selectitem('camera') + self.nodePathMenuEntry = ( + self.nodePathMenu.component('entryfield_entry')) + self.nodePathMenuBG = ( + self.nodePathMenuEntry.configure('background')[3]) + self.nodePathMenu.pack(side = LEFT, fill = X, expand = 1) + self.bind(self.nodePathMenu, + 'Select active node path used for recording and playback') + # Recording type + self.recordingType = StringVar() + self.recordingType.set('New Curve') + widget = self.createRadiobutton( + frame, 'left', + 'Recording', 'New Curve', + ('Next record session records a new path'), + self.recordingType, 'New Curve',expand = 0) + widget = self.createRadiobutton( + frame, 'left', + 'Recording', 'Refine', + ('Next record session refines existing path'), + self.recordingType, 'Refine', expand = 0) + widget = self.createRadiobutton( + frame, 'left', + 'Recording', 'Extend', + ('Next record session extends existing path'), + self.recordingType, 'Extend', expand = 0) + frame.pack(fill = X, expand = 1) + + frame = Frame(mainFrame) + widget = self.createCheckbutton( + frame, 'Recording', 'Record', + 'On: path is being recorded', self.toggleRecord, 0, + side = LEFT, fill = BOTH, expand = 1) + widget.configure(foreground = 'Red', relief = RAISED, borderwidth = 2, + anchor = CENTER, width = 16) + widget = self.createButton(frame, 'Recording', 'Add Keyframe', + 'Add Keyframe To Current Path', + self.addKeyframe, + side = LEFT, expand = 1) + + widget = self.createButton(frame, 'Recording', 'Bind Path to Node', + 'Bind Motion Path to selected Object', + self.bindMotionPathToNode, + side = LEFT, expand = 1) + + + frame.pack(fill = X, expand = 1) + + mainFrame.pack(expand = 1, fill = X, pady = 3) + + # Playback controls + playbackFrame = Frame(interior, relief = SUNKEN, + borderwidth = 2) + Label(playbackFrame, text = 'PLAYBACK CONTROLS', + font=('MSSansSerif', 12, 'bold')).pack(fill = X) + # Main playback control slider + widget = self.createEntryScale( + playbackFrame, 'Playback', 'Time', 'Set current playback time', + resolution = 0.01, command = self.playbackGoTo, side = TOP) + widget.component('hull')['relief'] = RIDGE + # Kill playback task if drag slider + widget['preCallback'] = self.stopPlayback + # Jam duration entry into entry scale + self.createLabeledEntry(widget.labelFrame, 'Resample', 'Path Duration', + 'Set total curve duration', + command = self.setPathDuration, + side = LEFT, expand = 0) + # Start stop buttons + frame = Frame(playbackFrame) + widget = self.createButton(frame, 'Playback', '<<', + 'Jump to start of playback', + self.jumpToStartOfPlayback, + side = LEFT, expand = 1) + widget['font'] = (('MSSansSerif', 12, 'bold')) + widget = self.createCheckbutton(frame, 'Playback', 'Play', + 'Start/Stop playback', + self.startStopPlayback, 0, + side = LEFT, fill = BOTH, expand = 1) + widget.configure(anchor = 'center', justify = 'center', + relief = RAISED, font = ('MSSansSerif', 12, 'bold')) + widget = self.createButton(frame, 'Playback', '>>', + 'Jump to end of playback', + self.jumpToEndOfPlayback, + side = LEFT, expand = 1) + widget['font'] = (('MSSansSerif', 12, 'bold')) + self.createCheckbutton(frame, 'Playback', 'Loop', + 'On: loop playback', + self.setLoopPlayback, self.loopPlayback, + side = LEFT, fill = BOTH, expand = 0) + frame.pack(fill = X, expand = 1) + + # Speed control + frame = Frame(playbackFrame) + widget = Button(frame, text = 'PB Speed Vernier', relief = FLAT, + command = lambda s = self: s.setSpeedScale(1.0)) + widget.pack(side = LEFT, expand = 0) + self.speedScale = Scale(frame, from_ = -1, to = 1, + resolution = 0.01, showvalue = 0, + width = 10, orient = 'horizontal', + command = self.setPlaybackSF) + self.speedScale.pack(side = LEFT, fill = X, expand = 1) + self.speedVar = StringVar() + self.speedVar.set("0.00") + self.speedEntry = Entry(frame, textvariable = self.speedVar, + width = 8) + self.speedEntry.bind( + '', + lambda e = None, s = self: s.setSpeedScale( + string.atof(s.speedVar.get()))) + self.speedEntry.pack(side = LEFT, expand = 0) + frame.pack(fill = X, expand = 1) + + playbackFrame.pack(fill = X, pady = 2) + + # Create notebook pages + self.mainNotebook = Pmw.NoteBook(interior) + self.mainNotebook.pack(fill = BOTH, expand = 1) + self.resamplePage = self.mainNotebook.add('Resample') + self.refinePage = self.mainNotebook.add('Refine') + self.extendPage = self.mainNotebook.add('Extend') + self.cropPage = self.mainNotebook.add('Crop') + self.drawPage = self.mainNotebook.add('Draw') + self.optionsPage = self.mainNotebook.add('Options') + + ## RESAMPLE PAGE + label = Label(self.resamplePage, text = 'RESAMPLE CURVE', + font=('MSSansSerif', 12, 'bold')) + label.pack(fill = X) + + # Resample + resampleFrame = Frame( + self.resamplePage, relief = SUNKEN, borderwidth = 2) + label = Label(resampleFrame, text = 'RESAMPLE CURVE', + font=('MSSansSerif', 12, 'bold')).pack() + widget = self.createSlider( + resampleFrame, 'Resample', 'Num. Samples', + 'Number of samples in resampled curve', + resolution = 1, min = 2, max = 1000, command = self.setNumSamples) + widget.component('hull')['relief'] = RIDGE + widget['postCallback'] = self.sampleCurve + + frame = Frame(resampleFrame) + self.createButton( + frame, 'Resample', 'Make Even', + 'Apply timewarp so resulting path has constant velocity', + self.makeEven, side = LEFT, fill = X, expand = 1) + self.createButton( + frame, 'Resample', 'Face Forward', + 'Compute HPR so resulting hpr curve faces along xyz tangent', + self.faceForward, side = LEFT, fill = X, expand = 1) + frame.pack(fill = X, expand = 0) + resampleFrame.pack(fill = X, expand = 0, pady = 2) + + # Desample + desampleFrame = Frame( + self.resamplePage, relief = SUNKEN, borderwidth = 2) + Label(desampleFrame, text = 'DESAMPLE CURVE', + font=('MSSansSerif', 12, 'bold')).pack() + widget = self.createSlider( + desampleFrame, 'Resample', 'Points Between Samples', + 'Specify number of points to skip between samples', + min = 1, max = 100, resolution = 1, + command = self.setDesampleFrequency) + widget.component('hull')['relief'] = RIDGE + widget['postCallback'] = self.desampleCurve + desampleFrame.pack(fill = X, expand = 0, pady = 2) + + ## REFINE PAGE ## + refineFrame = Frame(self.refinePage, relief = SUNKEN, + borderwidth = 2) + label = Label(refineFrame, text = 'REFINE CURVE', + font=('MSSansSerif', 12, 'bold')) + label.pack(fill = X) + + widget = self.createSlider(refineFrame, + 'Refine Page', 'Refine From', + 'Begin time of refine pass', + resolution = 0.01, + command = self.setRecordStart) + widget['preCallback'] = self.setRefineMode + widget['postCallback'] = lambda s = self: s.getPrePoints('Refine') + widget = self.createSlider( + refineFrame, 'Refine Page', + 'Control Start', + 'Time when full control of node path is given during refine pass', + resolution = 0.01, + command = self.setControlStart) + widget['preCallback'] = self.setRefineMode + widget = self.createSlider( + refineFrame, 'Refine Page', + 'Control Stop', + 'Time when node path begins transition back to original curve', + resolution = 0.01, + command = self.setControlStop) + widget['preCallback'] = self.setRefineMode + widget = self.createSlider(refineFrame, 'Refine Page', 'Refine To', + 'Stop time of refine pass', + resolution = 0.01, + command = self.setRefineStop) + widget['preCallback'] = self.setRefineMode + widget['postCallback'] = self.getPostPoints + refineFrame.pack(fill = X) + + ## EXTEND PAGE ## + extendFrame = Frame(self.extendPage, relief = SUNKEN, + borderwidth = 2) + label = Label(extendFrame, text = 'EXTEND CURVE', + font=('MSSansSerif', 12, 'bold')) + label.pack(fill = X) + + widget = self.createSlider(extendFrame, + 'Extend Page', 'Extend From', + 'Begin time of extend pass', + resolution = 0.01, + command = self.setRecordStart) + widget['preCallback'] = self.setExtendMode + widget['postCallback'] = lambda s = self: s.getPrePoints('Extend') + widget = self.createSlider( + extendFrame, 'Extend Page', + 'Control Start', + 'Time when full control of node path is given during extend pass', + resolution = 0.01, + command = self.setControlStart) + widget['preCallback'] = self.setExtendMode + extendFrame.pack(fill = X) + + ## CROP PAGE ## + cropFrame = Frame(self.cropPage, relief = SUNKEN, + borderwidth = 2) + label = Label(cropFrame, text = 'CROP CURVE', + font=('MSSansSerif', 12, 'bold')) + label.pack(fill = X) + + widget = self.createSlider( + cropFrame, + 'Crop Page', 'Crop From', + 'Delete all curve points before this time', + resolution = 0.01, + command = self.setCropFrom) + + widget = self.createSlider( + cropFrame, + 'Crop Page', 'Crop To', + 'Delete all curve points after this time', + resolution = 0.01, + command = self.setCropTo) + + self.createButton(cropFrame, 'Crop Page', 'Crop Curve', + 'Crop curve to specified from to times', + self.cropCurve, fill = NONE) + cropFrame.pack(fill = X) + + ## DRAW PAGE ## + drawFrame = Frame(self.drawPage, relief = SUNKEN, + borderwidth = 2) + + self.sf = Pmw.ScrolledFrame(self.drawPage, horizflex = 'elastic') + self.sf.pack(fill = 'both', expand = 1) + sfFrame = self.sf.interior() + + label = Label(sfFrame, text = 'CURVE RENDERING STYLE', + font=('MSSansSerif', 12, 'bold')) + label.pack(fill = X) + + frame = Frame(sfFrame) + Label(frame, text = 'SHOW:').pack(side = LEFT, expand = 0) + widget = self.createCheckbutton( + frame, 'Style', 'Path', + 'On: path is visible', self.setPathVis, 1, + side = LEFT, fill = X, expand = 1) + widget = self.createCheckbutton( + frame, 'Style', 'Knots', + 'On: path knots are visible', self.setKnotVis, 1, + side = LEFT, fill = X, expand = 1) + widget = self.createCheckbutton( + frame, 'Style', 'CVs', + 'On: path CVs are visible', self.setCvVis, 0, + side = LEFT, fill = X, expand = 1) + widget = self.createCheckbutton( + frame, 'Style', 'Hull', + 'On: path hull is visible', self.setHullVis, 0, + side = LEFT, fill = X, expand = 1) + widget = self.createCheckbutton( + frame, 'Style', 'Trace', + 'On: record is visible', self.setTraceVis, 0, + side = LEFT, fill = X, expand = 1) + widget = self.createCheckbutton( + frame, 'Style', 'Marker', + 'On: playback marker is visible', self.setMarkerVis, 0, + side = LEFT, fill = X, expand = 1) + frame.pack(fill = X, expand = 1) + # Sliders + widget = self.createSlider( + sfFrame, 'Style', 'Num Segs', + 'Set number of segments used to approximate each parametric unit', + min = 1.0, max = 400, resolution = 1.0, + value = 40, + command = self.setNumSegs, side = TOP) + widget.component('hull')['relief'] = RIDGE + widget = self.createSlider( + sfFrame, 'Style', 'Num Ticks', + 'Set number of tick marks drawn for each unit of time', + min = 0.0, max = 10.0, resolution = 1.0, + value = 0.0, + command = self.setNumTicks, side = TOP) + widget.component('hull')['relief'] = RIDGE + widget = self.createSlider( + sfFrame, 'Style', 'Tick Scale', + 'Set visible size of time tick marks', + min = 0.01, max = 100.0, resolution = 0.01, + value = 5.0, + command = self.setTickScale, side = TOP) + widget.component('hull')['relief'] = RIDGE + self.createColorEntry( + sfFrame, 'Style', 'Path Color', + 'Color of curve', + command = self.setPathColor, + value = [255.0,255.0,255.0,255.0]) + self.createColorEntry( + sfFrame, 'Style', 'Knot Color', + 'Color of knots', + command = self.setKnotColor, + value = [0,0,255.0,255.0]) + self.createColorEntry( + sfFrame, 'Style', 'CV Color', + 'Color of CVs', + command = self.setCvColor, + value = [255.0,0,0,255.0]) + self.createColorEntry( + sfFrame, 'Style', 'Tick Color', + 'Color of Ticks', + command = self.setTickColor, + value = [255.0,0,0,255.0]) + self.createColorEntry( + sfFrame, 'Style', 'Hull Color', + 'Color of Hull', + command = self.setHullColor, + value = [255.0,128.0,128.0,255.0]) + + #drawFrame.pack(fill = X) + + ## OPTIONS PAGE ## + optionsFrame = Frame(self.optionsPage, relief = SUNKEN, + borderwidth = 2) + label = Label(optionsFrame, text = 'RECORDING OPTIONS', + font=('MSSansSerif', 12, 'bold')) + label.pack(fill = X) + # Hooks + frame = Frame(optionsFrame) + widget = self.createLabeledEntry( + frame, 'Recording', 'Record Hook', + 'Hook used to start/stop recording', + value = self.startStopHook, + command = self.setStartStopHook)[0] + label = self.getWidget('Recording', 'Record Hook-Label') + label.configure(width = 16, anchor = W) + self.setStartStopHook() + widget = self.createLabeledEntry( + frame, 'Recording', 'Keyframe Hook', + 'Hook used to add a new keyframe', + value = self.keyframeHook, + command = self.setKeyframeHook)[0] + label = self.getWidget('Recording', 'Keyframe Hook-Label') + label.configure(width = 16, anchor = W) + self.setKeyframeHook() + frame.pack(expand = 1, fill = X) + # PreRecordFunc + frame = Frame(optionsFrame) + widget = self.createComboBox( + frame, 'Recording', 'Pre-Record Func', + 'Function called before sampling each point', + PRF_UTILITIES, self.setPreRecordFunc, + history = 1, expand = 1) + widget.configure(label_width = 16, label_anchor = W) + widget.configure(entryfield_entry_state = 'normal') + # Initialize preRecordFunc + self.preRecordFunc = eval(PRF_UTILITIES[0]) + self.createCheckbutton(frame, 'Recording', 'PRF Active', + 'On: Pre Record Func enabled', + None, 0, + side = LEFT, fill = BOTH, expand = 0) + frame.pack(expand = 1, fill = X) + # Pack record frame + optionsFrame.pack(fill = X, pady = 2) + + self.mainNotebook.setnaturalsize() + + def pushUndo(self, fResetRedo = 1): + SEditor.pushUndo([self.nodePath]) + + def undoHook(self): + # Reflect new changes + pass + + def pushUndoHook(self): + # Make sure button is reactivated + self.undoButton.configure(state = 'normal') + + def undoListEmptyHook(self): + # Make sure button is deactivated + self.undoButton.configure(state = 'disabled') + + def pushRedo(self): + SEditor.pushRedo([self.nodePath]) + + def redoHook(self): + # Reflect new changes + pass + + def pushRedoHook(self): + # Make sure button is reactivated + self.redoButton.configure(state = 'normal') + + def redoListEmptyHook(self): + # Make sure button is deactivated + self.redoButton.configure(state = 'disabled') + + def selectedNodePathHook(self, nodePath): + """ + Hook called upon selection of a node path used to select playback + marker if subnode selected + """ + taskMgr.remove(self.name + '-curveEditTask') + print nodePath.id() + if nodePath.id() in self.playbackMarkerIds: + SEditor.select(self.playbackMarker) + elif nodePath.id() in self.tangentMarkerIds: + SEditor.select(self.tangentMarker) + elif nodePath.id() == self.playbackMarker.id(): + self.tangentGroup.show() + taskMgr.add(self.curveEditTask, + self.name + '-curveEditTask') + elif nodePath.id() == self.tangentMarker.id(): + self.tangentGroup.show() + taskMgr.add(self.curveEditTask, + self.name + '-curveEditTask') + else: + self.tangentGroup.hide() + + def getChildIds(self, nodePath): + ids = [nodePath.id()] + kids = nodePath.getChildrenAsList() + for kid in kids: + ids += self.getChildIds(kid) + return ids + + def deselectedNodePathHook(self, nodePath): + """ + Hook called upon deselection of a node path used to select playback + marker if subnode selected + """ + if ((nodePath.id() == self.playbackMarker.id()) or + (nodePath.id() == self.tangentMarker.id())): + self.tangentGroup.hide() + + def curveEditTask(self,state): + if self.curveCollection != None: + # Update curve position + if self.manipulandumId == self.playbackMarker.id(): + # Show playback marker + self.playbackMarker.getChild(0).show() + pos = Point3(0) + hpr = Point3(0) + pos = self.playbackMarker.getPos(self.nodePathParent) + hpr = self.playbackMarker.getHpr(self.nodePathParent) + self.curveCollection.adjustXyz( + self.playbackTime, VBase3(pos[0], pos[1], pos[2])) + self.curveCollection.adjustHpr( + self.playbackTime, VBase3(hpr[0], hpr[1], hpr[2])) + # Note: this calls recompute on the curves + self.nurbsCurveDrawer.draw() + # Update tangent + if self.manipulandumId == self.tangentMarker.id(): + # If manipulating marker, update tangent + # Hide playback marker + self.playbackMarker.getChild(0).hide() + # Where is tangent marker relative to playback marker + tan = self.tangentMarker.getPos() + # Transform this vector to curve space + tan2Curve = Vec3( + self.playbackMarker.getMat( + self.nodePathParent).xformVec(tan)) + # Update nurbs curve + self.curveCollection.getXyzCurve().adjustTangent( + self.playbackTime, + tan2Curve[0], tan2Curve[1], tan2Curve[2]) + # Note: this calls recompute on the curves + self.nurbsCurveDrawer.draw() + else: + # Show playback marker + self.playbackMarker.getChild(0).show() + # Update tangent marker line + tan = Point3(0) + self.curveCollection.getXyzCurve().getTangent( + self.playbackTime, tan) + # Transform this point to playback marker space + tan.assign( + self.nodePathParent.getMat( + self.playbackMarker).xformVec(tan)) + self.tangentMarker.setPos(tan) + # In either case update tangent line + self.tangentLines.setVertex(1, tan[0], tan[1], tan[2]) + return Task.cont + + def manipulateObjectStartHook(self): + self.manipulandumId = None + if SEditor.selected.last: + if SEditor.selected.last.id() == self.playbackMarker.id(): + self.manipulandumId = self.playbackMarker.id() + elif SEditor.selected.last.id() == self.tangentMarker.id(): + self.manipulandumId = self.tangentMarker.id() + + def manipulateObjectCleanupHook(self): + # Clear flag + self.manipulandumId = None + + def onDestroy(self, event): + # Remove hooks + for event, method in self.actionEvents: + self.ignore(event) + # remove start stop hook + self.ignore(self.startStopHook) + self.ignore(self.keyframeHook) + self.curveNodePath.reparentTo(self.recorderNodePath) + self.trace.reparentTo(self.recorderNodePath) + self.recorderNodePath.removeNode() + # Make sure markers are deselected + SEditor.deselect(self.playbackMarker) + SEditor.deselect(self.tangentMarker) + # Remove tasks + taskMgr.remove(self.name + '-recordTask') + taskMgr.remove(self.name + '-playbackTask') + taskMgr.remove(self.name + '-curveEditTask') + self.mopathRecorderNode.removeChildren() + self.mopathRecorderNode.removeNode() + messenger.send('mPath_close') + messenger.send('SGE_Update Explorer',[render]) + + def createNewPointSet(self, curveName = None): + if curveName == None: + self.pointSetName = self.name + '-ps-' + `self.pointSetCount` + else: + self.pointSetName = curveName + # Update dictionary and record pointer to new point set + self.pointSet = self.pointSetDict[self.pointSetName] = [] + # Update combo box + comboBox = self.getWidget('Mopath', 'Path:') + scrolledList = comboBox.component('scrolledlist') + listbox = scrolledList.component('listbox') + names = list(listbox.get(0,'end')) + names.append(self.pointSetName) + scrolledList.setlist(names) + comboBox.selectitem(self.pointSetName) + # Update count + self.pointSetCount += 1 + + def extractPointSetFromCurveFitter(self, curveName = None): + # Get new point set based on newly created curve + self.createNewPointSet(curveName) + for i in range(self.curveFitter.getNumSamples()): + time = self.curveFitter.getSampleT(i) + pos = Point3(self.curveFitter.getSampleXyz(i)) + hpr = Point3(self.curveFitter.getSampleHpr(i)) + self.pointSet.append([time, pos, hpr]) + + def extractPointSetFromCurveCollection(self, curveName=None): + # Use curve to compute new point set + # Record maxT + self.maxT = self.curveCollection.getMaxT() + # Determine num samples + # Limit point set to 1000 points and samples per second to 30 + samplesPerSegment = min(30.0, 1000.0/self.curveCollection.getMaxT()) + self.setNumSamples(self.maxT * samplesPerSegment) + # Sample the curve but don't create a new curve collection + self.sampleCurve(fCompute = 0, curveName = curveName) + # Update widgets based on new data + self.updateWidgets() + + def selectPointSetNamed(self, name): + self.pointSet = self.pointSetDict.get(name, None) + # Reload points into curve fitter + # Reset curve fitters + self.curveFitter.reset() + for time, pos, hpr in self.pointSet: + # Add it to the curve fitters + self.curveFitter.addXyzHpr(time, pos, hpr) + # Compute curve + self.computeCurves() + + def setPathVis(self): + if self.getVariable('Style', 'Path').get(): + self.curveNodePath.show() + else: + self.curveNodePath.hide() + + def setKnotVis(self): + self.nurbsCurveDrawer.setShowKnots( + self.getVariable('Style', 'Knots').get()) + + def setCvVis(self): + self.nurbsCurveDrawer.setShowCvs( + self.getVariable('Style', 'CVs').get()) + + def setHullVis(self): + self.nurbsCurveDrawer.setShowHull( + self.getVariable('Style', 'Hull').get()) + + def setTraceVis(self): + if self.getVariable('Style', 'Trace').get(): + self.trace.show() + else: + self.trace.hide() + + def setMarkerVis(self): + if self.getVariable('Style', 'Marker').get(): + self.playbackMarker.reparentTo(self.recorderNodePath) + else: + self.playbackMarker.reparentTo(hidden) + + def setNumSegs(self, value): + self.numSegs = int(value) + self.nurbsCurveDrawer.setNumSegs(self.numSegs) + + def setNumTicks(self, value): + self.nurbsCurveDrawer.setNumTicks(float(value)) + + def setTickScale(self, value): + self.nurbsCurveDrawer.setTickScale(float(value)) + + def setPathColor(self, color): + self.nurbsCurveDrawer.setColor( + color[0]/255.0,color[1]/255.0,color[2]/255.0) + self.nurbsCurveDrawer.draw() + + def setKnotColor(self, color): + self.nurbsCurveDrawer.setKnotColor( + color[0]/255.0,color[1]/255.0,color[2]/255.0) + + def setCvColor(self, color): + self.nurbsCurveDrawer.setCvColor( + color[0]/255.0,color[1]/255.0,color[2]/255.0) + + def setTickColor(self, color): + self.nurbsCurveDrawer.setTickColor( + color[0]/255.0,color[1]/255.0,color[2]/255.0) + + def setHullColor(self, color): + self.nurbsCurveDrawer.setHullColor( + color[0]/255.0,color[1]/255.0,color[2]/255.0) + + def setStartStopHook(self, event = None): + # Clear out old hook + self.ignore(self.startStopHook) + # Record new one + hook = self.getVariable('Recording', 'Record Hook').get() + self.startStopHook = hook + # Add new one + self.accept(self.startStopHook, self.toggleRecordVar) + + def setKeyframeHook(self, event = None): + # Clear out old hook + self.ignore(self.keyframeHook) + # Record new one + hook = self.getVariable('Recording', 'Keyframe Hook').get() + self.keyframeHook = hook + # Add new one + self.accept(self.keyframeHook, self.addKeyframe) + + def reset(self): + self.pointSet = [] + self.hasPoints = 0 + self.curveCollection = None + self.curveFitter.reset() + self.nurbsCurveDrawer.hide() + + def setSamplingMode(self, mode): + self.samplingMode = mode + + def disableKeyframeButton(self): + self.getWidget('Recording', 'Add Keyframe')['state'] = 'disabled' + def enableKeyframeButton(self): + self.getWidget('Recording', 'Add Keyframe')['state'] = 'normal' + + def setRecordingType(self, type): + self.recordingType.set(type) + + def setNewCurveMode(self): + self.setRecordingType('New Curve') + + def setRefineMode(self): + self.setRecordingType('Refine') + + def setExtendMode(self): + self.setRecordingType('Extend') + + def toggleRecordVar(self): + # Get recording variable + v = self.getVariable('Recording', 'Record') + # Toggle it + v.set(1 - v.get()) + # Call the command + self.toggleRecord() + + def toggleRecord(self): + if self.getVariable('Recording', 'Record').get(): + # Reparent a Marker to target nodePath to show where the recording start + self.markingNode = self.nodePath.getParent().attachNewNode('MopthMarkerNode') + self.nodePath.copyTo(self.markingNode) + self.markingNode.wrtReparentTo(render) + # Kill old tasks + taskMgr.remove(self.name + '-recordTask') + taskMgr.remove(self.name + '-curveEditTask') + # Remove old curve + self.nurbsCurveDrawer.hide() + # Reset curve fitters + self.curveFitter.reset() + # Update sampling mode button if necessary + if self.samplingMode == 'Continuous': + self.disableKeyframeButton() + # Create a new point set to hold raw data + self.createNewPointSet() + # Clear out old trace, get ready to draw new + self.initTrace() + # Keyframe mode? + if (self.samplingMode == 'Keyframe'): + # Record first point + self.lastPos.assign(Point3( + self.nodePath.getPos(self.nodePathParent))) + # Init delta time + self.deltaTime = 0.0 + # Record first point + self.recordPoint(self.recordStart) + # Everything else + else: + if ((self.recordingType.get() == 'Refine') or + (self.recordingType.get() == 'Extend')): + # Turn off looping playback + self.loopPlayback = 0 + # Update widget to reflect new value + self.getVariable('Playback', 'Loop').set(0) + # Select tempCS as playback nodepath + self.oldPlaybackNodePath = self.playbackNodePath + self.setPlaybackNodePath(self.tempCS) + # Parent record node path to temp + self.nodePath.reparentTo(self.playbackNodePath) + # Align with temp + self.nodePath.setPosHpr(0,0,0,0,0,0) + # Set playback start to self.recordStart + self.playbackGoTo(self.recordStart) + # start flying nodePath along path + self.startPlayback() + # Start new task + t = taskMgr.add( + self.recordTask, self.name + '-recordTask') + t.startTime = globalClock.getFrameTime() + else: + self.markingNode.removeNode() # Hide the marker in the end of recording + if self.samplingMode == 'Continuous': + # Kill old task + taskMgr.remove(self.name + '-recordTask') + if ((self.recordingType.get() == 'Refine') or + (self.recordingType.get() == 'Extend')): + # Reparent node path back to parent + self.nodePath.wrtReparentTo(self.nodePathParent) + # Restore playback Node Path + self.setPlaybackNodePath(self.oldPlaybackNodePath) + else: + # Add last point + self.addKeyframe(0) + # Reset sampling mode + self.setSamplingMode('Continuous') + self.enableKeyframeButton() + # Clean up after refine or extend + if ((self.recordingType.get() == 'Refine') or + (self.recordingType.get() == 'Extend')): + # Merge prePoints, pointSet, postPoints + self.mergePoints() + # Clear out pre and post list + self.prePoints = [] + self.postPoints = [] + # Reset recording mode + self.setNewCurveMode() + # Compute curve + self.computeCurves() + + def recordTask(self, state): + # Record raw data point + time = self.recordStart + ( + globalClock.getFrameTime() - state.startTime) + self.recordPoint(time) + return Task.cont + + def addKeyframe(self, fToggleRecord = 1): + # Make sure we're in a recording mode! + if (fToggleRecord and + (not self.getVariable('Recording', 'Record').get())): + # Set sampling mode + self.setSamplingMode('Keyframe') + # This will automatically add the first point + self.toggleRecordVar() + else: + # Use distance as a time + pos = self.nodePath.getPos(self.nodePathParent) + deltaPos = Vec3(pos - self.lastPos).length() + if deltaPos != 0: + # If we've moved at all, use delta Pos as time + self.deltaTime = self.deltaTime + deltaPos + else: + # Otherwise add one second + self.deltaTime = self.deltaTime + 1.0 + # Record point at new time + self.recordPoint(self.recordStart + self.deltaTime) + # Update last pos + self.lastPos.assign(pos) + + def easeInOut(self, t): + x = t * t + return (3 * x) - (2 * t * x) + + def setPreRecordFunc(self, func): + # Note: If func is one defined at command prompt, need to set + # __builtins__.func = func at command line + self.preRecordFunc = eval(func) + # Update widget to reflect new value + self.getVariable('Recording', 'PRF Active').set(1) + + def recordPoint(self, time): + # Call user define callback before recording point + if (self.getVariable('Recording', 'PRF Active').get() and + (self.preRecordFunc != None)): + self.preRecordFunc() + # Get point + pos = self.nodePath.getPos(self.nodePathParent) + hpr = self.nodePath.getHpr(self.nodePathParent) + qNP = Quat() + qNP.setHpr(hpr) + # Blend between recordNodePath and self.nodePath + if ((self.recordingType.get() == 'Refine') or + (self.recordingType.get() == 'Extend')): + if ((time < self.controlStart) and + ((self.controlStart - self.recordStart) != 0.0)): + rPos = self.playbackNodePath.getPos(self.nodePathParent) + rHpr = self.playbackNodePath.getHpr(self.nodePathParent) + qR = Quat() + qR.setHpr(rHpr) + t = self.easeInOut(((time - self.recordStart)/ + (self.controlStart - self.recordStart))) + # Transition between the recorded node path and the driven one + pos = (rPos * (1 - t)) + (pos * t) + q = qSlerp(qR, qNP, t) + hpr.assign(q.getHpr()) + elif ((self.recordingType.get() == 'Refine') and + (time > self.controlStop) and + ((self.recordStop - self.controlStop) != 0.0)): + rPos = self.playbackNodePath.getPos(self.nodePathParent) + rHpr = self.playbackNodePath.getHpr(self.nodePathParent) + qR = Quat() + qR.setHpr(rHpr) + t = self.easeInOut(((time - self.controlStop)/ + (self.recordStop - self.controlStop))) + # Transition between the recorded node path and the driven one + pos = (pos * (1 - t)) + (rPos * t) + q = qSlerp(qNP, qR, t) + hpr.assign(q.getHpr()) + # Add it to the point set + self.pointSet.append([time, pos, hpr]) + # Add it to the curve fitters + self.curveFitter.addXyzHpr(time, pos, hpr) + # Update trace now if recording keyframes + if (self.samplingMode == 'Keyframe'): + self.trace.reset() + for t, p, h in self.pointSet: + self.trace.drawTo(p[0], p[1], p[2]) + self.trace.create() + + def computeCurves(self): + # Check to make sure curve fitters have points + if (self.curveFitter.getNumSamples() == 0): + print 'MopathRecorder.computeCurves: Must define curve first' + return + # Create curves + # XYZ + self.curveFitter.sortPoints() + self.curveFitter.wrapHpr() + self.curveFitter.computeTangents(1) + # This is really a collection + self.curveCollection = self.curveFitter.makeNurbs() + self.nurbsCurveDrawer.setCurves(self.curveCollection) + self.nurbsCurveDrawer.draw() + # Update widget based on new curve + self.updateWidgets() + + def initTrace(self): + self.trace.reset() + # Put trace line segs under node path's parent + self.trace.reparentTo(self.nodePathParent) + # Show it + self.trace.show() + + def updateWidgets(self): + if not self.curveCollection: + return + self.fAdjustingValues = 1 + # Widgets depending on max T + maxT = self.curveCollection.getMaxT() + maxT_text = '%0.2f' % maxT + # Playback controls + self.getWidget('Playback', 'Time').configure(max = maxT_text) + self.getVariable('Resample', 'Path Duration').set(maxT_text) + # Refine widgets + widget = self.getWidget('Refine Page', 'Refine From') + widget.configure(max = maxT) + widget.set(0.0) + widget = self.getWidget('Refine Page', 'Control Start') + widget.configure(max = maxT) + widget.set(0.0) + widget = self.getWidget('Refine Page', 'Control Stop') + widget.configure(max = maxT) + widget.set(float(maxT)) + widget = self.getWidget('Refine Page', 'Refine To') + widget.configure(max = maxT) + widget.set(float(maxT)) + # Extend widgets + widget = self.getWidget('Extend Page', 'Extend From') + widget.configure(max = maxT) + widget.set(float(0.0)) + widget = self.getWidget('Extend Page', 'Control Start') + widget.configure(max = maxT) + widget.set(float(0.0)) + # Crop widgets + widget = self.getWidget('Crop Page', 'Crop From') + widget.configure(max = maxT) + widget.set(float(0.0)) + widget = self.getWidget('Crop Page', 'Crop To') + widget.configure(max = maxT) + widget.set(float(maxT)) + self.maxT = float(maxT) + # Widgets depending on number of samples + numSamples = self.curveFitter.getNumSamples() + widget = self.getWidget('Resample', 'Points Between Samples') + widget.configure(max=numSamples) + widget = self.getWidget('Resample', 'Num. Samples') + widget.configure(max = 4 * numSamples) + widget.set(numSamples, 0) + self.fAdjustingValues = 0 + + def selectNodePathNamed(self, name): + nodePath = None + if name == 'init': + nodePath = self.nodePath + # Add Combo box entry for the initial node path + self.addNodePath(nodePath) + elif name == 'selected': + nodePath = SEditor.selected.last + # Add Combo box entry for this selected object + self.addNodePath(nodePath) + else: + nodePath = self.nodePathDict.get(name, None) + if (nodePath == None): + # See if this evaluates into a node path + try: + nodePath = eval(name) + if isinstance(nodePath, NodePath): + self.addNodePath(nodePath) + else: + # Good eval but not a node path, give up + nodePath = None + except: + # Bogus eval + nodePath = None + # Clear bogus entry from listbox + listbox = self.nodePathMenu.component('scrolledlist') + listbox.setlist(self.nodePathNames) + else: + if name == 'widget': + # Record relationship between selected nodes and widget + SEditor.selected.getWrtAll() + if name == 'marker': + self.playbackMarker.show() + # Initialize tangent marker position + tan = Point3(0) + if self.curveCollection != None: + self.curveCollection.getXyzCurve().getTangent( + self.playbackTime, tan) + self.tangentMarker.setPos(tan) + else: + self.playbackMarker.hide() + # Update active node path + self.setNodePath(nodePath) + messenger.send('mPath_requestCurveList',[nodePath,self.name]) + self.accept('curveListFor'+self.name, self.addCurvesFromNodepath) + + + def setNodePath(self, nodePath): + self.playbackNodePath = self.nodePath = nodePath + if self.nodePath: + # Record nopath's parent + self.nodePathParent = self.nodePath.getParent() + # Put curve drawer under record node path's parent + self.curveNodePath.reparentTo(self.nodePathParent) + # Set entry color + self.nodePathMenuEntry.configure( + background = self.nodePathMenuBG) + else: + # Flash entry + self.nodePathMenuEntry.configure(background = 'Pink') + + def setPlaybackNodePath(self, nodePath): + self.playbackNodePath = nodePath + + def addNodePath(self, nodePath): + self.addNodePathToDict(nodePath, self.nodePathNames, + self.nodePathMenu, self.nodePathDict) + + def addNodePathToDict(self, nodePath, names, menu, dict): + if not nodePath: + return + # Get node path's name + name = nodePath.getName() + if name in ['mopathRecorderTempCS', 'widget', 'camera', 'marker']: + dictName = name + else: + # Generate a unique name for the dict + dictName = name # + '-' + `nodePath.id()` + if not dict.has_key(dictName): + # Update combo box to include new item + names.append(dictName) + listbox = menu.component('scrolledlist') + listbox.setlist(names) + # Add new item to dictionary + dict[dictName] = nodePath + menu.selectitem(dictName) + + def setLoopPlayback(self): + self.loopPlayback = self.getVariable('Playback', 'Loop').get() + + def playbackGoTo(self, time): + if self.curveCollection == None: + return + self.playbackTime = CLAMP(time, 0.0, self.maxT) + if self.curveCollection != None: + pos = Point3(0) + hpr = Point3(0) + self.curveCollection.evaluate(self.playbackTime, pos, hpr) + self.playbackNodePath.setPosHpr(self.nodePathParent, pos, hpr) + + def startPlayback(self): + if self.curveCollection == None: + return + # Kill any existing tasks + self.stopPlayback() + # Make sure checkbutton is set + self.getVariable('Playback', 'Play').set(1) + # Start new playback task + t = taskMgr.add( + self.playbackTask, self.name + '-playbackTask') + t.currentTime = self.playbackTime + t.lastTime = globalClock.getFrameTime() + + def setSpeedScale(self, value): + self.speedScale.set(math.log10(value)) + + def setPlaybackSF(self, value): + self.playbackSF = pow(10.0, float(value)) + self.speedVar.set('%0.2f' % self.playbackSF) + + def playbackTask(self, state): + time = globalClock.getFrameTime() + dTime = self.playbackSF * (time - state.lastTime) + state.lastTime = time + if self.loopPlayback: + cTime = (state.currentTime + dTime) % self.maxT + else: + cTime = state.currentTime + dTime + # Stop task if not looping and at end of curve + # Or if refining curve and past recordStop + if ((self.recordingType.get() == 'Refine') and + (cTime > self.recordStop)): + # Go to recordStop + self.getWidget('Playback', 'Time').set(self.recordStop) + # Then stop playback + self.stopPlayback() + # Also kill record task + self.toggleRecordVar() + return Task.done + elif ((self.loopPlayback == 0) and (cTime > self.maxT)): + # Go to maxT + self.getWidget('Playback', 'Time').set(self.maxT) + # Then stop playback + self.stopPlayback() + return Task.done + elif ((self.recordingType.get() == 'Extend') and + (cTime > self.controlStart)): + # Go to final point + self.getWidget('Playback', 'Time').set(self.controlStart) + # Stop playback + self.stopPlayback() + return Task.done + # Otherwise go to specified time and continue + self.getWidget('Playback', 'Time').set(cTime) + state.currentTime = cTime + return Task.cont + + def stopPlayback(self): + self.getVariable('Playback', 'Play').set(0) + taskMgr.remove(self.name + '-playbackTask') + + def jumpToStartOfPlayback(self): + self.stopPlayback() + self.getWidget('Playback', 'Time').set(0.0) + + def jumpToEndOfPlayback(self): + self.stopPlayback() + if self.curveCollection != None: + self.getWidget('Playback', 'Time').set(self.maxT) + + def startStopPlayback(self): + if self.getVariable('Playback', 'Play').get(): + self.startPlayback() + else: + self.stopPlayback() + + def setDesampleFrequency(self, frequency): + self.desampleFrequency = frequency + + def desampleCurve(self): + if (self.curveFitter.getNumSamples() == 0): + print 'MopathRecorder.desampleCurve: Must define curve first' + return + # NOTE: This is destructive, points will be deleted from curve fitter + self.curveFitter.desample(self.desampleFrequency) + # Compute new curve based on desampled data + self.computeCurves() + # Get point set from the curve fitter + self.extractPointSetFromCurveFitter() + + def setNumSamples(self, numSamples): + self.numSamples = int(numSamples) + + def sampleCurve(self, fCompute = 1, curveName = None): + if self.curveCollection == None: + print 'MopathRecorder.sampleCurve: Must define curve first' + return + # Reset curve fitters + self.curveFitter.reset() + # Sample curve using specified number of samples + self.curveFitter.sample(self.curveCollection, self.numSamples) + if fCompute: + # Now recompute curves + self.computeCurves() + # Get point set from the curve fitter + self.extractPointSetFromCurveFitter(curveName) + + + + def makeEven(self): + # Note: segments_per_unit = 2 seems to give a good fit + self.curveCollection.makeEven(self.maxT, 2) + # Get point set from curve + self.extractPointSetFromCurveCollection() + + def faceForward(self): + # Note: segments_per_unit = 2 seems to give a good fit + self.curveCollection.faceForward(2) + # Get point set from curve + self.extractPointSetFromCurveCollection() + + def setPathDuration(self, event): + newMaxT = float(self.getWidget('Resample', 'Path Duration').get()) + self.setPathDurationTo(newMaxT) + + def setPathDurationTo(self, newMaxT): + # Compute scale factor + sf = newMaxT/self.maxT + # Scale curve collection + self.curveCollection.resetMaxT(newMaxT) + # Scale point set + # Save handle to old point set + oldPointSet = self.pointSet + # Create new point set + self.createNewPointSet() + # Reset curve fitters + self.curveFitter.reset() + # Now scale values + for time, pos, hpr in oldPointSet: + newTime = time * sf + # Update point set + self.pointSet.append([newTime, Point3(pos), Point3(hpr)]) + # Add it to the curve fitters + self.curveFitter.addXyzHpr(newTime, pos, hpr) + # Update widgets + self.updateWidgets() + # Compute curve + #self.computeCurves() + + def setRecordStart(self,value): + self.recordStart = value + # Someone else is adjusting values, let them take care of it + if self.fAdjustingValues: + return + self.fAdjustingValues = 1 + # Adjust refine widgets + # Make sure we're in sync + self.getWidget('Refine Page', 'Refine From').set( + self.recordStart) + self.getWidget('Extend Page', 'Extend From').set( + self.recordStart) + # Check bounds + if self.recordStart > self.controlStart: + self.getWidget('Refine Page', 'Control Start').set( + self.recordStart) + self.getWidget('Extend Page', 'Control Start').set( + self.recordStart) + if self.recordStart > self.controlStop: + self.getWidget('Refine Page', 'Control Stop').set( + self.recordStart) + if self.recordStart > self.recordStop: + self.getWidget('Refine Page', 'Refine To').set(self.recordStart) + # Move playback node path to specified time + self.getWidget('Playback', 'Time').set(value) + self.fAdjustingValues = 0 + + def getPrePoints(self, type = 'Refine'): + # Switch to appropriate recording type + self.setRecordingType(type) + # Reset prePoints + self.prePoints = [] + # See if we need to save any points before recordStart + for i in range(len(self.pointSet)): + # Have we passed recordStart? + if self.recordStart < self.pointSet[i][0]: + # Get a copy of the points prior to recordStart + self.prePoints = self.pointSet[:i-1] + break + + def setControlStart(self, value): + self.controlStart = value + # Someone else is adjusting values, let them take care of it + if self.fAdjustingValues: + return + self.fAdjustingValues = 1 + # Adjust refine widgets + # Make sure both pages are in sync + self.getWidget('Refine Page', 'Control Start').set( + self.controlStart) + self.getWidget('Extend Page', 'Control Start').set( + self.controlStart) + # Check bounds on other widgets + if self.controlStart < self.recordStart: + self.getWidget('Refine Page', 'Refine From').set( + self.controlStart) + self.getWidget('Extend Page', 'Extend From').set( + self.controlStart) + if self.controlStart > self.controlStop: + self.getWidget('Refine Page', 'Control Stop').set( + self.controlStart) + if self.controlStart > self.recordStop: + self.getWidget('Refine Page', 'Refine To').set( + self.controlStart) + # Move playback node path to specified time + self.getWidget('Playback', 'Time').set(value) + self.fAdjustingValues = 0 + + def setControlStop(self, value): + self.controlStop = value + # Someone else is adjusting values, let them take care of it + if self.fAdjustingValues: + return + self.fAdjustingValues = 1 + if self.controlStop < self.recordStart: + self.getWidget('Refine Page', 'Refine From').set( + self.controlStop) + if self.controlStop < self.controlStart: + self.getWidget('Refine Page', 'Control Start').set( + self.controlStop) + if self.controlStop > self.recordStop: + self.getWidget('Refine Page', 'Refine To').set( + self.controlStop) + # Move playback node path to specified time + self.getWidget('Playback', 'Time').set(value) + self.fAdjustingValues = 0 + + def setRefineStop(self, value): + self.recordStop = value + # Someone else is adjusting values, let them take care of it + if self.fAdjustingValues: + return + self.fAdjustingValues = 1 + if self.recordStop < self.recordStart: + self.getWidget('Refine Page', 'Refine From').set( + self.recordStop) + if self.recordStop < self.controlStart: + self.getWidget('Refine Page', 'Control Start').set( + self.recordStop) + if self.recordStop < self.controlStop: + self.getWidget('Refine Page', 'Control Stop').set( + self.recordStop) + # Move playback node path to specified time + self.getWidget('Playback', 'Time').set(value) + self.fAdjustingValues = 0 + + def getPostPoints(self): + # Set flag so we know to do a refine pass + self.setRefineMode() + # Reset postPoints + self.postPoints = [] + # See if we need to save any points after recordStop + for i in range(len(self.pointSet)): + # Have we reached recordStop? + if self.recordStop < self.pointSet[i][0]: + # Get a copy of the points after recordStop + self.postPoints = self.pointSet[i:] + break + + def mergePoints(self): + # prepend pre points + self.pointSet[0:0] = self.prePoints + for time, pos, hpr in self.prePoints: + # Add it to the curve fitters + self.curveFitter.addXyzHpr(time, pos, hpr) + # And post points + # What is end time of pointSet? + endTime = self.pointSet[-1][0] + for time, pos, hpr in self.postPoints: + adjustedTime = endTime + (time - self.recordStop) + # Add it to point set + self.pointSet.append([adjustedTime, pos, hpr]) + # Add it to the curve fitters + self.curveFitter.addXyzHpr(adjustedTime, pos, hpr) + + def setCropFrom(self,value): + self.cropFrom = value + # Someone else is adjusting values, let them take care of it + if self.fAdjustingValues: + return + self.fAdjustingValues = 1 + if self.cropFrom > self.cropTo: + self.getWidget('Crop Page', 'Crop To').set( + self.cropFrom) + # Move playback node path to specified time + self.getWidget('Playback', 'Time').set(value) + self.fAdjustingValues = 0 + + def setCropTo(self,value): + self.cropTo = value + # Someone else is adjusting values, let them take care of it + if self.fAdjustingValues: + return + self.fAdjustingValues = 1 + if self.cropTo < self.cropFrom: + self.getWidget('Crop Page', 'Crop From').set( + self.cropTo) + # Move playback node path to specified time + self.getWidget('Playback', 'Time').set(value) + self.fAdjustingValues = 0 + + def cropCurve(self): + if self.pointSet == None: + print 'Empty Point Set' + return + # Keep handle on old points + oldPoints = self.pointSet + # Create new point set + self.createNewPointSet() + # Copy over points between from/to + # Reset curve fitters + self.curveFitter.reset() + # Add start point + pos = Point3(0) + hpr = Point3(0) + self.curveCollection.evaluate(self.cropFrom, pos, hpr) + self.curveFitter.addXyzHpr(0.0, pos, hpr) + # Get points within bounds + for time, pos, hpr in oldPoints: + # Is it within the time? + if ((time > self.cropFrom) and + (time < self.cropTo)): + # Add it to the curve fitters + t = time - self.cropFrom + self.curveFitter.addXyzHpr(t, pos, hpr) + # And the point set + self.pointSet.append([t, pos, hpr]) + # Add last point + pos = Vec3(0) + hpr = Vec3(0) + self.curveCollection.evaluate(self.cropTo, pos, hpr) + self.curveFitter.addXyzHpr(self.cropTo - self.cropFrom, pos, hpr) + # Compute curve + self.computeCurves() + + def loadCurveFromFile(self): + # Use first directory in model path + mPath = getModelPath() + if mPath.getNumDirectories() > 0: + if `mPath.getDirectory(0)` == '.': + path = '.' + else: + path = mPath.getDirectory(0).toOsSpecific() + else: + path = '.' + if not os.path.isdir(path): + print 'MopathRecorder Info: Empty Model Path!' + print 'Using current directory' + path = '.' + mopathFilename = askopenfilename( + defaultextension = '.egg', + filetypes = (('Egg Files', '*.egg'), + ('Bam Files', '*.bam'), + ('All files', '*')), + initialdir = path, + title = 'Load Nurbs Curve', + parent = self.parent) + if mopathFilename: + self.reset() + nodePath = loader.loadModel( + Filename.fromOsSpecific(mopathFilename)) + self.curveCollection = ParametricCurveCollection() + # MRM: Add error check + self.curveCollection.addCurves(nodePath.node()) + nodePath.removeNode() + if self.curveCollection: + # Draw the curve + self.nurbsCurveDrawer.setCurves(self.curveCollection) + self.nurbsCurveDrawer.draw() + # Save a pointset for this curve + self.extractPointSetFromCurveCollection() + else: + self.reset() + + def saveCurveToFile(self): + # Use first directory in model path + mPath = getModelPath() + if mPath.getNumDirectories() > 0: + if `mPath.getDirectory(0)` == '.': + path = '.' + else: + path = mPath.getDirectory(0).toOsSpecific() + else: + path = '.' + if not os.path.isdir(path): + print 'MopathRecorder Info: Empty Model Path!' + print 'Using current directory' + path = '.' + mopathFilename = asksaveasfilename( + defaultextension = '.egg', + filetypes = (('Egg Files', '*.egg'), + ('Bam Files', '*.bam'), + ('All files', '*')), + initialdir = path, + title = 'Save Nurbs Curve as', + parent = self.parent) + if mopathFilename: + self.curveCollection.writeEgg(Filename(mopathFilename)) + + def followTerrain(self, height = 1.0): + self.iRay.rayCollisionNodePath.reparentTo(self.nodePath) + entry = self.iRay.pickGeom3D() + if entry: + hitPtDist = Vec3(entry.getFromIntersectionPoint()).length() + self.nodePath.setZ(self.nodePath, height - hitPtDist) + self.iRay.rayCollisionNodePath.reparentTo(self.recorderNodePath) + + ## WIDGET UTILITY FUNCTIONS ## + def addWidget(self, widget, category, text): + self.widgetDict[category + '-' + text] = widget + + def getWidget(self, category, text): + return self.widgetDict[category + '-' + text] + + def getVariable(self, category, text): + return self.variableDict[category + '-' + text] + + def createLabeledEntry(self, parent, category, text, balloonHelp, + value = '', command = None, + relief = 'sunken', side = LEFT, + expand = 1, width = 12): + frame = Frame(parent) + variable = StringVar() + variable.set(value) + label = Label(frame, text = text) + label.pack(side = LEFT, fill = X) + self.bind(label, balloonHelp) + self.widgetDict[category + '-' + text + '-Label'] = label + entry = Entry(frame, width = width, relief = relief, + textvariable = variable) + entry.pack(side = LEFT, fill = X, expand = expand) + self.bind(entry, balloonHelp) + self.widgetDict[category + '-' + text] = entry + self.variableDict[category + '-' + text] = variable + if command: + entry.bind('', command) + frame.pack(side = side, fill = X, expand = expand) + return (frame, label, entry) + + def createButton(self, parent, category, text, balloonHelp, command, + side = 'top', expand = 0, fill = X): + widget = Button(parent, text = text) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(side = side, fill = fill, expand = expand) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createCheckbutton(self, parent, category, text, + balloonHelp, command, initialState, + side = 'top', fill = X, expand = 0): + bool = BooleanVar() + bool.set(initialState) + widget = Checkbutton(parent, text = text, anchor = W, + variable = bool) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(side = side, fill = fill, expand = expand) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + self.variableDict[category + '-' + text] = bool + return widget + + def createRadiobutton(self, parent, side, category, text, + balloonHelp, variable, value, + command = None, fill = X, expand = 0): + widget = Radiobutton(parent, text = text, anchor = W, + variable = variable, value = value) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(side = side, fill = fill, expand = expand) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createFloater(self, parent, category, text, balloonHelp, + command = None, min = 0.0, resolution = None, + maxVelocity = 10.0, **kw): + kw['text'] = text + kw['min'] = min + kw['maxVelocity'] = maxVelocity + kw['resolution'] = resolution + widget = apply(Floater.Floater, (parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createAngleDial(self, parent, category, text, balloonHelp, + command = None, **kw): + kw['text'] = text + widget = apply(Dial.AngleDial,(parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createSlider(self, parent, category, text, balloonHelp, + command = None, min = 0.0, max = 1.0, + resolution = None, + side = TOP, fill = X, expand = 1, **kw): + kw['text'] = text + kw['min'] = min + kw['max'] = max + kw['resolution'] = resolution + #widget = apply(EntryScale.EntryScale, (parent,), kw) + import Slider + widget = apply(Slider.Slider, (parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(side = side, fill = fill, expand = expand) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createEntryScale(self, parent, category, text, balloonHelp, + command = None, min = 0.0, max = 1.0, + resolution = None, + side = TOP, fill = X, expand = 1, **kw): + kw['text'] = text + kw['min'] = min + kw['max'] = max + kw['resolution'] = resolution + widget = apply(EntryScale.EntryScale, (parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(side = side, fill = fill, expand = expand) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createVector2Entry(self, parent, category, text, balloonHelp, + command = None, **kw): + # Set label's text + kw['text'] = text + widget = apply(VectorWidgets.Vector2Entry, (parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createVector3Entry(self, parent, category, text, balloonHelp, + command = None, **kw): + # Set label's text + kw['text'] = text + widget = apply(VectorWidgets.Vector3Entry, (parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createColorEntry(self, parent, category, text, balloonHelp, + command = None, **kw): + # Set label's text + kw['text'] = text + widget = apply(VectorWidgets.ColorEntry, (parent,) ,kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createOptionMenu(self, parent, category, text, balloonHelp, + items, command): + optionVar = StringVar() + if len(items) > 0: + optionVar.set(items[0]) + widget = Pmw.OptionMenu(parent, labelpos = W, label_text = text, + label_width = 12, menu_tearoff = 1, + menubutton_textvariable = optionVar, + items = items) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget.component('menubutton'), balloonHelp) + self.widgetDict[category + '-' + text] = widget + self.variableDict[category + '-' + text] = optionVar + return optionVar + + def createComboBox(self, parent, category, text, balloonHelp, + items, command, history = 0, + side = LEFT, expand = 0, fill = X): + widget = Pmw.ComboBox(parent, + labelpos = W, + label_text = text, + label_anchor = 'e', + label_width = 12, + entry_width = 16, + history = history, + scrolledlist_items = items) + # Don't allow user to edit entryfield + widget.configure(entryfield_entry_state = 'disabled') + # Select first item if it exists + if len(items) > 0: + widget.selectitem(items[0]) + # Bind selection command + widget['selectioncommand'] = command + widget.pack(side = side, fill = fill, expand = expand) + # Bind help + self.bind(widget, balloonHelp) + # Record widget + self.widgetDict[category + '-' + text] = widget + return widget + + def makeCameraWindow(self): + # First, we need to make a new layer on the window. + chan = base.win.getChannel(0) + self.cLayer = chan.makeLayer(1) + self.layerIndex = 1 + self.cDr = self.cLayer.makeDisplayRegion(0.6, 1.0, 0, 0.4) + self.cDr.setClearDepthActive(1) + self.cDr.setClearColorActive(1) + self.cDr.setClearColor(Vec4(0)) + + # It gets its own camera + self.cCamera = render.attachNewNode('cCamera') + self.cCamNode = Camera('cCam') + self.cLens = PerspectiveLens() + self.cLens.setFov(40,40) + self.cLens.setNear(0.1) + self.cLens.setFar(100.0) + self.cCamNode.setLens(self.cLens) + self.cCamNode.setScene(render) + self.cCam = self.cCamera.attachNewNode(self.cCamNode) + + self.cDr.setCamera(self.cCam) + + def toggleWidgetVis(self): + ## In order to make sure everything is going on right way... + messenger.send('SEditor-ToggleWidgetVis') + SEditor.toggleWidgetVis() + + + def bindMotionPathToNode(self): + if self.curveCollection == None: + print '----Error: you need to select or create a curve first!' + return + self.accept('MP_checkName', self.bindMotionPath) + self.askName = namePathPanel(MopathRecorder.count) + return + + def bindMotionPath(self,name=None,test=None): + print test + self.ignore('MP_checkName') + del self.askName + self.curveCollection.getCurve(0).setName(name) + comboBox = self.getWidget('Mopath', 'Path:') + oldName = comboBox.get() + self.pointSetDict[name] = self.pointSetDict[oldName] + del self.pointSetDict[oldName] + scrolledList = comboBox.component('scrolledlist') + listbox = scrolledList.component('listbox') + names = list(listbox.get(0,'end')) + num = names.index(oldName) + names.pop(num) + names.append(name) + scrolledList.setlist(names) + comboBox.selectitem(name) + messenger.send('mPath_bindPathToNode',[self.playbackNodePath, self.curveCollection]) + return + + + + def addCurvesFromNodepath(self,curveList): + '''addCurvesFromNodepath(self,curveList) + This function will take a curveCollection list as a input. + If the list is not None, it will put the vurve back into the curve list. + else, do nothing. + ''' + print curveList + self.ignore('curveListFor'+self.name) + if curveList != None : + for collection in curveList: + self.curveCollection = collection + self.extractPointSetFromCurveCollection(curveName=self.curveCollection.getCurve(0).getName()) + else: + pass + return + + +class namePathPanel(AppShell): + # Override class variables + appname = 'Name the Path' + frameWidth = 575 + frameHeight = 200 + usecommandarea = 0 + usestatusarea = 0 + index = 0 + + def __init__(self, count, parent = None, **kw): + INITOPT = Pmw.INITOPT + self.id = 'Name the Path' + self.appname = self.id + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + + # Initialize the superclass + AppShell.__init__(self) + + self.parent.resizable(False,False) + + # Execute option callbacks + self.initialiseoptions(namePathPanel) + + def createInterface(self): + self.menuBar.destroy() + interior = self.interior() + mainFrame = Frame(interior) + + dataFrame = Frame(mainFrame) + label = Label(dataFrame, text='This name will be used as a reference for this Path.',font=('MSSansSerif', 10)) + label.pack(side = TOP, expand = 0, fill = X) + dataFrame.pack(side = TOP, expand = 0, fill = X, padx=5, pady=10) + + dataFrame = Frame(mainFrame) + self.inputZone = Pmw.EntryField(dataFrame, labelpos='w', label_text = 'Name Selected Path: ', + entry_font=('MSSansSerif', 10), + label_font=('MSSansSerif', 10), + validate = None, + entry_width = 20) + self.inputZone.pack(side = LEFT, fill=X,expand=0) + + self.button_ok = Button(dataFrame, text="OK", command=self.ok_press,width=10) + self.button_ok.pack(fill=X,expand=0,side=LEFT, padx = 3) + + dataFrame.pack(side = TOP, expand = 0, fill = X, padx=10, pady=10) + + mainFrame.pack(expand = 1, fill = BOTH) + + + + def onDestroy(self, event): + ''' + If you have open any thing, please rewrite here! + ''' + pass + + + def ok_press(self): + name = self.inputZone.getvalue() + messenger.send('MP_checkName',[name]) + self.quit() + return + + diff --git a/doc/SceneEditor/seParticleEffect.py b/doc/SceneEditor/seParticleEffect.py new file mode 100644 index 0000000000..b47d156d2d --- /dev/null +++ b/doc/SceneEditor/seParticleEffect.py @@ -0,0 +1,267 @@ +from PandaModules import * +import seParticles +import seForceGroup +import DirectNotifyGlobal + +class ParticleEffect(NodePath): + + notify = DirectNotifyGlobal.directNotify.newCategory('ParticleEffect') + pid = 1 + + def __init__(self, name=None, particles=None): + """__init__()""" + if (name == None): + name = 'particle-effect-%d' % ParticleEffect.pid + ParticleEffect.pid += 1 + NodePath.__init__(self, name) + # Record particle effect name + self.name = name + # Enabled flag + self.fEnabled = 0 + # Dictionary of particles and forceGroups + self.particlesDict = {} + self.forceGroupDict = {} + # The effect's particle system + if (particles != None): + self.addParticles(particles) + self.renderParent = None + + def start(self, parent=None, renderParent=None): + assert(self.notify.debug('start() - name: %s' % self.name)) + self.renderParent = renderParent + self.enable() + if (parent != None): + self.reparentTo(parent) + + def cleanup(self): + self.removeNode() + self.disable() + for f in self.forceGroupDict.values(): + f.cleanup() + for p in self.particlesDict.values(): + p.cleanup() + del self.renderParent + del self.particlesDict + del self.forceGroupDict + + def reset(self): + self.removeAllForces() + self.removeAllParticles() + self.forceGroupDict = {} + self.particlesDict = {} + + def enable(self): + """enable()""" + if (self.renderParent != None): + for p in self.particlesDict.values(): + p.setRenderParent(self.renderParent.node()) + for f in self.forceGroupDict.values(): + f.enable() + for p in self.particlesDict.values(): + p.enable() + self.fEnabled = 1 + + def disable(self): + """disable()""" + self.detachNode() + for p in self.particlesDict.values(): + p.setRenderParent(p.node) + for f in self.forceGroupDict.values(): + f.disable() + for p in self.particlesDict.values(): + p.disable() + self.fEnabled = 0 + + def isEnabled(self): + """ + isEnabled() + Note: this may be misleading if enable(),disable() not used + """ + return self.fEnabled + + def addForceGroup(self, forceGroup): + """addForceGroup(forceGroup)""" + forceGroup.nodePath.reparentTo(self) + forceGroup.particleEffect = self + self.forceGroupDict[forceGroup.getName()] = forceGroup + + # Associate the force group with all particles + for i in range(len(forceGroup)): + self.addForce(forceGroup[i]) + + def addForce(self, force): + """addForce(force)""" + for p in self.particlesDict.values(): + p.addForce(force) + + def removeForceGroup(self, forceGroup): + """removeForceGroup(forceGroup)""" + # Remove forces from all particles + for i in range(len(forceGroup)): + self.removeForce(forceGroup[i]) + + forceGroup.nodePath.removeNode() + forceGroup.particleEffect = None + del self.forceGroupDict[forceGroup.getName()] + + def removeForce(self, force): + """removeForce(force)""" + for p in self.particlesDict.values(): + p.removeForce(force) + + def removeAllForces(self): + for fg in self.forceGroupDict.values(): + self.removeForceGroup(fg) + + def addParticles(self, particles): + """addParticles(particles)""" + particles.nodePath.reparentTo(self) + self.particlesDict[particles.getName()] = particles + + # Associate all forces in all force groups with the particles + for fg in self.forceGroupDict.values(): + for i in range(len(fg)): + particles.addForce(fg[i]) + + def removeParticles(self, particles): + """removeParticles(particles)""" + if (particles == None): + self.notify.warning('removeParticles() - particles == None!') + return + particles.nodePath.detachNode() + del self.particlesDict[particles.getName()] + + # Remove all forces from the particles + for fg in self.forceGroupDict.values(): + for f in fg.asList(): + particles.removeForce(f) + + def removeAllParticles(self): + for p in self.particlesDict.values(): + self.removeParticles(p) + + def getParticlesList(self): + """getParticles()""" + return self.particlesDict.values() + + def getParticlesNamed(self, name): + """getParticlesNamed(name)""" + return self.particlesDict.get(name, None) + + def getParticlesDict(self): + """getParticlesDict()""" + return self.particlesDict + + def getForceGroupList(self): + """getForceGroup()""" + return self.forceGroupDict.values() + + def getForceGroupNamed(self, name): + """getForceGroupNamed(name)""" + return self.forceGroupDict.get(name, None) + + def getForceGroupDict(self): + """getForceGroup()""" + return self.forceGroupDict + + def saveConfig(self, filename): + """saveFileData(filename)""" + f = open(filename.toOsSpecific(), 'wb') + # Add a blank line + f.write('\n') + + # Make sure we start with a clean slate + f.write('self.reset()\n') + + pos = self.getPos() + hpr = self.getHpr() + scale = self.getScale() + f.write('self.setPos(%0.3f, %0.3f, %0.3f)\n' % + (pos[0], pos[1], pos[2])) + f.write('self.setHpr(%0.3f, %0.3f, %0.3f)\n' % + (hpr[0], hpr[1], hpr[2])) + f.write('self.setScale(%0.3f, %0.3f, %0.3f)\n' % + (scale[0], scale[1], scale[2])) + + # Save all the particles to file + num = 0 + for p in self.particlesDict.values(): + target = 'p%d' % num + num = num + 1 + f.write(target + ' = Particles.Particles(\'%s\')\n' % p.getName()) + p.printParams(f, target) + f.write('self.addParticles(%s)\n' % target) + + # Save all the forces to file + num = 0 + for fg in self.forceGroupDict.values(): + target = 'f%d' % num + num = num + 1 + f.write(target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \ + fg.getName()) + fg.printParams(f, target) + f.write('self.addForceGroup(%s)\n' % target) + + # Close the file + f.close() + + def loadConfig(self, filename): + """loadConfig(filename)""" + #try: + # if vfs: + print vfs.readFile(filename) + exec vfs.readFile(filename) + print "Particle Effect Reading using VFS" + # else: + # execfile(filename.toOsSpecific()) + # print "Shouldnt be wrong" + #except: + # self.notify.error('loadConfig: failed to load particle file: '+ repr(filename)) + + + + def AppendConfig(self, f): + f.write('\n') + i1=" " + i2=i1+i1 + # Make sure we start with a clean slate + f.write(i2+'self.effect.reset()\n') + + pos = self.getPos() + hpr = self.getHpr() + scale = self.getScale() + f.write(i2+'self.effect.setPos(%0.3f, %0.3f, %0.3f)\n' % + (pos[0], pos[1], pos[2])) + f.write(i2+'self.effect.setHpr(%0.3f, %0.3f, %0.3f)\n' % + (hpr[0], hpr[1], hpr[2])) + f.write(i2+'self.effect.setScale(%0.3f, %0.3f, %0.3f)\n' % + (scale[0], scale[1], scale[2])) + + # Save all the particles to file + num = 0 + for p in self.particlesDict.values(): + target = 'p%d' % num + num = num + 1 + f.write(i2+"if(mode==0):\n") + f.write(i2+i1+target + ' = seParticles.Particles(\'%s\')\n' % p.getName()) + f.write(i2+"else:\n") + f.write(i2+i1+target + ' = Particles.Particles(\'%s\')\n' % p.getName()) + p.printParams(f, target) + f.write(i2+'self.effect.addParticles(%s)\n' % target) + + # Save all the forces to file + num = 0 + for fg in self.forceGroupDict.values(): + target = 'f%d' % num + num = num + 1 + f.write(i2+target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \ + fg.getName()) + fg.printParams(f, target) + f.write(i2+'self.effect.addForceGroup(%s)\n' % target) + + + + + + + diff --git a/doc/SceneEditor/seParticlePanel.py b/doc/SceneEditor/seParticlePanel.py new file mode 100644 index 0000000000..cad5dbb70f --- /dev/null +++ b/doc/SceneEditor/seParticlePanel.py @@ -0,0 +1,1976 @@ +"""PANDA3D Particle Panel""" + +# Import Tkinter, Pmw, and the floater code from this directory tree. +from AppShell import * +from TkGlobal import * +from tkFileDialog import * +from tkSimpleDialog import askstring +import os +import Dial +import Floater +import Slider +import VectorWidgets +import sePlacer +import seForceGroup +import seParticles +import seParticleEffect + +class ParticlePanel(AppShell): + # Override class variables + appname = 'Particle Panel' + frameWidth = 375 + frameHeight = 575 + usecommandarea = 0 + usestatusarea = 0 + balloonState = 'both' + effectsDict={} + def __init__(self, particleEffect = None, effectsDict={}, **kw): + INITOPT = Pmw.INITOPT + optiondefs = ( + ('title', self.appname, None), + ) + self.defineoptions(kw, optiondefs) + + # Record particle effect + if particleEffect != None: + self.particleEffect = particleEffect + self.effectsDict = effectsDict + else: + # Or create a new one if none given + particles = seParticles.Particles() + particles.setBirthRate(0.02) + particles.setLitterSize(10) + particles.setLitterSpread(0) + particles.setFactory("PointParticleFactory") + particles.setRenderer("PointParticleRenderer") + particles.setEmitter("SphereVolumeEmitter") + particles.enable() + pe = seParticleEffect.ParticleEffect('effect1', particles) + self.particleEffect = pe + self.emitter=loader.loadModel("sphere") + pe.reparentTo(self.emitter) + self.emitter.setName("effect1") + self.emitter.reparentTo(render) + pe.enable() + messenger.send('ParticlePanel_Added_Effect',['effect1',pe,self.emitter]) + self.effectsDict[self.particleEffect.getName()]=self.particleEffect + + + messenger.send('SGE_Update Explorer',[render]) + + # Initialize application specific info + AppShell.__init__(self) + + # Initialize panel Pmw options + self.initialiseoptions(ParticlePanel) + # Update panel values to reflect particle effect's state + self.selectEffectNamed(self.effectsDict.keys()[0]) + # Make sure labels/menus reflect current state + self.updateMenusAndLabels() + # Make sure there is a page for each forceGroup objects + for forceGroup in self.particleEffect.getForceGroupList(): + self.addForceGroupNotebookPage(self.particleEffect, forceGroup) + + def appInit(self): + # Create dictionaries to keep track of panel objects + self.widgetDict = {} + self.variableDict = {} + self.forcePagesDict = {} + # Make sure particles are enabled + base.enableParticles() + + def onDestroy(self, event): + messenger.send('ParticlePanle_close') + return + + def createInterface(self): + # Handle to the toplevels hull + interior = self.interior() + + # Create particle panel menu items + + ## MENUBAR ENTRIES ## + # FILE MENU + # Get a handle on the file menu so commands can be inserted + # before quit item + fileMenu = self.menuBar.component('File-menu') + # MRM: Need to add load and save effects methods + fileMenu.insert_command( + fileMenu.index('Quit'), + label = 'Load Params', + command = self.loadParticleEffectFromFile) + fileMenu.insert_command( + fileMenu.index('Quit'), + label = 'Save Params', + command = self.saveParticleEffectToFile) + fileMenu.insert_command( + fileMenu.index('Quit'), + label = 'Print Params', + command = lambda s = self: s.particles.printParams()) + + # PARTICLE MANAGER MENU + self.menuBar.addmenu('ParticleMgr', 'ParticleMgr Operations') + self.particleMgrActive = IntVar() + self.particleMgrActive.set(base.isParticleMgrEnabled()) + self.menuBar.addmenuitem( + 'ParticleMgr', 'checkbutton', + 'Enable/Disable ParticleMgr', + label = 'Active', + variable = self.particleMgrActive, + command = self.toggleParticleMgr) + + ## MENUBUTTON LABELS ## + # Menubutton/label to identify the current objects being configured + labelFrame = Frame(interior) + # Current effect + self.effectsLabel = Menubutton(labelFrame, width = 10, + relief = RAISED, + borderwidth = 2, + font=('MSSansSerif', 12, 'bold'), + activebackground = '#909090') + self.effectsLabelMenu = Menu(self.effectsLabel, tearoff = 0) + self.effectsLabel['menu'] = self.effectsLabelMenu + self.effectsLabel.pack(side = LEFT, fill = 'x', expand = 1) + self.bind(self.effectsLabel, + 'Select effect to configure or create new effect') + self.effectsLabelMenu.add_command(label = 'Create New Effect', + command = self.createNewEffect) + self.effectsLabelMenu.add_command( + label = 'Select Particle Effect', + command = lambda s = self: SEditor.select(s.particleEffect)) + self.effectsLabelMenu.add_command( + label = 'Place Particle Effect', + command = lambda s = self: sePlacer.place(s.particleEffect)) + def togglePEVis(s = self): + if s.particleEffect.isHidden(): + s.particleEffect.show() + else: + s.particleEffect.hide() + self.effectsLabelMenu.add_command( + label = 'Toggle Effect Vis', + command = togglePEVis) + self.effectsEnableMenu = Menu(self.effectsLabelMenu, tearoff = 0) + self.effectsLabelMenu.add_cascade(label = 'Enable/Disable', + menu = self.effectsEnableMenu) + self.effectsLabelMenu.add_separator() + # Current particles + self.particlesLabel = Menubutton(labelFrame, width = 10, + relief = RAISED, + borderwidth = 2, + font=('MSSansSerif', 12, 'bold'), + activebackground = '#909090') + self.particlesLabelMenu = Menu(self.particlesLabel, tearoff = 0) + self.particlesLabel['menu'] = self.particlesLabelMenu + self.particlesLabel.pack(side = LEFT, fill = 'x', expand = 1) + self.bind(self.particlesLabel, + ('Select particles object to configure ' + + 'or add new particles object to current effect' )) + self.particlesLabelMenu.add_command(label = 'Create New Particles', + command = self.createNewParticles) + self.particlesEnableMenu = Menu(self.particlesLabelMenu, tearoff = 0) + self.particlesLabelMenu.add_cascade(label = 'Enable/Disable', + menu = self.particlesEnableMenu) + self.particlesLabelMenu.add_separator() + # Current force + self.forceGroupLabel = Menubutton(labelFrame, width = 10, + relief = RAISED, + borderwidth = 2, + font=('MSSansSerif', 12, 'bold'), + activebackground = '#909090') + self.forceGroupLabelMenu = Menu(self.forceGroupLabel, tearoff = 0) + self.forceGroupLabel['menu'] = self.forceGroupLabelMenu + self.forceGroupLabel.pack(side = LEFT, fill = 'x', expand = 1) + self.bind(self.forceGroupLabel, + ('Select force group to configure ' + + 'or add a new force group to current effect')) + self.forceGroupLabelMenu.add_command( + label = 'Create New ForceGroup', + command = self.createNewForceGroup) + self.forceGroupEnableMenu = Menu(self.forceGroupLabelMenu, tearoff = 0) + self.forceGroupLabelMenu.add_cascade(label = 'Enable/Disable', + menu = self.forceGroupEnableMenu) + self.forceGroupLabelMenu.add_separator() + # Pack labels + labelFrame.pack(fill = 'x', expand = 0) + + # Create the toplevel notebook pages + self.mainNotebook = Pmw.NoteBook(interior) + self.mainNotebook.pack(fill = BOTH, expand = 1) + systemPage = self.mainNotebook.add('System') + factoryPage = self.mainNotebook.add('Factory') + emitterPage = self.mainNotebook.add('Emitter') + rendererPage = self.mainNotebook.add('Renderer') + forcePage = self.mainNotebook.add('Force') + # Put this here so it isn't called right away + self.mainNotebook['raisecommand'] = self.updateInfo + + ## SYSTEM PAGE WIDGETS ## + # Create system floaters + systemFloaterDefs = ( + ('System', 'Pool Size', + 'Max number of simultaneous particles', + self.setSystemPoolSize, + 1.0, 1.0), + ('System', 'Birth Rate', + 'Seconds between particle births', + self.setSystemBirthRate, + 0.0, None), + ('System', 'Litter Size', + 'Number of particle created at each birth', + self.setSystemLitterSize, + 1.0, 1.0), + ('System', 'Litter Spread', + 'Variation in litter size', + self.setSystemLitterSpread, + 0.0, 1.0), + ('System', 'Lifespan', + 'Age in seconds at which the system (vs. particles) should die', + self.setSystemLifespan, + 0.0, None) + ) + self.createFloaters(systemPage, systemFloaterDefs) + + # Checkboxes + self.createCheckbutton( + systemPage, 'System', 'Render Space Velocities', + ('On: velocities are in render space; ' + + 'Off: velocities are in particle local space'), + self.toggleSystemLocalVelocity, 0) + self.createCheckbutton( + systemPage, 'System', 'System Grows Older', + 'On: system has a lifespan', + self.toggleSystemGrowsOlder, 0) + + # Vector widgets + pos = self.createVector3Entry(systemPage, 'System', 'Pos', + 'Particle system position', + command = self.setSystemPos) + pos.addMenuItem('Popup Placer Panel', sePlacer.Placer) + hpr = self.createVector3Entry(systemPage, 'System', 'Hpr', + 'Particle system orientation', + fGroup_labels = ('H', 'P', 'R'), + command = self.setSystemHpr) + hpr.addMenuItem('Popup Placer Panel', sePlacer.Placer) + + ## FACTORY PAGE WIDGETS ## + self.createOptionMenu( + factoryPage, + 'Factory', 'Factory Type', + 'Select type of particle factory', + ('PointParticleFactory', 'ZSpinParticleFactory', + 'OrientedParticleFactory'), + self.selectFactoryType) + factoryWidgets = ( + ('Factory', 'Life Span', + 'Average particle lifespan in seconds', + self.setFactoryLifeSpan, + 0.0, None), + ('Factory', 'Life Span Spread', + 'Variation in lifespan', + self.setFactoryLifeSpanSpread, + 0.0, None), + ('Factory', 'Mass', + 'Average particle mass', + self.setFactoryParticleMass, + 0.001, None), + ('Factory', 'Mass Spread', + 'Variation in particle mass', + self.setFactoryParticleMassSpread, + 0.0, None), + ('Factory', 'Terminal Velocity', + 'Cap on average particle velocity', + self.setFactoryTerminalVelocity, + 0.0, None), + ('Factory', 'Terminal Vel. Spread', + 'Variation in terminal velocity', + self.setFactoryTerminalVelocitySpread, + 0.0, None), + ) + self.createFloaters(factoryPage, factoryWidgets) + + self.factoryNotebook = Pmw.NoteBook(factoryPage, tabpos = None) + # Point page # + factoryPointPage = self.factoryNotebook.add('PointParticleFactory') + # Z spin page # + zSpinPage = self.factoryNotebook.add('ZSpinParticleFactory') + + self.createCheckbutton( + zSpinPage, 'Z Spin Factory', 'Enable Angular Velocity', + ("On: angular velocity is used; " + + "Off: final angle is used"), + self.toggleAngularVelocity, 0, side = TOP), + + self.createFloater( + zSpinPage, 'Z Spin Factory', 'Angular Velocity', + 'How fast sprites rotate', + command = self.setFactoryZSpinAngularVelocity) + + self.createFloater( + zSpinPage, 'Z Spin Factory', 'Angular Velocity Spread', + 'Variation in how fast sprites rotate', + command = self.setFactoryZSpinAngularVelocitySpread) + + self.createAngleDial(zSpinPage, 'Z Spin Factory', 'Initial Angle', + 'Starting angle in degrees', + fRollover = 1, + command = self.setFactoryZSpinInitialAngle) + self.createAngleDial( + zSpinPage, 'Z Spin Factory', + 'Initial Angle Spread', + 'Spread of the initial angle', + fRollover = 1, + command = self.setFactoryZSpinInitialAngleSpread) + self.createAngleDial(zSpinPage, 'Z Spin Factory', 'Final Angle', + 'Final angle in degrees', + fRollover = 1, + command = self.setFactoryZSpinFinalAngle) + self.createAngleDial( + zSpinPage, 'Z Spin Factory', + 'Final Angle Spread', + 'Spread of the final angle', + fRollover = 1, + command = self.setFactoryZSpinFinalAngleSpread) + # Oriented page # + orientedPage = self.factoryNotebook.add('OrientedParticleFactory') + Label(orientedPage, text = 'Not implemented').pack(expand = 1, + fill = BOTH) + self.factoryNotebook.pack(expand = 1, fill = BOTH) + + ## EMITTER PAGE WIDGETS ## + self.createOptionMenu( + emitterPage, 'Emitter', + 'Emitter Type', + 'Select type of particle emitter', + ('BoxEmitter', 'DiscEmitter', 'LineEmitter', 'PointEmitter', + 'RectangleEmitter', 'RingEmitter', 'SphereVolumeEmitter', + 'SphereSurfaceEmitter', 'TangentRingEmitter'), + self.selectEmitterType) + + # Emitter modes + self.emissionType = IntVar() + self.emissionType.set(BaseParticleEmitter.ETRADIATE) + emissionFrame = Frame(emitterPage) + self.createRadiobutton( + emissionFrame, 'left', + 'Emitter', 'Explicit Emission', + ('particles are all emitted in parallel, direction is based ' + + 'on explicit velocity vector'), + self.emissionType, BaseParticleEmitter.ETEXPLICIT, + self.setEmissionType) + self.createRadiobutton( + emissionFrame, 'left', + 'Emitter', 'Radiate Emission', + 'particles are emitted away from a specific point', + self.emissionType, BaseParticleEmitter.ETRADIATE, + self.setEmissionType) + self.createRadiobutton( + emissionFrame, 'left', + 'Emitter', 'Custom Emission', + ('particles are emitted with a velocity that ' + + 'is determined by the particular emitter'), + self.emissionType, BaseParticleEmitter.ETCUSTOM, + self.setEmissionType) + emissionFrame.pack(fill = 'x', expand = 0) + + self.createFloater( + emitterPage, 'Emitter', 'Velocity Multiplier', + 'launch velocity multiplier (all emission modes)', + command = self.setEmitterAmplitude, + min = None) + + self.createFloater( + emitterPage, 'Emitter', 'Velocity Multiplier Spread', + 'spread for launch velocity multiplier (all emission modes)', + command = self.setEmitterAmplitudeSpread) + + self.createVector3Entry( + emitterPage, 'Emitter', 'Offset Velocity', + 'Velocity vector applied to all particles', + command = self.setEmitterOffsetForce) + + self.createVector3Entry( + emitterPage, 'Emitter', 'Explicit Velocity', + 'all particles launch with this velocity in Explicit mode', + command = self.setEmitterExplicitLaunchVector) + + self.createVector3Entry( + emitterPage, 'Emitter', 'Radiate Origin', + 'particles launch away from this point in Radiate mode', + command = self.setEmitterRadiateOrigin) + + self.emitterNotebook = Pmw.NoteBook(emitterPage, tabpos = None) + # Box page # + boxPage = self.emitterNotebook.add('BoxEmitter') + self.createVector3Entry(boxPage, 'Box Emitter', 'Min', + 'Min point defining emitter box', + command = self.setEmitterBoxPoint1) + self.createVector3Entry(boxPage, 'Box Emitter', 'Max', + 'Max point defining emitter box', + command = self.setEmitterBoxPoint2, + value = (1.0, 1.0, 1.0)) + # Disc page # + discPage = self.emitterNotebook.add('DiscEmitter') + self.createFloater(discPage, 'Disc Emitter', 'Radius', + 'Radius of disc', + command = self.setEmitterDiscRadius, + min = 0.01) + customPage = self.discCustomFrame = Frame(discPage) + self.createAngleDial(customPage, 'Disc Emitter', 'Inner Angle', + 'Particle launch angle at center of disc', + command = self.setEmitterDiscInnerAngle) + self.createFloater(customPage, 'Disc Emitter', 'Inner Velocity', + 'Launch velocity multiplier at center of disc', + command = self.setEmitterDiscInnerVelocity) + self.createAngleDial(customPage, 'Disc Emitter', 'Outer Angle', + 'Particle launch angle at outer edge of disc', + command = self.setEmitterDiscOuterAngle) + self.createFloater(customPage, 'Disc Emitter', 'Outer Velocity', + 'Launch velocity multiplier at edge of disc', + command = self.setEmitterDiscOuterVelocity) + self.createCheckbutton( + customPage, 'Disc Emitter', 'Cubic Lerping', + 'On: magnitude/angle interpolation from center', + self.toggleEmitterDiscCubicLerping, 0) + customPage.pack(fill = BOTH, expand = 1) + + # Line page # + linePage = self.emitterNotebook.add('LineEmitter') + self.createVector3Entry(linePage, 'Line Emitter', 'Min', + 'Min point defining emitter line', + command = self.setEmitterLinePoint1) + self.createVector3Entry(linePage, 'Line Emitter', 'Max', + 'Max point defining emitter line', + command = self.setEmitterLinePoint2, + value = (1.0, 0.0, 0.0)) + # Point page # + emitterPointPage = self.emitterNotebook.add('PointEmitter') + self.createVector3Entry(emitterPointPage, 'Point Emitter', 'Position', + 'Position of emitter point', + command = self.setEmitterPointPosition) + # Rectangle # + rectanglePage = self.emitterNotebook.add('RectangleEmitter') + self.createVector2Entry(rectanglePage, + 'Rectangle Emitter', 'Min', + 'Point defining rectangle', + command = self.setEmitterRectanglePoint1) + self.createVector2Entry(rectanglePage, + 'Rectangle Emitter', 'Max', + 'Point defining rectangle', + command = self.setEmitterRectanglePoint2) + # Ring # + ringPage = self.emitterNotebook.add('RingEmitter') + self.createFloater(ringPage, 'Ring Emitter', 'Radius', + 'Radius of ring', + command = self.setEmitterRingRadius, + min = 0.01) + self.ringCustomFrame = Frame(ringPage) + self.createAngleDial(self.ringCustomFrame, 'Ring Emitter', 'Angle', + 'Particle launch angle', + command = self.setEmitterRingLaunchAngle) + self.ringCustomFrame.pack(fill = BOTH, expand = 1) + + # Sphere volume # + sphereVolumePage = self.emitterNotebook.add('SphereVolumeEmitter') + self.createFloater(sphereVolumePage, 'Sphere Volume Emitter', 'Radius', + 'Radius of sphere', + command = self.setEmitterSphereVolumeRadius, + min = 0.01) + # Sphere surface # + sphereSurfacePage = self.emitterNotebook.add('SphereSurfaceEmitter') + self.createFloater(sphereSurfacePage, 'Sphere Surface Emitter', + 'Radius', + 'Radius of sphere', + command = self.setEmitterSphereSurfaceRadius, + min = 0.01) + # Tangent ring # + tangentRingPage = self.emitterNotebook.add('TangentRingEmitter') + self.createFloater(tangentRingPage, 'Tangent Ring Emitter', 'Radius', + 'Radius of ring', + command = self.setEmitterTangentRingRadius, + min = 0.01) + self.emitterNotebook.pack(fill = X) + + ## RENDERER PAGE WIDGETS ## + self.createOptionMenu( + rendererPage, 'Renderer', 'Renderer Type', + 'Select type of particle renderer', + ('LineParticleRenderer', 'GeomParticleRenderer', + 'PointParticleRenderer', 'SparkleParticleRenderer', + 'SpriteParticleRenderer'), + self.selectRendererType) + + self.createOptionMenu(rendererPage, + 'Renderer', 'Alpha Mode', + "alpha setting over particles' lifetime", + ('NO_ALPHA','ALPHA_OUT','ALPHA_IN','ALPHA_USER'), + self.setRendererAlphaMode) + + self.createSlider( + rendererPage, 'Renderer', 'User Alpha', + 'alpha value for ALPHA_USER alpha mode', + command = self.setRendererUserAlpha) + + self.rendererNotebook = Pmw.NoteBook(rendererPage, tabpos = None) + # Line page # + linePage = self.rendererNotebook.add('LineParticleRenderer') + self.createColorEntry(linePage, 'Line Renderer', 'Head Color', + 'Head color of line', + command = self.setRendererLineHeadColor) + self.createColorEntry(linePage, 'Line Renderer', 'Tail Color', + 'Tail color of line', + command = self.setRendererLineTailColor) + # Geom page # + geomPage = self.rendererNotebook.add('GeomParticleRenderer') + f = Frame(geomPage) + f.pack(fill = X) + Label(f, width = 12, text = 'Geom Node').pack(side = LEFT) + self.rendererGeomNode = StringVar() + self.rendererGeomNodeEntry = Entry( + f, width = 12, + textvariable = self.rendererGeomNode) + self.rendererGeomNodeEntry.bind('', self.setRendererGeomNode) + self.rendererGeomNodeEntry.pack(side = LEFT, expand = 1, fill = X) + # Point # + rendererPointPage = self.rendererNotebook.add('PointParticleRenderer') + self.createFloater(rendererPointPage, 'Point Renderer', 'Point Size', + 'Width and height of points in pixels', + command = self.setRendererPointSize) + self.createColorEntry(rendererPointPage, 'Point Renderer', + 'Start Color', + 'Starting color of point', + command = self.setRendererPointStartColor) + self.createColorEntry(rendererPointPage, 'Point Renderer', + 'End Color', + 'Ending color of point', + command = self.setRendererPointEndColor) + self.createOptionMenu(rendererPointPage, 'Point Renderer', + 'Blend Type', + 'Type of color blending used for particle', + ('PP_ONE_COLOR', 'PP_BLEND_LIFE', + 'PP_BLEND_VEL'), + self.rendererPointSelectBlendType) + self.createOptionMenu(rendererPointPage, 'Point Renderer', + 'Blend Method', + 'Interpolation method between colors', + ('PP_NO_BLEND', 'PP_BLEND_LINEAR', + 'PP_BLEND_CUBIC'), + self.rendererPointSelectBlendMethod) + # Sparkle # + sparklePage = self.rendererNotebook.add('SparkleParticleRenderer') + self.createColorEntry(sparklePage, 'Sparkle Renderer', + 'Center Color', + 'Color of sparkle center', + command = self.setRendererSparkleCenterColor) + self.createColorEntry(sparklePage, 'Sparkle Renderer', + 'Edge Color', + 'Color of sparkle line endpoints', + command = self.setRendererSparkleEdgeColor) + self.createFloater(sparklePage, 'Sparkle Renderer', + 'Birth Radius', + 'Initial sparkle radius', + command = self.setRendererSparkleBirthRadius) + self.createFloater(sparklePage, 'Sparkle Renderer', + 'Death Radius', + 'Final sparkle radius', + command = self.setRendererSparkleDeathRadius) + self.createOptionMenu(sparklePage, + 'Sparkle Renderer', 'Life Scale', + 'Does particle scale over its lifetime?', + ('SP_NO_SCALE', 'SP_SCALE'), + self.setRendererSparkleLifeScale) + # Sprite # + spritePage = self.rendererNotebook.add('SpriteParticleRenderer') + f = Frame(spritePage) + Label(f, width = 12, text = 'Texture Type:').pack(side = LEFT) + self.rendererSpriteSourceType = IntVar() + self.rendererSpriteSourceType.set(SpriteParticleRenderer.STTexture) + self.rendererSpriteSTTexture = self.createRadiobutton( + f, 'left', + 'Sprite Renderer', 'Texture Type', + 'Sprite particle renderer created from texture file', + self.rendererSpriteSourceType, SpriteParticleRenderer.STTexture, + self.setSpriteSourceType) + self.rendererSpriteSTTexture = self.createRadiobutton( + f, 'left', + 'Sprite Renderer', 'NodePath Type', + 'Sprite particle renderer created from node path', + self.rendererSpriteSourceType, SpriteParticleRenderer.STFromNode, + self.setSpriteSourceType) + f.pack(fill = X) + f = Frame(spritePage) + Label(f, width = 6, text = 'Texture:').pack(side = LEFT) + self.rendererSpriteTexture = StringVar() + self.rendererSpriteTexture.set(SpriteParticleRenderer.sourceTextureName) + self.rendererSpriteTextureEntry = Entry( + f, width = 12, + textvariable = self.rendererSpriteTexture) + self.rendererSpriteTextureEntry.pack(side = LEFT, expand = 1, fill = X) + f.pack(fill = X) + f = Frame(spritePage) + Label(f, width = 6, text = 'File:').pack(side = LEFT) + self.rendererSpriteFile = StringVar() + self.rendererSpriteFile.set(SpriteParticleRenderer.sourceFileName) + self.rendererSpriteFileEntry = Entry( + f, width = 12, + textvariable = self.rendererSpriteFile) + self.rendererSpriteFileEntry.pack(side = LEFT, expand = 1, fill = X) + Label(f, width = 6, text = 'Node:').pack(side = LEFT) + self.rendererSpriteNode = StringVar() + self.rendererSpriteNode.set(SpriteParticleRenderer.sourceNodeName) + self.rendererSpriteNodeEntry = Entry( + f, width = 6, + textvariable = self.rendererSpriteNode) + self.rendererSpriteNodeEntry.pack(side = LEFT, expand = 1, fill = X) + f.pack(fill = X) + # Init entries + self.setSpriteSourceType() + self.setTextureButton = Button(spritePage, text = 'Set Texture', + command = self.setRendererSpriteTexture) + self.setTextureButton.pack(fill = X) + f = Frame(spritePage) + self.createCheckbutton( + f, 'Sprite Renderer', 'X Scale', + ("On: x scale is interpolated over particle's life; " + + "Off: stays as start_X_Scale"), + self.toggleRendererSpriteXScale, 0, side = LEFT) + self.createCheckbutton( + f, 'Sprite Renderer', 'Y Scale', + ("On: y scale is interpolated over particle's life; " + + "Off: stays as start_Y_Scale"), + self.toggleRendererSpriteYScale, 0, side = LEFT) + self.createCheckbutton( + f, 'Sprite Renderer', 'Anim Angle', + ("On: particles that are set to spin on the Z axis will " + + "spin appropriately"), + self.toggleRendererSpriteAnimAngle, 0, side = LEFT) + f.pack(fill = X) + self.createFloater(spritePage, 'Sprite Renderer', + 'Initial X Scale', + 'Initial X scaling factor', + command = self.setRendererSpriteInitialXScale) + self.createFloater(spritePage, 'Sprite Renderer', + 'Final X Scale', + 'Final X scaling factor, if xScale enabled', + command = self.setRendererSpriteFinalXScale) + self.createFloater(spritePage, 'Sprite Renderer', + 'Initial Y Scale', + 'Initial Y scaling factor', + command = self.setRendererSpriteInitialYScale) + self.createFloater(spritePage, 'Sprite Renderer', + 'Final Y Scale', + 'Final Y scaling factor, if yScale enabled', + command = self.setRendererSpriteFinalYScale) + self.createAngleDial(spritePage, 'Sprite Renderer', + 'Non Animated Theta', + ('If animAngle is false: counter clockwise ' + + 'Z rotation of all sprites'), + command = self.setRendererSpriteNonAnimatedTheta) + self.createOptionMenu(spritePage, 'Sprite Renderer', + 'Blend Type', + 'Interpolation blend type for X and Y scaling', + ('PP_NO_BLEND', 'PP_LINEAR', 'PP_CUBIC'), + self.setRendererSpriteBlendMethod) + self.createCheckbutton( + spritePage, 'Sprite Renderer', 'Alpha Disable', + 'On: alpha blending is disabled', + self.toggleRendererSpriteAlphaDisable, 0) + self.rendererNotebook.pack(fill = X) + + ## FORCE PAGE WIDGETS ## + self.addForceButton = Menubutton(forcePage, text = 'Add Force', + relief = RAISED, + borderwidth = 2, + font=('MSSansSerif', 14, 'bold'), + activebackground = '#909090') + forceMenu = Menu(self.addForceButton) + self.addForceButton['menu'] = forceMenu + # DERIVED FROM LINEAR FORCE + # This also has: setVector + forceMenu.add_command(label = 'Add Linear Vector Force', + command = self.addLinearVectorForce) + # Parameters: setAmplitude, setMassDependent, setVectorMasks + forceMenu.add_command(label = 'Add Linear Noise Force', + command = self.addLinearNoiseForce) + forceMenu.add_command(label = 'Add Linear Jitter Force', + command = self.addLinearJitterForce) + # This also has setCoef + forceMenu.add_command(label = 'Add Linear Friction Force', + command = self.addLinearFrictionForce) + # This also has: setCoef, setLength, setRadius, + forceMenu.add_command(label = 'Add Linear Cylinder Vortex Force', + command = self.addLinearCylinderVortexForce) + + # DERIVED FROM LINEAR DISTANCE FORCE + # Parameters: setFalloffType, setForceCenter, setRadius + forceMenu.add_command(label = 'Add Linear Sink Force', + command = self.addLinearSinkForce) + forceMenu.add_command(label = 'Add Linear Source Force', + command = self.addLinearSourceForce) + """ + # Avoid for now + forceMenu.add_command(label = 'Add Linear User Defined Force', + command = self.addLinearUserDefinedForce) + """ + + self.addForceButton.pack(expand = 0) + + # Scrolled frame to hold force widgets + self.sf = Pmw.ScrolledFrame(forcePage, horizflex = 'elastic') + self.sf.pack(fill = 'both', expand = 1) + self.forceFrame = self.sf.interior() + # Notebook to hold force widgets as the are added + self.forceGroupNotebook = Pmw.NoteBook(self.forceFrame, tabpos = None) + self.forceGroupNotebook.pack(fill = X) + + self.factoryNotebook.setnaturalsize() + self.emitterNotebook.setnaturalsize() + self.rendererNotebook.setnaturalsize() + self.forceGroupNotebook.setnaturalsize() + self.mainNotebook.setnaturalsize() + + # Make sure input variables processed + self.initialiseoptions(ParticlePanel) + + ### WIDGET UTILITY FUNCTIONS ### + def createCheckbutton(self, parent, category, text, + balloonHelp, command, initialState, side = 'top'): + bool = BooleanVar() + bool.set(initialState) + widget = Checkbutton(parent, text = text, anchor = W, + variable = bool) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X, side = side) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + self.variableDict[category + '-' + text] = bool + return widget + + def createRadiobutton(self, parent, side, category, text, + balloonHelp, variable, value, + command): + widget = Radiobutton(parent, text = text, anchor = W, + variable = variable, value = value) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(side = side, fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createFloaters(self, parent, widgetDefinitions): + widgets = [] + for category, label, balloonHelp, command, min, resolution in widgetDefinitions: + widgets.append( + self.createFloater(parent, category, label, balloonHelp, + command, min, resolution) + ) + return widgets + + def createFloater(self, parent, category, text, balloonHelp, + command = None, min = 0.0, resolution = None, + numDigits = 3, **kw): + kw['text'] = text + kw['min'] = min + kw['resolution'] = resolution + kw['numDigits'] = numDigits + widget = apply(Floater.Floater, (parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createAngleDial(self, parent, category, text, balloonHelp, + command = None, **kw): + kw['text'] = text + kw['style'] = 'mini' + widget = apply(Dial.AngleDial,(parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createSlider(self, parent, category, text, balloonHelp, + command = None, min = 0.0, max = 1.0, + resolution = 0.001, **kw): + kw['text'] = text + kw['min'] = min + kw['max'] = max + kw['resolution'] = resolution + widget = apply(Slider.Slider, (parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createVector2Entry(self, parent, category, text, balloonHelp, + command = None, **kw): + # Set label's text + kw['text'] = text + widget = apply(VectorWidgets.Vector2Entry, (parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createVector3Entry(self, parent, category, text, balloonHelp, + command = None, **kw): + # Set label's text + kw['text'] = text + widget = apply(VectorWidgets.Vector3Entry, (parent,), kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createColorEntry(self, parent, category, text, balloonHelp, + command = None, **kw): + # Set label's text + kw['text'] = text + widget = apply(VectorWidgets.ColorEntry, (parent,) ,kw) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget, balloonHelp) + self.widgetDict[category + '-' + text] = widget + return widget + + def createOptionMenu(self, parent, category, text, balloonHelp, + items, command): + optionVar = StringVar() + if len(items) > 0: + optionVar.set(items[0]) + widget = Pmw.OptionMenu(parent, labelpos = W, label_text = text, + label_width = 12, menu_tearoff = 1, + menubutton_textvariable = optionVar, + items = items) + # Do this after the widget so command isn't called on creation + widget['command'] = command + widget.pack(fill = X) + self.bind(widget.component('menubutton'), balloonHelp) + self.widgetDict[category + '-' + text] = widget + self.variableDict[category + '-' + text] = optionVar + return optionVar + + def createComboBox(self, parent, category, text, balloonHelp, + items, command, history = 0): + widget = Pmw.ComboBox(parent, + labelpos = W, + label_text = text, + label_anchor = 'w', + label_width = 12, + entry_width = 16, + history = history, + scrolledlist_items = items) + # Don't allow user to edit entryfield + widget.configure(entryfield_entry_state = 'disabled') + # Select first item if it exists + if len(items) > 0: + widget.selectitem(items[0]) + # Bind selection command + widget['selectioncommand'] = command + widget.pack(side = 'left', expand = 0) + # Bind help + self.bind(widget, balloonHelp) + # Record widget + self.widgetDict[category + '-' + text] = widget + return widget + + def updateMenusAndLabels(self): + self.updateMenus() + self.updateLabels() + + def updateLabels(self): + self.effectsLabel['text'] = self.particleEffect.getName() + self.particlesLabel['text'] = self.particles.getName() + if self.forceGroup != None: + self.forceGroupLabel['text'] = self.forceGroup.getName() + else: + self.forceGroupLabel['text'] = 'Force Group' + + def updateMenus(self): + self.updateEffectsMenus() + self.updateParticlesMenus() + self.updateForceGroupMenus() + + def updateEffectsMenus(self): + # Get rid of old effects entries if any + self.effectsEnableMenu.delete(0, 'end') + self.effectsLabelMenu.delete(5, 'end') + self.effectsLabelMenu.add_separator() + # Add in a checkbutton for each effect (to toggle on/off) + keys = self.effectsDict.keys() + keys.sort() + for name in keys: + effect = self.effectsDict[name] + self.effectsLabelMenu.add_command( + label = effect.getName(), + command = (lambda s = self, + e = effect: s.selectEffectNamed(e.getName())) + ) + effectActive = IntVar() + effectActive.set(effect.isEnabled()) + self.effectsEnableMenu.add_checkbutton( + label = effect.getName(), + variable = effectActive, + command = (lambda s = self, + e = effect, + v = effectActive: s.toggleEffect(e, v))) + + def updateParticlesMenus(self): + # Get rid of old particles entries if any + self.particlesEnableMenu.delete(0, 'end') + self.particlesLabelMenu.delete(2, 'end') + self.particlesLabelMenu.add_separator() + # Add in a checkbutton for each effect (to toggle on/off) + particles = self.particleEffect.getParticlesList() + names = map(lambda x: x.getName(), particles) + names.sort() + for name in names: + particle = self.particleEffect.getParticlesNamed(name) + self.particlesLabelMenu.add_command( + label = name, + command = (lambda s = self, + n = name: s.selectParticlesNamed(n)) + ) + particleActive = IntVar() + particleActive.set(particle.isEnabled()) + self.particlesEnableMenu.add_checkbutton( + label = name, + variable = particleActive, + command = (lambda s = self, + p = particle, + v = particleActive: s.toggleParticles(p, v))) + + def updateForceGroupMenus(self): + # Get rid of old forceGroup entries if any + self.forceGroupEnableMenu.delete(0, 'end') + self.forceGroupLabelMenu.delete(2, 'end') + self.forceGroupLabelMenu.add_separator() + # Add in a checkbutton for each effect (to toggle on/off) + forceGroupList = self.particleEffect.getForceGroupList() + names = map(lambda x: x.getName(), forceGroupList) + names.sort() + for name in names: + force = self.particleEffect.getForceGroupNamed(name) + self.forceGroupLabelMenu.add_command( + label = name, + command = (lambda s = self, + n = name: s.selectForceGroupNamed(n)) + ) + forceActive = IntVar() + forceActive.set(force.isEnabled()) + self.forceGroupEnableMenu.add_checkbutton( + label = name, + variable = forceActive, + command = (lambda s = self, + f = force, + v = forceActive: s.toggleForceGroup(f, v))) + + def selectEffectNamed(self, name): + effect = self.effectsDict.get(name, None) + if effect != None: + self.particleEffect = effect + # Default to first particle in particlesDict + self.particles = self.particleEffect.getParticlesList()[0] + # See if particle effect has any forceGroup + forceGroupList = self.particleEffect.getForceGroupList() + if len(forceGroupList) > 0: + self.forceGroup = forceGroupList[0] + else: + self.forceGroup = None + self.mainNotebook.selectpage('System') + self.updateInfo('System') + else: + print 'ParticlePanel: No effect named ' + name + + def toggleEffect(self, effect, var): + if var.get(): + effect.enable() + else: + effect.disable() + + def selectParticlesNamed(self, name): + particles = self.particleEffect.getParticlesNamed(name) + if particles != None: + self.particles = particles + self.updateInfo() + + def toggleParticles(self, particles, var): + if var.get(): + particles.enable() + else: + particles.disable() + + def selectForceGroupNamed(self, name): + forceGroup = self.particleEffect.getForceGroupNamed(name) + if forceGroup != None: + self.forceGroup = forceGroup + self.updateInfo('Force') + + def toggleForceGroup(self, forceGroup, var): + if var.get(): + forceGroup.enable() + else: + forceGroup.disable() + + def toggleForce(self, force, pageName, variableName): + v = self.getVariable(pageName, variableName) + if v.get(): + force.setActive(1) + else: + force.setActive(0) + + def getWidget(self, category, text): + return self.widgetDict[category + '-' + text] + + def getVariable(self, category, text): + return self.variableDict[category + '-' + text] + + def loadParticleEffectFromFile(self): + # Find path to particle directory + pPath = getParticlePath() + if pPath.getNumDirectories() > 0: + if `pPath.getDirectory(0)` == '.': + path = '.' + else: + path = pPath.getDirectory(0).toOsSpecific() + else: + path = '.' + if not os.path.isdir(path): + print 'ParticlePanel Warning: Invalid default DNA directory!' + print 'Using current directory' + path = '.' + particleFilename = askopenfilename( + defaultextension = '.ptf', + filetypes = (('Particle Files', '*.ptf'),('All files', '*')), + initialdir = path, + title = 'Load Particle Effect', + parent = self.parent) + if particleFilename: + # Delete existing particles and forces + self.particleEffect.loadConfig( + Filename.fromOsSpecific(particleFilename)) + self.selectEffectNamed(self.particleEffect.getName()) + # Enable effect + self.particleEffect.enable() + messenger.send('SGE_Update Explorer',[render]) + + def saveParticleEffectToFile(self): + # Find path to particle directory + pPath = getParticlePath() + if pPath.getNumDirectories() > 0: + if `pPath.getDirectory(0)` == '.': + path = '.' + else: + path = pPath.getDirectory(0).toOsSpecific() + else: + path = '.' + if not os.path.isdir(path): + print 'ParticlePanel Warning: Invalid default DNA directory!' + print 'Using current directory' + path = '.' + particleFilename = asksaveasfilename( + defaultextension = '.ptf', + filetypes = (('Particle Files', '*.ptf'),('All files', '*')), + initialdir = path, + title = 'Save Particle Effect as', + parent = self.parent) + if particleFilename: + self.particleEffect.saveConfig(Filename(particleFilename)) + + ### PARTICLE EFFECTS COMMANDS ### + def toggleParticleMgr(self): + if self.particleMgrActive.get(): + base.enableParticles() + else: + base.disableParticles() + + ### PARTICLE SYSTEM COMMANDS ### + def updateInfo(self, page = 'System'): + self.updateMenusAndLabels() + if page == 'System': + self.updateSystemWidgets() + elif page == 'Factory': + self.selectFactoryPage() + self.updateFactoryWidgets() + elif page == 'Emitter': + self.selectEmitterPage() + self.updateEmitterWidgets() + elif page == 'Renderer': + self.selectRendererPage() + self.updateRendererWidgets() + elif page == 'Force': + self.updateForceWidgets() + + def toggleParticleEffect(self): + if self.getVariable('Effect', 'Active').get(): + self.particleEffect.enable() + else: + self.particleEffect.disable() + + ## SYSTEM PAGE ## + def updateSystemWidgets(self): + poolSize = self.particles.getPoolSize() + self.getWidget('System', 'Pool Size').set(int(poolSize), 0) + birthRate = self.particles.getBirthRate() + self.getWidget('System', 'Birth Rate').set(birthRate, 0) + litterSize = self.particles.getLitterSize() + self.getWidget('System', 'Litter Size').set(int(litterSize), 0) + litterSpread = self.particles.getLitterSpread() + self.getWidget('System', 'Litter Spread').set(litterSpread, 0) + systemLifespan = self.particles.getSystemLifespan() + self.getWidget('System', 'Lifespan').set(systemLifespan, 0) + pos = self.particles.nodePath.getPos() + self.getWidget('System', 'Pos').set([pos[0], pos[1], pos[2]], 0) + hpr = self.particles.nodePath.getHpr() + self.getWidget('System', 'Hpr').set([hpr[0], hpr[1], hpr[2]], 0) + self.getVariable('System', 'Render Space Velocities').set( + self.particles.getLocalVelocityFlag()) + self.getVariable('System', 'System Grows Older').set( + self.particles.getSystemGrowsOlderFlag()) + def setSystemPoolSize(self, value): + self.particles.setPoolSize(int(value)) + def setSystemBirthRate(self, value): + self.particles.setBirthRate(value) + def setSystemLitterSize(self, value): + self.particles.setLitterSize(int(value)) + def setSystemLitterSpread(self, value): + self.particles.setLitterSpread(int(value)) + def setSystemLifespan(self, value): + self.particles.setSystemLifespan(value) + def toggleSystemLocalVelocity(self): + self.particles.setLocalVelocityFlag( + self.getVariable('System', 'Render Space Velocities').get()) + def toggleSystemGrowsOlder(self): + self.particles.setSystemGrowsOlderFlag( + self.getVariable('System', 'System Grows Older').get()) + def setSystemPos(self, pos): + self.particles.nodePath.setPos(Vec3(pos[0], pos[1], pos[2])) + def setSystemHpr(self, pos): + self.particles.nodePath.setHpr(Vec3(pos[0], pos[1], pos[2])) + + ## FACTORY PAGE ## + def selectFactoryType(self, type): + self.factoryNotebook.selectpage(type) + self.particles.setFactory(type) + self.updateFactoryWidgets() + + def selectFactoryPage(self): + pass + + def updateFactoryWidgets(self): + factory = self.particles.factory + lifespan = factory.getLifespanBase() + self.getWidget('Factory', 'Life Span').set(lifespan, 0) + lifespanSpread = factory.getLifespanSpread() + self.getWidget('Factory', 'Life Span Spread').set(lifespanSpread, 0) + mass = factory.getMassBase() + self.getWidget('Factory', 'Mass').set(mass, 0) + massSpread = factory.getMassSpread() + self.getWidget('Factory', 'Mass Spread').set(massSpread, 0) + terminalVelocity = factory.getTerminalVelocityBase() + self.getWidget('Factory', 'Terminal Velocity').set(terminalVelocity, 0) + terminalVelocitySpread = factory.getTerminalVelocitySpread() + self.getWidget('Factory', 'Terminal Vel. Spread').set( + terminalVelocitySpread, 0) + + def setFactoryLifeSpan(self, value): + self.particles.factory.setLifespanBase(value) + def setFactoryLifeSpanSpread(self, value): + self.particles.factory.setLifespanSpread(value) + def setFactoryParticleMass(self, value): + self.particles.factory.setMassBase(value) + def setFactoryParticleMassSpread(self, value): + self.particles.factory.setMassSpread(value) + def setFactoryTerminalVelocity(self, value): + self.particles.factory.setTerminalVelocityBase(value) + def setFactoryTerminalVelocitySpread(self, value): + self.particles.factory.setTerminalVelocitySpread(value) + # Point Page # + # Z Spin Page # + def setFactoryZSpinInitialAngle(self, angle): + self.particles.factory.setInitialAngle(angle) + def setFactoryZSpinInitialAngleSpread(self, spread): + self.particles.factory.setInitialAngleSpread(spread) + def setFactoryZSpinFinalAngle(self, angle): + self.particles.factory.setFinalAngle(angle) + def setFactoryZSpinFinalAngleSpread(self, spread): + self.particles.factory.setFinalAngleSpread(spread) + def setFactoryZSpinAngularVelocity(self, vel): + self.particles.factory.setAngularVelocity(vel) + def setFactoryZSpinAngularVelocitySpread(self, spread): + self.particles.factory.setAngularVelocitySpread(spread) + + ## EMITTER PAGE ## + def selectEmitterType(self, type): + self.emitterNotebook.selectpage(type) + self.particles.setEmitter(type) + self.updateEmitterWidgets() + + def selectEmitterPage(self): + type = self.particles.emitter.__class__.__name__ + self.emitterNotebook.selectpage(type) + self.getVariable('Emitter', 'Emitter Type').set(type) + + def updateEmitterWidgets(self): + emitter = self.particles.emitter + self.setEmissionType(self.particles.emitter.getEmissionType()) + amp = emitter.getAmplitude() + self.getWidget('Emitter', 'Velocity Multiplier').set(amp) + spread = emitter.getAmplitudeSpread() + self.getWidget('Emitter', 'Velocity Multiplier Spread').set(spread) + vec = emitter.getOffsetForce() + self.getWidget('Emitter', 'Offset Velocity').set( + [vec[0], vec[1], vec[2]], 0) + vec = emitter.getRadiateOrigin() + self.getWidget('Emitter', 'Radiate Origin').set( + [vec[0], vec[1], vec[2]], 0) + vec = emitter.getExplicitLaunchVector() + self.getWidget('Emitter', 'Explicit Velocity').set( + [vec[0], vec[1], vec[2]], 0) + if isinstance(emitter, BoxEmitter): + min = emitter.getMinBound() + self.getWidget('Box Emitter', 'Min').set( + [min[0], min[1], min[2]], 0) + max = emitter.getMaxBound() + self.getWidget('Box Emitter', 'Max').set( + [max[0], max[1], max[2]], 0) + elif isinstance(emitter, DiscEmitter): + radius = emitter.getRadius() + self.getWidget('Disc Emitter', 'Radius').set(radius, 0) + innerAngle = emitter.getInnerAngle() + self.getWidget('Disc Emitter', 'Inner Angle').set(innerAngle, 0) + innerMagnitude = emitter.getInnerMagnitude() + self.getWidget('Disc Emitter', 'Inner Velocity').set( + innerMagnitude, 0) + outerAngle = emitter.getOuterAngle() + self.getWidget('Disc Emitter', 'Outer Angle').set(outerAngle, 0) + outerMagnitude = emitter.getOuterMagnitude() + self.getWidget('Disc Emitter', 'Inner Velocity').set( + outerMagnitude, 0) + cubicLerping = emitter.getCubicLerping() + self.getVariable('Disc Emitter', 'Cubic Lerping').set(cubicLerping) + elif isinstance(emitter, LineEmitter): + min = emitter.getEndpoint1() + self.getWidget('Line Emitter', 'Min').set( + [min[0], min[1], min[2]], 0) + max = emitter.getEndpoint2() + self.getWidget('Line Emitter', 'Max').set( + [max[0], max[1], max[2]], 0) + elif isinstance(emitter, PointEmitter): + location = emitter.getLocation() + self.getWidget('Point Emitter', 'Position').set( + [location[0], location[1], location[2]], 0) + elif isinstance(emitter, RectangleEmitter): + min = emitter.getMinBound() + self.getWidget('Rectangle Emitter', 'Min').set( + [min[0], min[1]], 0) + max = emitter.getMaxBound() + self.getWidget('Rectangle Emitter', 'Max').set( + [max[0], max[1]], 0) + elif isinstance(emitter, RingEmitter): + radius = emitter.getRadius() + self.getWidget('Ring Emitter', 'Radius').set(radius, 0) + angle = emitter.getAngle() + self.getWidget('Ring Emitter', 'Angle').set(angle, 0) + elif isinstance(emitter, SphereVolumeEmitter): + radius = emitter.getRadius() + self.getWidget('Sphere Volume Emitter', 'Radius').set(radius, 0) + elif isinstance(emitter, SphereSurfaceEmitter): + radius = emitter.getRadius() + self.getWidget('Sphere Surface Emitter', 'Radius').set(radius, 0) + elif isinstance(emitter, TangentRingEmitter): + radius = emitter.getRadius() + self.getWidget('Tangent Ring Emitter', 'Radius').set(radius, 0) + # All # + def setEmissionType(self, newType = None): + if newType: + type = newType + self.emissionType.set(type) + else: + type = self.emissionType.get() + self.particles.emitter.setEmissionType(type) + if type == BaseParticleEmitter.ETEXPLICIT: + self.getWidget( + 'Emitter', 'Radiate Origin')['state'] = 'disabled' + self.getWidget( + 'Emitter', 'Explicit Velocity')['state'] = 'normal' + # Hide custom widgets + if isinstance(self.particles.emitter, DiscEmitter): + self.discCustomFrame.pack_forget() + elif isinstance(self.particles.emitter, RingEmitter): + self.ringCustomFrame.pack_forget() + elif type == BaseParticleEmitter.ETRADIATE: + self.getWidget( + 'Emitter', 'Radiate Origin')['state'] = 'normal' + self.getWidget( + 'Emitter', 'Explicit Velocity')['state'] = 'disabled' + # Hide custom widgets + if isinstance(self.particles.emitter, DiscEmitter): + self.discCustomFrame.pack_forget() + elif isinstance(self.particles.emitter, RingEmitter): + self.ringCustomFrame.pack_forget() + elif type == BaseParticleEmitter.ETCUSTOM: + self.getWidget( + 'Emitter', 'Radiate Origin')['state'] = 'disabled' + self.getWidget( + 'Emitter', 'Explicit Velocity')['state'] = 'disabled' + # Show custom widgets + if isinstance(self.particles.emitter, DiscEmitter): + self.discCustomFrame.pack(fill = BOTH, expand = 1) + elif isinstance(self.particles.emitter, RingEmitter): + self.ringCustomFrame.pack(fill = BOTH, expand = 1) + + def setEmitterAmplitude(self, value): + self.particles.emitter.setAmplitude(value) + + def setEmitterAmplitudeSpread(self, value): + self.particles.emitter.setAmplitudeSpread(value) + + def setEmitterOffsetForce(self, vec): + self.particles.emitter.setOffsetForce( + Vec3(vec[0], vec[1], vec[2])) + + def setEmitterRadiateOrigin(self, origin): + self.particles.emitter.setRadiateOrigin( + Point3(origin[0], origin[1], origin[2])) + + def setEmitterExplicitLaunchVector(self, vec): + self.particles.emitter.setExplicitLaunchVector( + Vec3(vec[0], vec[1], vec[2])) + + # Box # + def setEmitterBoxPoint1(self, point): + self.particles.emitter.setMinBound(Point3(point[0], + point[1], + point[2])) + def setEmitterBoxPoint2(self, point): + self.particles.emitter.setMaxBound(Point3(point[0], + point[1], + point[2])) + # Disc # + def setEmitterDiscRadius(self, radius): + self.particles.emitter.setRadius(radius) + def setEmitterDiscInnerAngle(self, angle): + self.particles.emitter.setInnerAngle(angle) + def setEmitterDiscInnerVelocity(self, velocity): + self.particles.emitter.setInnerMagnitude(velocity) + def setEmitterDiscOuterAngle(self, angle): + self.particles.emitter.setOuterAngle(angle) + def setEmitterDiscOuterVelocity(self, velocity): + self.particles.emitter.setOuterMagnitude(velocity) + def toggleEmitterDiscCubicLerping(self): + self.particles.emitter.setCubicLerping( + self.getVariable('Disc Emitter', 'Cubic Lerping').get()) + # Line # + def setEmitterLinePoint1(self, point): + self.particles.emitter.setEndpoint1(Point3(point[0], + point[1], + point[2])) + def setEmitterLinePoint2(self, point): + self.particles.emitter.setEndpoint2(Point3(point[0], + point[1], + point[2])) + # Point # + def setEmitterPointPosition(self, pos): + self.particles.emitter.setLocation(Point3(pos[0], pos[1], pos[2])) + # Rectangle # + def setEmitterRectanglePoint1(self, point): + self.particles.emitter.setMinBound(Point2(point[0], point[1])) + def setEmitterRectanglePoint2(self, point): + self.particles.emitter.setMaxBound(Point2(point[0], point[1])) + # Ring # + def setEmitterRingRadius(self, radius): + self.particles.emitter.setRadius(radius) + def setEmitterRingLaunchAngle(self, angle): + self.particles.emitter.setAngle(angle) + # Sphere surface # + def setEmitterSphereSurfaceRadius(self, radius): + self.particles.emitter.setRadius(radius) + # Sphere volume # + def setEmitterSphereVolumeRadius(self, radius): + self.particles.emitter.setRadius(radius) + # Tangent ring # + def setEmitterTangentRingRadius(self, radius): + self.particles.emitter.setRadius(radius) + + ## RENDERER PAGE ## + def selectRendererType(self, type): + self.rendererNotebook.selectpage(type) + self.particles.setRenderer(type) + self.updateRendererWidgets() + + def updateRendererWidgets(self): + renderer = self.particles.renderer + alphaMode = renderer.getAlphaMode() + if alphaMode == BaseParticleRenderer.PRALPHANONE: + aMode = 'NO_ALPHA' + elif alphaMode == BaseParticleRenderer.PRALPHAOUT: + aMode = 'ALPHA_OUT' + elif alphaMode == BaseParticleRenderer.PRALPHAIN: + aMode = 'ALPHA_IN' + elif alphaMode == BaseParticleRenderer.PRALPHAUSER: + aMode = 'ALPHA_USER' + self.getVariable('Renderer', 'Alpha Mode').set(aMode) + userAlpha = renderer.getUserAlpha() + self.getWidget('Renderer', 'User Alpha').set(userAlpha) + if isinstance(renderer, LineParticleRenderer): + headColor = renderer.getHeadColor() * 255.0 + self.getWidget('Line Renderer', 'Head Color').set( + [headColor[0], headColor[1], headColor[2], headColor[3]]) + tailColor = renderer.getTailColor() * 255.0 + self.getWidget('Line Renderer', 'Tail Color').set( + [tailColor[0], tailColor[1], tailColor[2], tailColor[3]]) + elif isinstance(renderer, GeomParticleRenderer): + pass + elif isinstance(renderer, PointParticleRenderer): + pointSize = renderer.getPointSize() + self.getWidget('Point Renderer', 'Point Size').set(pointSize) + startColor = renderer.getStartColor() * 255.0 + self.getWidget('Point Renderer', 'Start Color').set( + [startColor[0], startColor[1], startColor[2], startColor[3]]) + endColor = renderer.getEndColor() * 255.0 + self.getWidget('Point Renderer', 'End Color').set( + [endColor[0], endColor[1], endColor[2], endColor[3]]) + blendType = renderer.getBlendType() + if (blendType == PointParticleRenderer.PPONECOLOR): + bType = "PP_ONE_COLOR" + elif (blendType == PointParticleRenderer.PPBLENDLIFE): + bType = "PP_BLEND_LIFE" + elif (blendType == PointParticleRenderer.PPBLENDVEL): + bType = "PP_BLEND_VEL" + self.getVariable('Point Renderer', 'Blend Type').set(bType) + blendMethod = renderer.getBlendMethod() + bMethod = "PP_NO_BLEND" + if (blendMethod == BaseParticleRenderer.PPNOBLEND): + bMethod = "PP_NO_BLEND" + elif (blendMethod == BaseParticleRenderer.PPBLENDLINEAR): + bMethod = "PP_BLEND_LINEAR" + elif (blendMethod == BaseParticleRenderer.PPBLENDCUBIC): + bMethod = "PP_BLEND_CUBIC" + self.getVariable('Point Renderer', 'Blend Method').set(bMethod) + elif isinstance(renderer, SparkleParticleRenderer): + centerColor = renderer.getCenterColor() * 255.0 + self.getWidget('Sparkle Renderer', 'Center Color').set( + [centerColor[0], centerColor[1], + centerColor[2], centerColor[3]]) + edgeColor = renderer.getEdgeColor() * 255.0 + self.getWidget('Sparkle Renderer', 'Edge Color').set( + [edgeColor[0], edgeColor[1], edgeColor[2], edgeColor[3]]) + birthRadius = renderer.getBirthRadius() + self.getWidget('Sparkle Renderer', 'Birth Radius').set(birthRadius) + deathRadius = renderer.getDeathRadius() + self.getWidget('Sparkle Renderer', 'Death Radius').set(deathRadius) + lifeScale = renderer.getLifeScale() + lScale = "SP_NO_SCALE" + if (lifeScale == SparkleParticleRenderer.SPSCALE): + lScale = "SP_SCALE" + self.getVariable('Sparkle Renderer', 'Life Scale').set(lScale) + elif isinstance(renderer, SpriteParticleRenderer): + color = renderer.getColor() * 255.0 + # Update widgets to reflect current default values + # Texture + textureName = renderer.getSourceTextureName() + if textureName != None: + self.rendererSpriteTexture.set(textureName) + # File + fileName = renderer.getSourceFileName() + if fileName != None: + self.rendererSpriteFile.set(fileName) + # Node + nodeName = renderer.getSourceNodeName() + if nodeName != None: + self.rendererSpriteNode.set(nodeName) + self.getVariable('Sprite Renderer', 'X Scale').set( + renderer.getXScaleFlag()) + self.getVariable('Sprite Renderer', 'Y Scale').set( + renderer.getYScaleFlag()) + self.getVariable('Sprite Renderer', 'Anim Angle').set( + renderer.getAnimAngleFlag()) + initialXScale = renderer.getInitialXScale() + self.getWidget('Sprite Renderer', 'Initial X Scale').set( + initialXScale) + initialYScale = renderer.getInitialYScale() + self.getWidget('Sprite Renderer', 'Initial Y Scale').set( + initialYScale) + finalXScale = renderer.getFinalXScale() + self.getWidget('Sprite Renderer', 'Final X Scale').set( + finalXScale) + finalYScale = renderer.getFinalYScale() + self.getWidget('Sprite Renderer', 'Final Y Scale').set( + finalYScale) + nonanimatedTheta = renderer.getNonanimatedTheta() + self.getWidget('Sprite Renderer', 'Non Animated Theta').set( + nonanimatedTheta) + blendMethod = renderer.getAlphaBlendMethod() + bMethod = "PP_NO_BLEND" + if (blendMethod == BaseParticleRenderer.PPNOBLEND): + bMethod = "PP_NO_BLEND" + elif (blendMethod == BaseParticleRenderer.PPBLENDLINEAR): + bMethod = "PP_BLEND_LINEAR" + elif (blendMethod == BaseParticleRenderer.PPBLENDCUBIC): + bMethod = "PP_BLEND_CUBIC" + self.getVariable('Sprite Renderer', 'Alpha Disable').set( + renderer.getAlphaDisable()) + + def selectRendererPage(self): + type = self.particles.renderer.__class__.__name__ + self.rendererNotebook.selectpage(type) + self.getVariable('Renderer', 'Renderer Type').set(type) + + # All # + def setRendererAlphaMode(self, alphaMode): + if alphaMode == 'NO_ALPHA': + aMode = BaseParticleRenderer.PRALPHANONE + elif alphaMode == 'ALPHA_OUT': + aMode = BaseParticleRenderer.PRALPHAOUT + elif alphaMode == 'ALPHA_IN': + aMode = BaseParticleRenderer.PRALPHAIN + elif alphaMode == 'ALPHA_USER': + aMode = BaseParticleRenderer.PRALPHAUSER + self.particles.renderer.setAlphaMode(aMode) + + def setRendererUserAlpha(self, alpha): + self.particles.renderer.setUserAlpha(alpha) + + # Line # + def setRendererLineHeadColor(self, color): + self.particles.renderer.setHeadColor( + Vec4(color[0]/255.0, color[1]/255.0, + color[2]/255.0, color[3]/255.0)) + def setRendererLineTailColor(self, color): + self.particles.renderer.setTailColor( + Vec4(color[0]/255.0, color[1]/255.0, + color[2]/255.0, color[3]/255.0)) + # Geom # + def setRendererGeomNode(self, event): + node = None + nodePath = loader.loadModel(self.rendererGeomNode.get()) + if nodePath != None: + node = nodePath.node() + if (node != None): + self.particles.renderer.setGeomNode(node) + # Point # + def setRendererPointSize(self, size): + self.particles.renderer.setPointSize(size) + def setRendererPointStartColor(self, color): + self.particles.renderer.setStartColor( + Vec4(color[0]/255.0, color[1]/255.0, + color[2]/255.0, color[3]/255.0)) + def setRendererPointEndColor(self, color): + self.particles.renderer.setEndColor( + Vec4(color[0]/255.0, color[1]/255.0, + color[2]/255.0, color[3]/255.0)) + def rendererPointSelectBlendType(self, blendType): + if blendType == "PP_ONE_COLOR": + bType = PointParticleRenderer.PPONECOLOR + elif blendType == "PP_BLEND_LIFE": + bType = PointParticleRenderer.PPBLENDLIFE + elif blendType == "PP_BLEND_VEL": + bType = PointParticleRenderer.PPBLENDVEL + self.particles.renderer.setBlendType(bType) + def rendererPointSelectBlendMethod(self, blendMethod): + if blendMethod == "PP_NO_BLEND": + bMethod = BaseParticleRenderer.PPNOBLEND + elif blendMethod == "PP_BLEND_LINEAR": + bMethod = BaseParticleRenderer.PPBLENDLINEAR + elif blendMethod == "PP_BLEND_CUBIC": + bMethod = BaseParticleRenderer.PPBLENDCUBIC + self.particles.renderer.setBlendMethod(bMethod) + # Sparkle # + def setRendererSparkleCenterColor(self, color): + self.particles.renderer.setCenterColor( + Vec4(color[0]/255.0, color[1]/255.0, + color[2]/255.0, color[3]/255.0)) + def setRendererSparkleEdgeColor(self, color): + self.particles.renderer.setEdgeColor( + Vec4(color[0]/255.0, color[1]/255.0, + color[2]/255.0, color[3]/255.0)) + def setRendererSparkleBirthRadius(self, radius): + self.particles.renderer.setBirthRadius(radius) + def setRendererSparkleDeathRadius(self, radius): + self.particles.renderer.setDeathRadius(radius) + def setRendererSparkleLifeScale(self, lifeScaleMethod): + if lifeScaleMethod == 'SP_NO_SCALE': + lScale = SparkleParticleRenderer.SPNOSCALE + else: + lScale = SparkleParticleRenderer.SPSCALE + self.particles.renderer.setLifeScale(lScale) + # Sprite # + def setSpriteSourceType(self): + if self.rendererSpriteSourceType.get() == SpriteParticleRenderer.STTexture: + self.rendererSpriteTextureEntry['state'] = 'normal' + self.rendererSpriteFileEntry['state'] = 'disabled' + self.rendererSpriteNodeEntry['state'] = 'disabled' + self.rendererSpriteTextureEntry['background'] = 'SystemWindow' + self.rendererSpriteFileEntry['background'] = '#C0C0C0' + self.rendererSpriteNodeEntry['background'] = '#C0C0C0' + else: + self.rendererSpriteTextureEntry['state'] = 'disabled' + self.rendererSpriteFileEntry['state'] = 'normal' + self.rendererSpriteNodeEntry['state'] = 'normal' + self.rendererSpriteTextureEntry['background'] = '#C0C0C0' + self.rendererSpriteFileEntry['background'] = 'SystemWindow' + self.rendererSpriteNodeEntry['background'] = 'SystemWindow' + def setRendererSpriteTexture(self): + if self.rendererSpriteSourceType.get() == SpriteParticleRenderer.STTexture: + self.particles.renderer.setTextureFromFile(self.rendererSpriteTexture.get()) + else: + self.particles.renderer.setTextureFromNode( + self.rendererSpriteFile.get(), self.rendererSpriteNode.get()) + def toggleRendererSpriteXScale(self): + self.particles.renderer.setXScaleFlag( + self.getVariable('Sprite Renderer', 'X Scale').get()) + def toggleRendererSpriteYScale(self): + self.particles.renderer.setYScaleFlag( + self.getVariable('Sprite Renderer', 'Y Scale').get()) + def toggleRendererSpriteAnimAngle(self): + self.particles.renderer.setAnimAngleFlag( + self.getVariable('Sprite Renderer', 'Anim Angle').get()) + + def toggleAngularVelocity(self): + self.particles.factory.enableAngularVelocity( + self.getVariable('Z Spin Factory', 'Enable Angular Velocity').get()) + + def setRendererSpriteInitialXScale(self, xScale): + self.particles.renderer.setInitialXScale(xScale) + def setRendererSpriteFinalXScale(self, xScale): + self.particles.renderer.setFinalXScale(xScale) + def setRendererSpriteInitialYScale(self, yScale): + self.particles.renderer.setInitialYScale(yScale) + def setRendererSpriteFinalYScale(self, yScale): + self.particles.renderer.setFinalYScale(yScale) + def setRendererSpriteNonAnimatedTheta(self, theta): + self.particles.renderer.setNonanimatedTheta(theta) + def setRendererSpriteBlendMethod(self, blendMethod): + print blendMethod + if blendMethod == 'PP_NO_BLEND': + bMethod = BaseParticleRenderer.PPNOBLEND + elif blendMethod == 'PP_BLEND_LINEAR': + bMethod = BaseParticleRenderer.PPBLENDLINEAR + elif blendMethod == 'PP_BLEND_CUBIC': + bMethod = BaseParticleRenderer.PPBLENDCUBIC + else: + bMethod = BaseParticleRenderer.PPNOBLEND + self.particles.renderer.setAlphaBlendMethod(bMethod) + def toggleRendererSpriteAlphaDisable(self): + self.particles.renderer.setAlphaDisable( + self.getVariable('Sprite Renderer', 'Alpha Disable').get()) + + ## FORCEGROUP COMMANDS ## + def updateForceWidgets(self): + # Select appropriate notebook page + if self.forceGroup != None: + self.forceGroupNotebook.pack(fill = X) + self.forcePageName = (self.particleEffect.getName() + '-' + + self.forceGroup.getName()) + self.forcePage = self.forcePagesDict.get( + self.forcePageName, None) + # Page doesn't exist, add it + if self.forcePage == None: + self.addForceGroupNotebookPage( + self.particleEffect, self.forceGroup) + self.forceGroupNotebook.selectpage(self.forcePageName) + else: + self.forceGroupNotebook.pack_forget() + + def addLinearVectorForce(self): + self.addForce(LinearVectorForce()) + def addLinearFrictionForce(self): + self.addForce(LinearFrictionForce()) + def addLinearJitterForce(self): + self.addForce(LinearJitterForce()) + def addLinearNoiseForce(self): + self.addForce(LinearNoiseForce()) + def addLinearSinkForce(self): + self.addForce(LinearSinkForce()) + def addLinearSourceForce(self): + self.addForce(LinearSourceForce()) + def addLinearCylinderVortexForce(self): + self.addForce(LinearCylinderVortexForce()) + def addLinearUserDefinedForce(self): + self.addForce(LinearUserDefinedForce()) + + def addForce(self, f): + if self.forceGroup == None: + self.createNewForceGroup() + self.forceGroup.addForce(f) + self.addForceWidget(self.forceGroup,f) + + ## SYSTEM COMMANDS ## + def createNewEffect(self): + name = askstring('Particle Panel', 'Effect Name:', + parent = self.parent) + if name: + particles = seParticles.Particles() + particles.setBirthRate(0.02) + particles.setLitterSize(10) + particles.setLitterSpread(0) + particles.setFactory("PointParticleFactory") + particles.setRenderer("PointParticleRenderer") + particles.setEmitter("SphereVolumeEmitter") + particles.enable() + effect = seParticleEffect.ParticleEffect(name,particles) + self.effectsDict[name] = effect + self.updateMenusAndLabels() + self.selectEffectNamed(name) + self.emitter=loader.loadModel("sphere") + self.emitter.setName(name) + effect.reparentTo(self.emitter) + self.emitter.reparentTo(render) + effect.enable() + messenger.send('ParticlePanel_Added_Effect',[name,effect,self.emitter]) + messenger.send('SGE_Update Explorer',[render]) + + def createNewParticles(self): + name = askstring('Particle Panel', 'Particles Name:', + parent = self.parent) + if name: + p = seParticles.Particles(name) + p.setBirthRate(0.02) + p.setLitterSize(10) + p.setLitterSpread(0) + p.setFactory("PointParticleFactory") + p.setRenderer("PointParticleRenderer") + p.setEmitter("SphereVolumeEmitter") + self.particleEffect.addParticles(p) + self.updateParticlesMenus() + self.selectParticlesNamed(name) + p.enable() + + def createNewForceGroup(self): + name = askstring('Particle Panel', 'ForceGroup Name:', + parent = self.parent) + if name: + forceGroup = seForceGroup.ForceGroup(name) + self.particleEffect.addForceGroup(forceGroup) + self.updateForceGroupMenus() + self.addForceGroupNotebookPage(self.particleEffect, forceGroup) + self.selectForceGroupNamed(name) + forceGroup.enable() + + def addForceGroupNotebookPage(self, particleEffect, forceGroup): + self.forcePageName = (particleEffect.getName() + '-' + + forceGroup.getName()) + self.forcePage = self.forceGroupNotebook.add(self.forcePageName) + self.forcePagesDict[self.forcePageName] = self.forcePage + for force in forceGroup: + self.addForceWidget(forceGroup, force) + + def addForceWidget(self, forceGroup, force): + forcePage = self.forcePage + pageName = self.forcePageName + # How many forces of the same type in the force group object + count = 0 + for f in forceGroup: + if f.getClassType().eq(force.getClassType()): + count += 1 + if isinstance(force, LinearVectorForce): + self.createLinearVectorForceWidget( + forcePage, pageName, count, force) + elif isinstance(force, LinearNoiseForce): + self.createLinearRandomForceWidget( + forcePage, pageName, count, force, 'Noise') + elif isinstance(force, LinearJitterForce): + self.createLinearRandomForceWidget( + forcePage, pageName, count, force, 'Jitter') + elif isinstance(force, LinearFrictionForce): + self.createLinearFrictionForceWidget( + forcePage, pageName, count, force) + elif isinstance(force, LinearCylinderVortexForce): + self.createLinearCylinderVortexForceWidget( + forcePage, pageName, count, force) + elif isinstance(force, LinearSinkForce): + self.createLinearDistanceForceWidget( + forcePage, pageName, count, force, 'Sink') + elif isinstance(force, LinearSourceForce): + self.createLinearDistanceForceWidget( + forcePage, pageName, count, force, 'Source') + elif isinstance(force, LinearUserDefinedForce): + # Nothing + pass + self.forceGroupNotebook.setnaturalsize() + + def createForceFrame(self, forcePage, forceName, force): + frame = Frame(forcePage, relief = RAISED, borderwidth = 2) + lFrame = Frame(frame, relief = FLAT) + def removeForce(s = self, f = force, fr = frame): + s.forceGroup.removeForce(f) + fr.pack_forget() + b = Button(lFrame, text = 'X', + command = removeForce) + b.pack(side = 'right', expand = 0) + Label(lFrame, text = forceName, + foreground = 'Blue', + font=('MSSansSerif', 12, 'bold'), + ).pack(expand = 1, fill = 'x') + lFrame.pack(fill = 'x', expand =1) + frame.pack(pady = 3, fill = 'x', expand =0) + return frame + + def createLinearForceWidgets(self, frame, pageName, forceName, force): + def setAmplitude(amp, f = force): + f.setAmplitude(amp) + def toggleMassDependent(s=self, f=force, p=pageName, n=forceName): + v = s.getVariable(p, n+' Mass Dependent') + f.setMassDependent(v.get()) + def setVectorMasks(s=self, f=force, p=pageName, n=forceName): + xMask = s.getVariable(p, n+' Mask X').get() + yMask = s.getVariable(p, n+' Mask Y').get() + zMask = s.getVariable(p, n+' Mask Z').get() + f.setVectorMasks(xMask, yMask, zMask) + self.createFloater(frame, pageName, forceName + ' Amplitude', + 'Force amplitude multiplier', + command = setAmplitude, + value = force.getAmplitude()) + cbf = Frame(frame, relief = FLAT) + self.createCheckbutton(cbf, pageName, forceName + ' Mass Dependent', + ('On: force depends on mass; ' + + 'Off: force does not depend on mass'), + toggleMassDependent, + force.getMassDependent()) + self.createCheckbutton(cbf, pageName, forceName + ' Mask X', + 'On: enable force along X axis', + setVectorMasks, 1) + self.createCheckbutton(cbf, pageName, forceName + ' Mask Y', + 'On: enable force along X axis', + setVectorMasks, 1) + self.createCheckbutton(cbf, pageName, forceName + ' Mask Z', + 'On: enable force along X axis', + setVectorMasks, 1) + cbf.pack(fill = 'x', expand = 0) + + def createForceActiveWidget(self, frame, pageName, forceName, force): + cbName = forceName + ' Active' + def toggle(s = self, f = force, p = pageName, n = cbName): + s.toggleForce(f, p, n) + self.createCheckbutton(frame, pageName, cbName, + 'On: force is enabled; Off: force is disabled', + toggle, 1) + + def createLinearVectorForceWidget(self, forcePage, pageName, + count, force): + def setVec(vec, f = force): + f.setVector(vec[0], vec[1], vec[2]) + forceName = 'Vector Force-' + `count` + frame = self.createForceFrame(forcePage, forceName, force) + self.createLinearForceWidgets(frame, pageName, forceName, force) + vec = force.getLocalVector() + self.createVector3Entry(frame, pageName, forceName, + 'Set force direction and magnitude', + command = setVec, + value = [vec[0], vec[1], vec[2]]) + self.createForceActiveWidget(frame, pageName, forceName, force) + + def createLinearRandomForceWidget(self, forcePage, pageName, count, + force, type): + forceName = type + ' Force-' + `count` + frame = self.createForceFrame(forcePage, forceName, force) + self.createLinearForceWidgets(frame, pageName, forceName, force) + self.createForceActiveWidget(frame, pageName, forceName, force) + + def createLinearFrictionForceWidget(self, forcePage, pageName, + count, force): + def setCoef(coef, f = force): + f.setCoef(coef) + forceName = 'Friction Force-' + `count` + frame = self.createForceFrame(forcePage, forceName, force) + self.createLinearForceWidgets(frame, pageName, forceName, force) + self.createFloater(frame, pageName, forceName + ' Coef', + 'Set linear friction force', + command = setCoef, min = None, + value = force.getCoef()) + self.createForceActiveWidget(frame, pageName, forceName, force) + + def createLinearCylinderVortexForceWidget(self, forcePage, pageName, + count, force): + forceName = 'Vortex Force-' + `count` + def setCoef(coef, f = force): + f.setCoef(coef) + def setLength(length, f = force): + f.setLength(length) + def setRadius(radius, f = force): + f.setRadius(radius) + frame = self.createForceFrame(forcePage, forceName, force) + self.createLinearForceWidgets(frame, pageName, forceName, force) + self.createFloater(frame, pageName, forceName + ' Coef', + 'Set linear cylinder vortex coefficient', + command = setCoef, + value = force.getCoef()) + self.createFloater(frame, pageName, forceName + ' Length', + 'Set linear cylinder vortex length', + command = setLength, + value = force.getLength()) + self.createFloater(frame, pageName, forceName + ' Radius', + 'Set linear cylinder vortex radius', + command = setRadius, + value = force.getRadius()) + self.createForceActiveWidget(frame, pageName, forceName, force) + + def createLinearDistanceForceWidget(self, forcePage, pageName, + count, force, type): + def setFalloffType(type, f=force): + if type == 'FT_ONE_OVER_R': + #f.setFalloffType(LinearDistanceForce.FTONEOVERR) + f.setFalloffType(0) + if type == 'FT_ONE_OVER_R_SQUARED': + #f.setFalloffType(LinearDistanceForce.FTONEOVERRSQUARED) + f.setFalloffType(1) + if type == 'FT_ONE_OVER_R_CUBED': + #f.setFalloffType(LinearDistanceForce.FTONEOVERRCUBED) + f.setFalloffType(2) + def setForceCenter(vec, f = force): + f.setForceCenter(Point3(vec[0], vec[1], vec[2])) + def setRadius(radius, f = force): + f.setRadius(radius) + forceName = type + ' Force-' + `count` + frame = self.createForceFrame(forcePage, forceName, force) + self.createLinearForceWidgets(frame, pageName, forceName, force) + var = self.createOptionMenu( + frame, pageName, forceName + ' Falloff', + 'Set force falloff type', + ('FT_ONE_OVER_R', + 'FT_ONE_OVER_R_SQUARED', + 'FT_ONE_OVER_R_CUBED'), + command = setFalloffType) + self.getWidget(pageName, forceName + ' Falloff').configure( + label_width = 16) + falloff = force.getFalloffType() + if falloff == LinearDistanceForce.FTONEOVERR: + var.set('FT_ONE_OVER_R') + elif falloff == LinearDistanceForce.FTONEOVERRSQUARED: + var.set('FT_ONE_OVER_R_SQUARED') + elif falloff == LinearDistanceForce.FTONEOVERRCUBED: + var.set('FT_ONE_OVER_R_CUBED') + vec = force.getForceCenter() + self.createVector3Entry(frame, pageName, forceName + ' Center', + 'Set center of force', + command = setForceCenter, + label_width = 16, + value = [vec[0], vec[1], vec[2]]) + self.createFloater(frame, pageName, forceName + ' Radius', + 'Set falloff radius', + command = setRadius, + min = 0.01, + value = force.getRadius()) + self.createForceActiveWidget(frame, pageName, forceName, force) + +###################################################################### + +# Create demo in root window for testing. +if __name__ == '__main__': + root = Pmw.initialise() + pp = ParticlePanel() + #ve = VectorEntry(Toplevel(), relief = GROOVE) + #ve.pack() diff --git a/doc/SceneEditor/seParticles.py b/doc/SceneEditor/seParticles.py new file mode 100644 index 0000000000..eddeea13a3 --- /dev/null +++ b/doc/SceneEditor/seParticles.py @@ -0,0 +1,449 @@ +from PandaModules import * +from ParticleManagerGlobal import * +from PhysicsManagerGlobal import * + +import ParticleSystem +import BaseParticleFactory +import PointParticleFactory +import ZSpinParticleFactory +#import OrientedParticleFactory +import BaseParticleRenderer +import PointParticleRenderer +import LineParticleRenderer +import GeomParticleRenderer +import SparkleParticleRenderer +import SpriteParticleRenderer +import BaseParticleEmitter +import BoxEmitter +import DiscEmitter +import LineEmitter +import PointEmitter +import RectangleEmitter +import RingEmitter +import SphereSurfaceEmitter +import SphereVolumeEmitter +import TangentRingEmitter +import string +import os +import DirectNotifyGlobal +import sys + +class Particles(ParticleSystem.ParticleSystem): + + notify = DirectNotifyGlobal.directNotify.newCategory('Particles') + id = 1 + + def __init__(self, name=None, poolSize=1024): + """__init__(name, poolSize)""" + + if (name == None): + self.name = 'particles-%d' % Particles.id + Particles.id += 1 + else: + self.name = name + ParticleSystem.ParticleSystem.__init__(self, poolSize) + # self.setBirthRate(0.02) + # self.setLitterSize(10) + # self.setLitterSpread(0) + + # Set up a physical node + self.node = PhysicalNode(self.name) + self.nodePath = NodePath(self.node) + self.setRenderParent(self.node) + self.node.addPhysical(self) + + self.factory = None + self.factoryType = "undefined" + # self.setFactory("PointParticleFactory") + self.renderer = None + self.rendererType = "undefined" + # self.setRenderer("PointParticleRenderer") + self.emitter = None + self.emitterType = "undefined" + # self.setEmitter("SphereVolumeEmitter") + + # Enable particles by default + self.fEnabled = 0 + #self.enable() + + def cleanup(self): + self.disable() + self.clearLinearForces() + self.clearAngularForces() + self.setRenderParent(self.node) + self.node.removePhysical(self) + self.nodePath.removeNode() + del self.node + del self.nodePath + del self.factory + del self.renderer + del self.emitter + + def enable(self): + """enable()""" + if (self.fEnabled == 0): + physicsMgr.attachPhysical(self) + particleMgr.attachParticlesystem(self) + self.fEnabled = 1 + + def disable(self): + """disable()""" + if (self.fEnabled == 1): + physicsMgr.removePhysical(self) + particleMgr.removeParticlesystem(self) + self.fEnabled = 0 + + def isEnabled(self): + return self.fEnabled + + def getNode(self): + return self.node + + def setFactory(self, type): + """setFactory(type)""" + if (self.factoryType == type): + return None + if (self.factory): + self.factory = None + self.factoryType = type + if (type == "PointParticleFactory"): + self.factory = PointParticleFactory.PointParticleFactory() + elif (type == "ZSpinParticleFactory"): + self.factory = ZSpinParticleFactory.ZSpinParticleFactory() + elif (type == "OrientedParticleFactory"): + self.factory = OrientedParticleFactory.OrientedParticleFactory() + else: + print "unknown factory type: %s" % type + return None + self.factory.setLifespanBase(0.5) + ParticleSystem.ParticleSystem.setFactory(self, self.factory) + + def setRenderer(self, type): + """setRenderer(type)""" + if (self.rendererType == type): + return None + if (self.renderer): + self.renderer = None + self.rendererType = type + if (type == "PointParticleRenderer"): + self.renderer = PointParticleRenderer.PointParticleRenderer() + self.renderer.setPointSize(1.0) + elif (type == "LineParticleRenderer"): + self.renderer = LineParticleRenderer.LineParticleRenderer() + elif (type == "GeomParticleRenderer"): + self.renderer = GeomParticleRenderer.GeomParticleRenderer() + npath = NodePath('default-geom') + # This was moved here because we do not want to download + # the direct tools with toontown. + import DirectSelection + bbox = DirectSelection.DirectBoundingBox(npath) + self.renderer.setGeomNode(bbox.lines.node()) + elif (type == "SparkleParticleRenderer"): + self.renderer = SparkleParticleRenderer.SparkleParticleRenderer() + elif (type == "SpriteParticleRenderer"): + self.renderer = SpriteParticleRenderer.SpriteParticleRenderer() + if (self.renderer.getSourceType() == + SpriteParticleRenderer.SpriteParticleRenderer.STTexture): + # Use current default texture + # See sourceTextureName SpriteParticleRenderer-extensions.py + self.renderer.setTextureFromFile() + else: + # Use current default model file and node + # See sourceFileName and sourceNodeName in SpriteParticleRenderer-extensions.py + self.renderer.setTextureFromNode() + else: + print "unknown renderer type: %s" % type + return None + ParticleSystem.ParticleSystem.setRenderer(self, self.renderer) + + def setEmitter(self, type): + """setEmitter(type)""" + if (self.emitterType == type): + return None + if (self.emitter): + self.emitter = None + self.emitterType = type + if (type == "BoxEmitter"): + self.emitter = BoxEmitter.BoxEmitter() + elif (type == "DiscEmitter"): + self.emitter = DiscEmitter.DiscEmitter() + elif (type == "LineEmitter"): + self.emitter = LineEmitter.LineEmitter() + elif (type == "PointEmitter"): + self.emitter = PointEmitter.PointEmitter() + elif (type == "RectangleEmitter"): + self.emitter = RectangleEmitter.RectangleEmitter() + elif (type == "RingEmitter"): + self.emitter = RingEmitter.RingEmitter() + elif (type == "SphereSurfaceEmitter"): + self.emitter = SphereSurfaceEmitter.SphereSurfaceEmitter() + elif (type == "SphereVolumeEmitter"): + self.emitter = SphereVolumeEmitter.SphereVolumeEmitter() + self.emitter.setRadius(1.0) + elif (type == "TangentRingEmitter"): + self.emitter = TangentRingEmitter.TangentRingEmitter() + else: + print "unknown emitter type: %s" % type + return None + ParticleSystem.ParticleSystem.setEmitter(self, self.emitter) + + def addForce(self, force): + """addForce(force)""" + if (force.isLinear()): + self.addLinearForce(force) + else: + self.addAngularForce(force) + + def removeForce(self, force): + """removeForce(force)""" + if (force == None): + self.notify.warning('removeForce() - force == None!') + return + if (force.isLinear()): + self.removeLinearForce(force) + else: + self.removeAngularForce(force) + + def setRenderNodePath(self, nodePath): + self.setRenderParent(nodePath.node()) + + ## Getters ## + def getName(self): + """getName()""" + return self.name + def getFactory(self): + """getFactory()""" + return self.factory + def getEmitter(self): + """getEmitter()""" + return self.emitter + def getRenderer(self): + """getRenderer()""" + return self.renderer + + def printParams(self, file = sys.stdout, targ = 'self'): + """printParams(file, targ)""" + i1=" " + i2=i1+i1 + file.write(i2+'# Particles parameters\n') + file.write(i2+targ + '.setFactory(\"' + self.factoryType + '\")\n') + file.write(i2+targ + '.setRenderer(\"' + self.rendererType + '\")\n') + file.write(i2+targ + '.setEmitter(\"' + self.emitterType + '\")\n') + + # System parameters + file.write(i2+targ + ('.setPoolSize(%d)\n' % + int(self.getPoolSize()))) + file.write(i2+targ + ('.setBirthRate(%.4f)\n' % + self.getBirthRate())) + file.write(i2+targ + ('.setLitterSize(%d)\n' % + int(self.getLitterSize()))) + file.write(i2+targ + ('.setLitterSpread(%d)\n' % + self.getLitterSpread())) + file.write(i2+targ + ('.setSystemLifespan(%.4f)\n' % + self.getSystemLifespan())) + file.write(i2+targ + ('.setLocalVelocityFlag(%d)\n' % + self.getLocalVelocityFlag())) + file.write(i2+targ + ('.setSystemGrowsOlderFlag(%d)\n' % + self.getSystemGrowsOlderFlag())) + file.write(i2+'# Factory parameters\n') + file.write(i2+targ + ('.factory.setLifespanBase(%.4f)\n' % + self.factory.getLifespanBase())) + file.write(i2+targ + '.factory.setLifespanSpread(%.4f)\n' % \ + self.factory.getLifespanSpread()) + file.write(i2+targ + '.factory.setMassBase(%.4f)\n' % \ + self.factory.getMassBase()) + file.write(i2+targ + '.factory.setMassSpread(%.4f)\n' % \ + self.factory.getMassSpread()) + file.write(i2+targ + '.factory.setTerminalVelocityBase(%.4f)\n' % \ + self.factory.getTerminalVelocityBase()) + file.write(i2+targ + '.factory.setTerminalVelocitySpread(%.4f)\n' % \ + self.factory.getTerminalVelocitySpread()) + if (self.factoryType == "PointParticleFactory"): + file.write(i2+'# Point factory parameters\n') + elif (self.factoryType == "ZSpinParticleFactory"): + file.write(i2+'# Z Spin factory parameters\n') + file.write(i2+targ + '.factory.setInitialAngle(%.4f)\n' % \ + self.factory.getInitialAngle()) + file.write(i2+targ + '.factory.setInitialAngleSpread(%.4f)\n' % \ + self.factory.getInitialAngleSpread()) + file.write(i2+targ + '.factory.enableAngularVelocity(%d)\n' % \ + self.factory.getAngularVelocityEnabled()) + if(self.factory.getAngularVelocityEnabled()): + file.write(i2+targ + '.factory.setAngularVelocity(%.4f)\n' % \ + self.factory.getAngularVelocity()) + file.write(i2+targ + '.factory.setAngularVelocitySpread(%.4f)\n' % \ + self.factory.getAngularVelocitySpread()) + else: + file.write(i2+targ + '.factory.setFinalAngle(%.4f)\n' % \ + self.factory.getFinalAngle()) + file.write(i2+targ + '.factory.setFinalAngleSpread(%.4f)\n' % \ + self.factory.getFinalAngleSpread()) + + elif (self.factoryType == "OrientedParticleFactory"): + file.write(i2+'# Oriented factory parameters\n') + file.write(i2+targ + '.factory.setInitialOrientation(%.4f)\n' % \ + self.factory.getInitialOrientation()) + file.write(i2+targ + '.factory.setFinalOrientation(%.4f)\n' % \ + self.factory.getFinalOrientation()) + + file.write(i2+'# Renderer parameters\n') + alphaMode = self.renderer.getAlphaMode() + aMode = "PRALPHANONE" + if (alphaMode == BaseParticleRenderer.BaseParticleRenderer.PRALPHANONE): + aMode = "PRALPHANONE" + elif (alphaMode == + BaseParticleRenderer.BaseParticleRenderer.PRALPHAOUT): + aMode = "PRALPHAOUT" + elif (alphaMode == + BaseParticleRenderer.BaseParticleRenderer.PRALPHAIN): + aMode = "PRALPHAIN" + elif (alphaMode == + BaseParticleRenderer.BaseParticleRenderer.PRALPHAUSER): + aMode = "PRALPHAUSER" + file.write(i2+targ + '.renderer.setAlphaMode(BaseParticleRenderer.' + aMode + ')\n') + file.write(i2+targ + '.renderer.setUserAlpha(%.2f)\n' % \ + self.renderer.getUserAlpha()) + if (self.rendererType == "PointParticleRenderer"): + file.write(i2+'# Point parameters\n') + file.write(i2+targ + '.renderer.setPointSize(%.2f)\n' % \ + self.renderer.getPointSize()) + sColor = self.renderer.getStartColor() + file.write(i2+(targ + '.renderer.setStartColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3]))) + sColor = self.renderer.getEndColor() + file.write(i2+(targ + '.renderer.setEndColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3]))) + blendType = self.renderer.getBlendType() + bType = "PPONECOLOR" + if (blendType == PointParticleRenderer.PointParticleRenderer.PPONECOLOR): + bType = "PPONECOLOR" + elif (blendType == PointParticleRenderer.PointParticleRenderer.PPBLENDLIFE): + bType = "PPBLENDLIFE" + elif (blendType == PointParticleRenderer.PointParticleRenderer.PPBLENDVEL): + bType = "PPBLENDVEL" + file.write(i2+targ + '.renderer.setBlendType(PointParticleRenderer.' + bType + ')\n') + blendMethod = self.renderer.getBlendMethod() + bMethod = "PPNOBLEND" + if (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPNOBLEND): + bMethod = "PPNOBLEND" + elif (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPBLENDLINEAR): + bMethod = "PPBLENDLINEAR" + elif (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPBLENDCUBIC): + bMethod = "PPBLENDCUBIC" + file.write(i2+targ + '.renderer.setBlendMethod(BaseParticleRenderer.' + bMethod + ')\n') + elif (self.rendererType == "LineParticleRenderer"): + file.write(i2+'# Line parameters\n') + sColor = self.renderer.getHeadColor() + file.write(i2+(targ + '.renderer.setHeadColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3]))) + sColor = self.renderer.getTailColor() + file.write(i2+(targ + '.renderer.setTailColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3]))) + elif (self.rendererType == "GeomParticleRenderer"): + file.write(i2+'# Geom parameters\n') + node = self.renderer.getGeomNode() + file.write(i2+targ + '.renderer.setGeomNode(' + node.getName() + ')\n') + elif (self.rendererType == "SparkleParticleRenderer"): + file.write(i2+'# Sparkle parameters\n') + sColor = self.renderer.getCenterColor() + file.write(i2+(targ + '.renderer.setCenterColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3]))) + sColor = self.renderer.getEdgeColor() + file.write(i2+(targ + '.renderer.setEdgeColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3]))) + file.write(i2+targ + '.renderer.setBirthRadius(%.4f)\n' % self.renderer.getBirthRadius()) + file.write(i2+targ + '.renderer.setDeathRadius(%.4f)\n' % self.renderer.getDeathRadius()) + lifeScale = self.renderer.getLifeScale() + lScale = "SPNOSCALE" + if (lifeScale == SparkleParticleRenderer.SparkleParticleRenderer.SPSCALE): + lScale = "SPSCALE" + file.write(i2+targ + '.renderer.setLifeScale(SparkleParticleRenderer.' + lScale + ')\n') + elif (self.rendererType == "SpriteParticleRenderer"): + file.write(i2+'# Sprite parameters\n') + if (self.renderer.getSourceType() == + SpriteParticleRenderer.SpriteParticleRenderer.STTexture): + tex = self.renderer.getTexture() + file.write(i2+targ + '.renderer.setTexture(loader.loadTexture(\'' + tex.getFilename().getFullpath() + '\'))\n') + else: + modelName = self.renderer.getSourceFileName() + nodeName = self.renderer.getSourceNodeName() + file.write(i2+targ + '.renderer.setTextureFromNode("%s", "%s")\n' % (modelName, nodeName)) + sColor = self.renderer.getColor() + file.write(i2+(targ + '.renderer.setColor(Vec4(%.2f, %.2f, %.2f, %.2f))\n' % (sColor[0], sColor[1], sColor[2], sColor[3]))) + file.write(i2+targ + '.renderer.setXScaleFlag(%d)\n' % self.renderer.getXScaleFlag()) + file.write(i2+targ + '.renderer.setYScaleFlag(%d)\n' % self.renderer.getYScaleFlag()) + file.write(i2+targ + '.renderer.setAnimAngleFlag(%d)\n' % self.renderer.getAnimAngleFlag()) + file.write(i2+targ + '.renderer.setInitialXScale(%.4f)\n' % self.renderer.getInitialXScale()) + file.write(i2+targ + '.renderer.setFinalXScale(%.4f)\n' % self.renderer.getFinalXScale()) + file.write(i2+targ + '.renderer.setInitialYScale(%.4f)\n' % self.renderer.getInitialYScale()) + file.write(i2+targ + '.renderer.setFinalYScale(%.4f)\n' % self.renderer.getFinalYScale()) + file.write(i2+targ + '.renderer.setNonanimatedTheta(%.4f)\n' % self.renderer.getNonanimatedTheta()) + blendMethod = self.renderer.getAlphaBlendMethod() + bMethod = "PPNOBLEND" + if (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPNOBLEND): + bMethod = "PPNOBLEND" + elif (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPBLENDLINEAR): + bMethod = "PPBLENDLINEAR" + elif (blendMethod == BaseParticleRenderer.BaseParticleRenderer.PPBLENDCUBIC): + bMethod = "PPBLENDCUBIC" + file.write(i2+targ + '.renderer.setAlphaBlendMethod(BaseParticleRenderer.' + bMethod + ')\n') + file.write(i2+targ + '.renderer.setAlphaDisable(%d)\n' % self.renderer.getAlphaDisable()) + + file.write(i2+'# Emitter parameters\n') + emissionType = self.emitter.getEmissionType() + eType = "ETEXPLICIT" + if (emissionType == BaseParticleEmitter.BaseParticleEmitter.ETEXPLICIT): + eType = "ETEXPLICIT" + elif (emissionType == BaseParticleEmitter.BaseParticleEmitter.ETRADIATE): + eType = "ETRADIATE" + elif (emissionType == BaseParticleEmitter.BaseParticleEmitter.ETCUSTOM): + eType = "ETCUSTOM" + file.write(i2+targ + '.emitter.setEmissionType(BaseParticleEmitter.' + eType + ')\n') + file.write(i2+targ + '.emitter.setAmplitude(%.4f)\n' % self.emitter.getAmplitude()) + file.write(i2+targ + '.emitter.setAmplitudeSpread(%.4f)\n' % self.emitter.getAmplitudeSpread()) + oForce = self.emitter.getOffsetForce() + file.write(i2+(targ + '.emitter.setOffsetForce(Vec3(%.4f, %.4f, %.4f))\n' % (oForce[0], oForce[1], oForce[2]))) + oForce = self.emitter.getExplicitLaunchVector() + file.write(i2+(targ + '.emitter.setExplicitLaunchVector(Vec3(%.4f, %.4f, %.4f))\n' % (oForce[0], oForce[1], oForce[2]))) + orig = self.emitter.getRadiateOrigin() + file.write(i2+(targ + '.emitter.setRadiateOrigin(Point3(%.4f, %.4f, %.4f))\n' % (orig[0], orig[1], orig[2]))) + if (self.emitterType == "BoxEmitter"): + file.write(i2+'# Box parameters\n') + bound = self.emitter.getMinBound() + file.write(i2+(targ + '.emitter.setMinBound(Point3(%.4f, %.4f, %.4f))\n' % (bound[0], bound[1], bound[2]))) + bound = self.emitter.getMaxBound() + file.write(i2+(targ + '.emitter.setMaxBound(Point3(%.4f, %.4f, %.4f))\n' % (bound[0], bound[1], bound[2]))) + elif (self.emitterType == "DiscEmitter"): + file.write(i2+'# Disc parameters\n') + file.write(i2+targ + '.emitter.setRadius(%.4f)\n' % self.emitter.getRadius()) + if (eType == "ETCUSTOM"): + file.write(i2+targ + '.emitter.setOuterAngle(%.4f)\n' % self.emitter.getOuterAngle()) + file.write(i2+targ + '.emitter.setInnerAngle(%.4f)\n' % self.emitter.getInnerAngle()) + file.write(i2+targ + '.emitter.setOuterMagnitude(%.4f)\n' % self.emitter.getOuterMagnitude()) + file.write(i2+targ + '.emitter.setInnerMagnitude(%.4f)\n' % self.emitter.getInnerMagnitude()) + file.write(i2+targ + '.emitter.setCubicLerping(%d)\n' % self.emitter.getCubicLerping()) + + elif (self.emitterType == "LineEmitter"): + file.write(i2+'# Line parameters\n') + point = self.emitter.getEndpoint1() + file.write(i2+(targ + '.emitter.setEndpoint1(Point3(%.4f, %.4f, %.4f))\n' % (point[0], point[1], point[2]))) + point = self.emitter.getEndpoint2() + file.write(i2+(targ + '.emitter.setEndpoint2(Point3(%.4f, %.4f, %.4f))\n' % (point[0], point[1], point[2]))) + elif (self.emitterType == "PointEmitter"): + file.write(i2+'# Point parameters\n') + point = self.emitter.getLocation() + file.write(i2+(targ + '.emitter.setLocation(Point3(%.4f, %.4f, %.4f))\n' % (point[0], point[1], point[2]))) + elif (self.emitterType == "RectangleEmitter"): + file.write(i2+'# Rectangle parameters\n') + point = self.emitter.getMinBound() + file.write(i2+(targ + '.emitter.setMinBound(Point2(%.4f, %.4f))\n' % (point[0], point[1]))) + point = self.emitter.getMaxBound() + file.write(i2+(targ + '.emitter.setMaxBound(Point2(%.4f, %.4f))\n' % (point[0], point[1]))) + elif (self.emitterType == "RingEmitter"): + file.write(i2+'# Ring parameters\n') + file.write(i2+targ + '.emitter.setRadius(%.4f)\n' % self.emitter.getRadius()) + if (eType == "ETCUSTOM"): + file.write(i2+targ + '.emitter.setAngle(%.4f)\n' % self.emitter.getAngle()) + elif (self.emitterType == "SphereSurfaceEmitter"): + file.write(i2+'# Sphere Surface parameters\n') + file.write(i2+targ + '.emitter.setRadius(%.4f)\n' % self.emitter.getRadius()) + elif (self.emitterType == "SphereVolumeEmitter"): + file.write(i2+'# Sphere Volume parameters\n') + file.write(i2+targ + '.emitter.setRadius(%.4f)\n' % self.emitter.getRadius()) + elif (self.emitterType == "TangentRingEmitter"): + file.write(i2+'# Tangent Ring parameters\n') + file.write(i2+targ + '.emitter.setRadius(%.4f)\n' % self.emitter.getRadius()) diff --git a/doc/SceneEditor/sePlacer.py b/doc/SceneEditor/sePlacer.py new file mode 100644 index 0000000000..aafaca80c8 --- /dev/null +++ b/doc/SceneEditor/sePlacer.py @@ -0,0 +1,799 @@ +""" DIRECT Nine DoF Manipulation Panel """ + +# Import Tkinter, Pmw, and the dial code from this directory tree. +from PandaObject import * +from DirectGlobals import * +from TkGlobal import * +from AppShell import * +import Dial +import Floater +""" +TODO: +Task to monitor pose +""" + +class Placer(AppShell): + # Override class variables here + appname = 'Placer Panel' + frameWidth = 625 + frameHeight = 215 + usecommandarea = 0 + usestatusarea = 0 + + def __init__(self, parent = None, **kw): + INITOPT = Pmw.INITOPT + optiondefs = ( + ('title', self.appname, None), + ('nodePath', SEditor.camera, None), + ) + self.defineoptions(kw, optiondefs) + + # Call superclass initialization function + AppShell.__init__(self) + + self.initialiseoptions(Placer) + + # Accept the message from sceneEditor to update the information about the target nodePath + self.accept('placerUpdate', self.updatePlacer) + + def appInit(self): + # Initialize state + self.tempCS = SEditor.group.attachNewNode('placerTempCS') + self.orbitFromCS = SEditor.group.attachNewNode( + 'placerOrbitFromCS') + self.orbitToCS = SEditor.group.attachNewNode('placerOrbitToCS') + self.refCS = self.tempCS + + # Dictionary keeping track of all node paths manipulated so far + self.nodePathDict = {} + self.nodePathDict['camera'] = SEditor.camera + self.nodePathDict['widget'] = SEditor.widget + self.nodePathNames = ['camera', 'widget', 'selected'] + + self.refNodePathDict = {} + self.refNodePathDict['parent'] = self['nodePath'].getParent() + self.refNodePathDict['render'] = render + self.refNodePathDict['camera'] = SEditor.camera + self.refNodePathDict['widget'] = SEditor.widget + self.refNodePathNames = ['parent', 'self', 'render', + 'camera', 'widget', 'selected'] + + # Initial state + self.initPos = Vec3(0) + self.initHpr = Vec3(0) + self.initScale = Vec3(1) + self.deltaHpr = Vec3(0) + + # Offset for orbital mode + self.posOffset = Vec3(0) + + # Set up event hooks + self.undoEvents = [('DIRECT_undo', self.undoHook), + ('DIRECT_pushUndo', self.pushUndoHook), + ('DIRECT_undoListEmpty', self.undoListEmptyHook), + ('DIRECT_redo', self.redoHook), + ('DIRECT_pushRedo', self.pushRedoHook), + ('DIRECT_redoListEmpty', self.redoListEmptyHook)] + for event, method in self.undoEvents: + self.accept(event, method) + + # Init movement mode + self.movementMode = 'Relative To:' + + def createInterface(self): + # The interior of the toplevel panel + interior = self.interior() + interior['relief'] = FLAT + # Add placer commands to menubar + self.menuBar.addmenu('Placer', 'Placer Panel Operations') + self.menuBar.addmenuitem('Placer', 'command', + 'Zero Node Path', + label = 'Zero All', + command = self.zeroAll) + self.menuBar.addmenuitem('Placer', 'command', + 'Reset Node Path', + label = 'Reset All', + command = self.resetAll) + self.menuBar.addmenuitem('Placer', 'command', + 'Print Node Path Info', + label = 'Print Info', + command = self.printNodePathInfo) + self.menuBar.addmenuitem( + 'Placer', 'command', + 'Toggle widget visability', + label = 'Toggle Widget Vis', + command = SEditor.toggleWidgetVis) + self.menuBar.addmenuitem( + 'Placer', 'command', + 'Toggle widget manipulation mode', + label = 'Toggle Widget Mode', + command = SEditor.manipulationControl.toggleObjectHandlesMode) + + # Get a handle to the menu frame + menuFrame = self.menuFrame + self.nodePathMenu = Pmw.ComboBox( + menuFrame, labelpos = W, label_text = 'Node Path:', + entry_width = 20, + selectioncommand = self.selectNodePathNamed, + scrolledlist_items = self.nodePathNames) + self.nodePathMenu.selectitem('selected') + self.nodePathMenuEntry = ( + self.nodePathMenu.component('entryfield_entry')) + self.nodePathMenuBG = ( + self.nodePathMenuEntry.configure('background')[3]) + self.nodePathMenu.pack(side = 'left', fill = 'x', expand = 1) + self.bind(self.nodePathMenu, 'Select node path to manipulate') + + modeMenu = Pmw.OptionMenu(menuFrame, + items = ('Relative To:', + 'Orbit:'), + initialitem = 'Relative To:', + command = self.setMovementMode, + menubutton_width = 8) + modeMenu.pack(side = 'left', expand = 0) + self.bind(modeMenu, 'Select manipulation mode') + + self.refNodePathMenu = Pmw.ComboBox( + menuFrame, entry_width = 16, + selectioncommand = self.selectRefNodePathNamed, + scrolledlist_items = self.refNodePathNames) + self.refNodePathMenu.selectitem('parent') + self.refNodePathMenuEntry = ( + self.refNodePathMenu.component('entryfield_entry')) + self.refNodePathMenu.pack(side = 'left', fill = 'x', expand = 1) + self.bind(self.refNodePathMenu, 'Select relative node path') + + self.undoButton = Button(menuFrame, text = 'Undo', + command = SEditor.undo) + if SEditor.undoList: + self.undoButton['state'] = 'normal' + else: + self.undoButton['state'] = 'disabled' + self.undoButton.pack(side = 'left', expand = 0) + self.bind(self.undoButton, 'Undo last operation') + + self.redoButton = Button(menuFrame, text = 'Redo', + command = SEditor.redo) + if SEditor.redoList: + self.redoButton['state'] = 'normal' + else: + self.redoButton['state'] = 'disabled' + self.redoButton.pack(side = 'left', expand = 0) + self.bind(self.redoButton, 'Redo last operation') + + # Create and pack the Pos Controls + posGroup = Pmw.Group(interior, + tag_pyclass = Menubutton, + tag_text = 'Position', + tag_font=('MSSansSerif', 14), + tag_activebackground = '#909090', + ring_relief = RIDGE) + posMenubutton = posGroup.component('tag') + self.bind(posMenubutton, 'Position menu operations') + posMenu = Menu(posMenubutton, tearoff = 0) + posMenu.add_command(label = 'Set to zero', command = self.zeroPos) + posMenu.add_command(label = 'Reset initial', + command = self.resetPos) + posMenubutton['menu'] = posMenu + posGroup.pack(side='left', fill = 'both', expand = 1) + posInterior = posGroup.interior() + + # Create the dials + self.posX = self.createcomponent('posX', (), None, + Floater.Floater, (posInterior,), + text = 'X', relief = FLAT, + value = 0.0, + label_foreground = 'Red') + self.posX['commandData'] = ['x'] + self.posX['preCallback'] = self.xformStart + self.posX['postCallback'] = self.xformStop + self.posX['callbackData'] = ['x'] + self.posX.pack(expand=1,fill='both') + + self.posY = self.createcomponent('posY', (), None, + Floater.Floater, (posInterior,), + text = 'Y', relief = FLAT, + value = 0.0, + label_foreground = '#00A000') + self.posY['commandData'] = ['y'] + self.posY['preCallback'] = self.xformStart + self.posY['postCallback'] = self.xformStop + self.posY['callbackData'] = ['y'] + self.posY.pack(expand=1,fill='both') + + self.posZ = self.createcomponent('posZ', (), None, + Floater.Floater, (posInterior,), + text = 'Z', relief = FLAT, + value = 0.0, + label_foreground = 'Blue') + self.posZ['commandData'] = ['z'] + self.posZ['preCallback'] = self.xformStart + self.posZ['postCallback'] = self.xformStop + self.posZ['callbackData'] = ['z'] + self.posZ.pack(expand=1,fill='both') + + # Create and pack the Hpr Controls + hprGroup = Pmw.Group(interior, + tag_pyclass = Menubutton, + tag_text = 'Orientation', + tag_font=('MSSansSerif', 14), + tag_activebackground = '#909090', + ring_relief = RIDGE) + hprMenubutton = hprGroup.component('tag') + self.bind(hprMenubutton, 'Orientation menu operations') + hprMenu = Menu(hprMenubutton, tearoff = 0) + hprMenu.add_command(label = 'Set to zero', command = self.zeroHpr) + hprMenu.add_command(label = 'Reset initial', command = self.resetHpr) + hprMenubutton['menu'] = hprMenu + hprGroup.pack(side='left',fill = 'both', expand = 1) + hprInterior = hprGroup.interior() + + # Create the dials + self.hprH = self.createcomponent('hprH', (), None, + Dial.AngleDial, (hprInterior,), + style = 'mini', + text = 'H', value = 0.0, + relief = FLAT, + label_foreground = 'blue') + self.hprH['commandData'] = ['h'] + self.hprH['preCallback'] = self.xformStart + self.hprH['postCallback'] = self.xformStop + self.hprH['callbackData'] = ['h'] + self.hprH.pack(expand=1,fill='both') + + self.hprP = self.createcomponent('hprP', (), None, + Dial.AngleDial, (hprInterior,), + style = 'mini', + text = 'P', value = 0.0, + relief = FLAT, + label_foreground = 'red') + self.hprP['commandData'] = ['p'] + self.hprP['preCallback'] = self.xformStart + self.hprP['postCallback'] = self.xformStop + self.hprP['callbackData'] = ['p'] + self.hprP.pack(expand=1,fill='both') + + self.hprR = self.createcomponent('hprR', (), None, + Dial.AngleDial, (hprInterior,), + style = 'mini', + text = 'R', value = 0.0, + relief = FLAT, + label_foreground = '#00A000') + self.hprR['commandData'] = ['r'] + self.hprR['preCallback'] = self.xformStart + self.hprR['postCallback'] = self.xformStop + self.hprR['callbackData'] = ['r'] + self.hprR.pack(expand=1,fill='both') + + # Create and pack the Scale Controls + # The available scaling modes + self.scalingMode = StringVar() + self.scalingMode.set('Scale Uniform') + # The scaling widgets + scaleGroup = Pmw.Group(interior, + tag_text = 'Scale Uniform', + tag_pyclass = Menubutton, + tag_font=('MSSansSerif', 14), + tag_activebackground = '#909090', + ring_relief = RIDGE) + self.scaleMenubutton = scaleGroup.component('tag') + self.bind(self.scaleMenubutton, 'Scale menu operations') + self.scaleMenubutton['textvariable'] = self.scalingMode + + # Scaling menu + scaleMenu = Menu(self.scaleMenubutton, tearoff = 0) + scaleMenu.add_command(label = 'Set to unity', + command = self.unitScale) + scaleMenu.add_command(label = 'Reset initial', + command = self.resetScale) + scaleMenu.add_radiobutton(label = 'Scale Free', + variable = self.scalingMode) + scaleMenu.add_radiobutton(label = 'Scale Uniform', + variable = self.scalingMode) + scaleMenu.add_radiobutton(label = 'Scale Proportional', + variable = self.scalingMode) + self.scaleMenubutton['menu'] = scaleMenu + # Pack group widgets + scaleGroup.pack(side='left',fill = 'both', expand = 1) + scaleInterior = scaleGroup.interior() + + # Create the dials + self.scaleX = self.createcomponent('scaleX', (), None, + Floater.Floater, (scaleInterior,), + text = 'X Scale', + relief = FLAT, + min = 0.0001, value = 1.0, + resetValue = 1.0, + label_foreground = 'Red') + self.scaleX['commandData'] = ['sx'] + self.scaleX['callbackData'] = ['sx'] + self.scaleX['preCallback'] = self.xformStart + self.scaleX['postCallback'] = self.xformStop + self.scaleX.pack(expand=1,fill='both') + + self.scaleY = self.createcomponent('scaleY', (), None, + Floater.Floater, (scaleInterior,), + text = 'Y Scale', + relief = FLAT, + min = 0.0001, value = 1.0, + resetValue = 1.0, + label_foreground = '#00A000') + self.scaleY['commandData'] = ['sy'] + self.scaleY['callbackData'] = ['sy'] + self.scaleY['preCallback'] = self.xformStart + self.scaleY['postCallback'] = self.xformStop + self.scaleY.pack(expand=1,fill='both') + + self.scaleZ = self.createcomponent('scaleZ', (), None, + Floater.Floater, (scaleInterior,), + text = 'Z Scale', + relief = FLAT, + min = 0.0001, value = 1.0, + resetValue = 1.0, + label_foreground = 'Blue') + self.scaleZ['commandData'] = ['sz'] + self.scaleZ['callbackData'] = ['sz'] + self.scaleZ['preCallback'] = self.xformStart + self.scaleZ['postCallback'] = self.xformStop + self.scaleZ.pack(expand=1,fill='both') + + # Make sure appropriate labels are showing + self.setMovementMode('Relative To:') + # Set up placer for inital node path + self.selectNodePathNamed('init') + self.selectRefNodePathNamed('parent') + # Update place to reflect initial state + self.updatePlacer() + # Now that you're done setting up, attach commands + self.posX['command'] = self.xform + self.posY['command'] = self.xform + self.posZ['command'] = self.xform + self.hprH['command'] = self.xform + self.hprP['command'] = self.xform + self.hprR['command'] = self.xform + self.scaleX['command'] = self.xform + self.scaleY['command'] = self.xform + self.scaleZ['command'] = self.xform + + + ### WIDGET OPERATIONS ### + def setMovementMode(self, movementMode): + # Set prefix + namePrefix = '' + self.movementMode = movementMode + if (movementMode == 'Relative To:'): + namePrefix = 'Relative ' + elif (movementMode == 'Orbit:'): + namePrefix = 'Orbit ' + # Update pos widgets + self.posX['text'] = namePrefix + 'X' + self.posY['text'] = namePrefix + 'Y' + self.posZ['text'] = namePrefix + 'Z' + # Update hpr widgets + if (movementMode == 'Orbit:'): + namePrefix = 'Orbit delta ' + self.hprH['text'] = namePrefix + 'H' + self.hprP['text'] = namePrefix + 'P' + self.hprR['text'] = namePrefix + 'R' + # Update temp cs and initialize widgets + self.updatePlacer() + + def setScalingMode(self): + if self['nodePath']: + scale = self['nodePath'].getScale() + if ((scale[0] != scale[1]) or + (scale[0] != scale[2]) or + (scale[1] != scale[2])): + self.scalingMode.set('Scale Free') + + def selectNodePathNamed(self, name): + nodePath = None + if name == 'init': + nodePath = self['nodePath'] + # Add Combo box entry for the initial node path + self.addNodePath(nodePath) + elif name == 'selected': + nodePath = SEditor.selected.last + # Add Combo box entry for this selected object + self.addNodePath(nodePath) + else: + nodePath = self.nodePathDict.get(name, None) + if (nodePath == None): + # See if this evaluates into a node path + try: + nodePath = eval(name) + if isinstance(nodePath, NodePath): + self.addNodePath(nodePath) + else: + # Good eval but not a node path, give up + nodePath = None + except: + # Bogus eval + nodePath = None + # Clear bogus entry from listbox + listbox = self.nodePathMenu.component('scrolledlist') + listbox.setlist(self.nodePathNames) + else: + if name == 'widget': + # Record relationship between selected nodes and widget + SEditor.selected.getWrtAll() + # Update active node path + self.setActiveNodePath(nodePath) + + def setActiveNodePath(self, nodePath): + self['nodePath'] = nodePath + if self['nodePath']: + self.nodePathMenuEntry.configure( + background = self.nodePathMenuBG) + # Check to see if node path and ref node path are the same + if ((self.refCS != None) and + (self.refCS.id() == self['nodePath'].id())): + # Yes they are, use temp CS as ref + # This calls updatePlacer + self.setReferenceNodePath(self.tempCS) + # update listbox accordingly + self.refNodePathMenu.selectitem('parent') + else: + # Record initial value and initialize the widgets + self.updatePlacer() + # Record initial position + self.updateResetValues(self['nodePath']) + # Set scaling mode based on node path's current scale + self.setScalingMode() + else: + # Flash entry + self.nodePathMenuEntry.configure(background = 'Pink') + + def selectRefNodePathNamed(self, name): + nodePath = None + if name == 'self': + nodePath = self.tempCS + elif name == 'selected': + nodePath = SEditor.selected.last + # Add Combo box entry for this selected object + self.addRefNodePath(nodePath) + elif name == 'parent': + nodePath = self['nodePath'].getParent() + else: + nodePath = self.refNodePathDict.get(name, None) + if (nodePath == None): + # See if this evaluates into a node path + try: + nodePath = eval(name) + if isinstance(nodePath, NodePath): + self.addRefNodePath(nodePath) + else: + # Good eval but not a node path, give up + nodePath = None + except: + # Bogus eval + nodePath = None + # Clear bogus entry from listbox + listbox = self.refNodePathMenu.component('scrolledlist') + listbox.setlist(self.refNodePathNames) + # Check to see if node path and ref node path are the same + if (nodePath != None) and (nodePath.id() == self['nodePath'].id()): + # Yes they are, use temp CS and update listbox accordingly + nodePath = self.tempCS + self.refNodePathMenu.selectitem('parent') + # Update ref node path + self.setReferenceNodePath(nodePath) + + def setReferenceNodePath(self, nodePath): + self.refCS = nodePath + if self.refCS: + self.refNodePathMenuEntry.configure( + background = self.nodePathMenuBG) + # Update placer to reflect new state + self.updatePlacer() + else: + # Flash entry + self.refNodePathMenuEntry.configure(background = 'Pink') + + def addNodePath(self, nodePath): + self.addNodePathToDict(nodePath, self.nodePathNames, + self.nodePathMenu, self.nodePathDict) + + def addRefNodePath(self, nodePath): + self.addNodePathToDict(nodePath, self.refNodePathNames, + self.refNodePathMenu, self.refNodePathDict) + + def addNodePathToDict(self, nodePath, names, menu, dict): + if not nodePath: + return + # Get node path's name + name = nodePath.getName() + if name in ['parent', 'render', 'camera']: + dictName = name + else: + # Generate a unique name for the dict + dictName = name + '-' + `nodePath.id()` + if not dict.has_key(dictName): + # Update combo box to include new item + names.append(dictName) + listbox = menu.component('scrolledlist') + listbox.setlist(names) + # Add new item to dictionary + dict[dictName] = nodePath + menu.selectitem(dictName) + + def updatePlacer(self): + pos = Vec3(0) + hpr = Vec3(0) + scale = Vec3(1) + np = self['nodePath'] + if (np != None) and isinstance(np, NodePath): + # Update temp CS + self.updateAuxiliaryCoordinateSystems() + # Update widgets + if self.movementMode == 'Orbit:': + pos.assign(self.posOffset) + hpr.assign(ZERO_VEC) + scale.assign(np.getScale()) + elif self.refCS: + pos.assign(np.getPos(self.refCS)) + hpr.assign(np.getHpr(self.refCS)) + scale.assign(np.getScale()) + self.updatePosWidgets(pos) + self.updateHprWidgets(hpr) + self.updateScaleWidgets(scale) + + def updateAuxiliaryCoordinateSystems(self): + # Temp CS + self.tempCS.setPosHpr(self['nodePath'], 0,0,0,0,0,0) + # Orbit CS + # At reference + self.orbitFromCS.setPos(self.refCS, 0,0,0) + # But aligned with target + self.orbitFromCS.setHpr(self['nodePath'], 0,0,0) + # Also update to CS + self.orbitToCS.setPosHpr(self.orbitFromCS, 0,0,0,0,0,0) + # Get offset from origin + self.posOffset.assign(self['nodePath'].getPos(self.orbitFromCS)) + + ### NODE PATH TRANSFORMATION OPERATIONS ### + def xform(self, value, axis): + if axis in ['sx', 'sy', 'sz']: + self.xformScale(value,axis) + elif self.movementMode == 'Relative To:': + self.xformRelative(value, axis) + elif self.movementMode == 'Orbit:': + self.xformOrbit(value, axis) + if self.nodePathMenu.get() == 'widget': + if SEditor.manipulationControl.fSetCoa: + # Update coa based on current widget position + SEditor.selected.last.mCoa2Dnp.assign( + SEditor.widget.getMat(SEditor.selected.last)) + else: + # Move the objects with the widget + SEditor.selected.moveWrtWidgetAll() + + def xformStart(self, data): + # Record undo point + self.pushUndo() + # If moving widget kill follow task and update wrts + if self.nodePathMenu.get() == 'widget': + taskMgr.remove('followSelectedNodePath') + # Record relationship between selected nodes and widget + SEditor.selected.getWrtAll() + # Record initial state + self.deltaHpr = self['nodePath'].getHpr(self.refCS) + # Update placer to reflect new state + self.updatePlacer() + + def xformStop(self, data): + # Throw event to signal manipulation done + # Send nodepath as a list + messenger.send('DIRECT_manipulateObjectCleanup', [[self['nodePath']]]) + # Update placer to reflect new state + self.updatePlacer() + # If moving widget restart follow task + if self.nodePathMenu.get() == 'widget': + # Restart followSelectedNodePath task + SEditor.manipulationControl.spawnFollowSelectedNodePathTask() + + def xformRelative(self, value, axis): + nodePath = self['nodePath'] + if (nodePath != None) and (self.refCS != None): + if axis == 'x': + nodePath.setX(self.refCS, value) + elif axis == 'y': + nodePath.setY(self.refCS, value) + elif axis == 'z': + nodePath.setZ(self.refCS, value) + else: + if axis == 'h': + self.deltaHpr.setX(value) + elif axis == 'p': + self.deltaHpr.setY(value) + elif axis == 'r': + self.deltaHpr.setZ(value) + # Put node path at new hpr + nodePath.setHpr(self.refCS, self.deltaHpr) + + def xformOrbit(self, value, axis): + nodePath = self['nodePath'] + if ((nodePath != None) and (self.refCS != None) and + (self.orbitFromCS != None) and (self.orbitToCS != None)): + if axis == 'x': + self.posOffset.setX(value) + elif axis == 'y': + self.posOffset.setY(value) + elif axis == 'z': + self.posOffset.setZ(value) + elif axis == 'h': + self.orbitToCS.setH(self.orbitFromCS, value) + elif axis == 'p': + self.orbitToCS.setP(self.orbitFromCS, value) + elif axis == 'r': + self.orbitToCS.setR(self.orbitFromCS, value) + nodePath.setPosHpr(self.orbitToCS, self.posOffset, ZERO_VEC) + + def xformScale(self, value, axis): + if self['nodePath']: + mode = self.scalingMode.get() + scale = self['nodePath'].getScale() + if mode == 'Scale Free': + if axis == 'sx': + scale.setX(value) + elif axis == 'sy': + scale.setY(value) + elif axis == 'sz': + scale.setZ(value) + elif mode == 'Scale Uniform': + scale.set(value,value,value) + elif mode == 'Scale Proportional': + if axis == 'sx': + sf = value/scale[0] + elif axis == 'sy': + sf = value/scale[1] + elif axis == 'sz': + sf = value/scale[2] + scale = scale * sf + self['nodePath'].setScale(scale) + + def updatePosWidgets(self, pos): + self.posX.set(pos[0]) + self.posY.set(pos[1]) + self.posZ.set(pos[2]) + + def updateHprWidgets(self, hpr): + self.hprH.set(hpr[0]) + self.hprP.set(hpr[1]) + self.hprR.set(hpr[2]) + + def updateScaleWidgets(self, scale): + self.scaleX.set(scale[0]) + self.scaleY.set(scale[1]) + self.scaleZ.set(scale[2]) + + def zeroAll(self): + self.xformStart(None) + self.updatePosWidgets(ZERO_VEC) + self.updateHprWidgets(ZERO_VEC) + self.updateScaleWidgets(UNIT_VEC) + self.xformStop(None) + + def zeroPos(self): + self.xformStart(None) + self.updatePosWidgets(ZERO_VEC) + self.xformStop(None) + + def zeroHpr(self): + self.xformStart(None) + self.updateHprWidgets(ZERO_VEC) + self.xformStop(None) + + def unitScale(self): + self.xformStart(None) + self.updateScaleWidgets(UNIT_VEC) + self.xformStop(None) + + def updateResetValues(self, nodePath): + self.initPos.assign(nodePath.getPos()) + self.posX['resetValue'] = self.initPos[0] + self.posY['resetValue'] = self.initPos[1] + self.posZ['resetValue'] = self.initPos[2] + self.initHpr.assign(nodePath.getHpr()) + self.hprH['resetValue'] = self.initHpr[0] + self.hprP['resetValue'] = self.initHpr[1] + self.hprR['resetValue'] = self.initHpr[2] + self.initScale.assign(nodePath.getScale()) + self.scaleX['resetValue'] = self.initScale[0] + self.scaleY['resetValue'] = self.initScale[1] + self.scaleZ['resetValue'] = self.initScale[2] + + def resetAll(self): + if self['nodePath']: + self.xformStart(None) + self['nodePath'].setPosHprScale( + self.initPos, self.initHpr, self.initScale) + self.xformStop(None) + + def resetPos(self): + if self['nodePath']: + self.xformStart(None) + self['nodePath'].setPos(self.initPos) + self.xformStop(None) + + def resetHpr(self): + if self['nodePath']: + self.xformStart(None) + self['nodePath'].setHpr(self.initHpr) + self.xformStop(None) + + def resetScale(self): + if self['nodePath']: + self.xformStart(None) + self['nodePath'].setScale(self.initScale) + self.xformStop(None) + + def pushUndo(self, fResetRedo = 1): + SEditor.pushUndo([self['nodePath']]) + + def undoHook(self, nodePathList = []): + # Reflect new changes + self.updatePlacer() + + def pushUndoHook(self): + # Make sure button is reactivated + self.undoButton.configure(state = 'normal') + + def undoListEmptyHook(self): + # Make sure button is deactivated + self.undoButton.configure(state = 'disabled') + + def pushRedo(self): + SEditor.pushRedo([self['nodePath']]) + + def redoHook(self, nodePathList = []): + # Reflect new changes + self.updatePlacer() + + def pushRedoHook(self): + # Make sure button is reactivated + self.redoButton.configure(state = 'normal') + + def redoListEmptyHook(self): + # Make sure button is deactivated + self.redoButton.configure(state = 'disabled') + + def printNodePathInfo(self): + np = self['nodePath'] + if np: + name = np.getName() + pos = np.getPos() + hpr = np.getHpr() + scale = np.getScale() + posString = '%.2f, %.2f, %.2f' % (pos[0], pos[1], pos[2]) + hprString = '%.2f, %.2f, %.2f' % (hpr[0], hpr[1], hpr[2]) + scaleString = '%.2f, %.2f, %.2f' % (scale[0], scale[1], scale[2]) + print 'NodePath: %s' % name + print 'Pos: %s' % posString + print 'Hpr: %s' % hprString + print 'Scale: %s' % scaleString + print ('%s.setPosHprScale(%s, %s, %s)' % + (name, posString, hprString, scaleString)) + + def onDestroy(self, event): + # Remove hooks + for event, method in self.undoEvents: + self.ignore(event) + self.tempCS.removeNode() + self.orbitFromCS.removeNode() + self.orbitToCS.removeNode() + # send out a message to let sceneEditor know that placer panel has been closed. + # Also, stop accepting the updata message from sceneEditor + messenger.send('Placer_close') + self.ignore('placerUpdate') + +def place(nodePath): + return Placer(nodePath = nodePath) + +###################################################################### + +# Create demo in root window for testing. +if __name__ == '__main__': + root = Pmw.initialise() + widget = Placer() + diff --git a/doc/SceneEditor/seSceneGraphExplorer.py b/doc/SceneEditor/seSceneGraphExplorer.py new file mode 100644 index 0000000000..776bb5c732 --- /dev/null +++ b/doc/SceneEditor/seSceneGraphExplorer.py @@ -0,0 +1,208 @@ +################################################################# +# seSceneGraphExplorer.py +# Originally from SceneGraphExplorer.py +# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +# +# we need a customized SceneGraphExplorer. +# +# Do forget to check the seTree. +# +################################################################# +from PandaObject import * +from TkGlobal import * +from seTree import * + +# changing these strings requires changing sceneEditor.py SGE_ strs too! +# This list of items will be showed on the pop out window when user right click on +# any node on the graph. And, this is also the main reason we decide to copy from +# the original one but not inherited from it. +# Because except drawing part, we have changed a lot of things... +DEFAULT_MENU_ITEMS = [ + 'Update Explorer', + 'Separator', + 'Properties', + 'Separator', + 'Duplicate', + 'Remove', + 'Add Dummy', + 'Add Collision Object', + 'Metadata', + 'Separator', + 'Set as Reparent Target', + 'Reparent to Target', + 'Separator', + 'Animation Panel', + 'Blend Animation Panel', + 'MoPath Panel', + 'Align Tool', + 'Separator'] + +class seSceneGraphExplorer(Pmw.MegaWidget, PandaObject): + "Graphical display of a scene graph" + def __init__(self, parent = None, nodePath = render, **kw): + # Define the megawidget options. + optiondefs = ( + ('menuItems', [], Pmw.INITOPT), + ) + self.defineoptions(kw, optiondefs) + + # Initialise superclass + Pmw.MegaWidget.__init__(self, parent) + + # Initialize some class variables + self.nodePath = nodePath + + # Create the components. + + # Setup up container + interior = self.interior() + interior.configure(relief = GROOVE, borderwidth = 2) + + # Create a label and an entry + self._scrolledCanvas = self.createcomponent( + 'scrolledCanvas', + (), None, + Pmw.ScrolledCanvas, (interior,), + hull_width = 200, hull_height = 300, + usehullsize = 1) + self._canvas = self._scrolledCanvas.component('canvas') + self._canvas['scrollregion'] = ('0i', '0i', '2i', '4i') + self._scrolledCanvas.resizescrollregion() + self._scrolledCanvas.pack(padx = 3, pady = 3, expand=1, fill = BOTH) + + self._canvas.bind('', self.mouse2Down) + self._canvas.bind('', self.mouse2Motion) + self._canvas.bind('', + lambda e, sc = self._scrolledCanvas: + sc.resizescrollregion()) + self.interior().bind('', self.onDestroy) + + # Create the contents + self._treeItem = SceneGraphExplorerItem(self.nodePath) + + self._node = TreeNode(self._canvas, None, self._treeItem, + DEFAULT_MENU_ITEMS + self['menuItems']) + self._node.expand() + + self._parentFrame = Frame(interior) + self._label = self.createcomponent( + 'parentLabel', + (), None, + Label, (interior,), + text = 'Active Reparent Target: ', + anchor = W, justify = LEFT) + self._label.pack(fill = X) + + # Add update parent label + def updateLabel(nodePath = None, s = self): + s._label['text'] = 'Active Reparent Target: ' + nodePath.getName() + self.accept('DIRECT_activeParent', updateLabel) + + # Add update hook + self.accept('SGE_Update Explorer', + lambda np, s = self: s.update()) + + # Check keywords and initialise options based on input values. + self.initialiseoptions(seSceneGraphExplorer) + + def update(self): + """ Refresh scene graph explorer """ + self._node.update() + + def mouse2Down(self, event): + self._width = 1.0 * self._canvas.winfo_width() + self._height = 1.0 * self._canvas.winfo_height() + xview = self._canvas.xview() + yview = self._canvas.yview() + self._left = xview[0] + self._top = yview[0] + self._dxview = xview[1] - xview[0] + self._dyview = yview[1] - yview[0] + self._2lx = event.x + self._2ly = event.y + + def mouse2Motion(self,event): + newx = self._left - ((event.x - self._2lx)/self._width) * self._dxview + self._canvas.xview_moveto(newx) + newy = self._top - ((event.y - self._2ly)/self._height) * self._dyview + self._canvas.yview_moveto(newy) + self._2lx = event.x + self._2ly = event.y + self._left = self._canvas.xview()[0] + self._top = self._canvas.yview()[0] + + def onDestroy(self, event): + # Remove hooks + self.ignore('DIRECT_activeParent') + self.ignore('SGE_Update Explorer') + + def deSelectTree(self): + self._node.deselecttree() + + def selectNodePath(self,nodePath, callBack=True): + item = self._node.find(nodePath.id()) + if item!= None: + item.select(callBack) + else: + print '----SGE: Error Selection' + +class SceneGraphExplorerItem(TreeItem): + + """Example TreeItem subclass -- browse the file system.""" + + def __init__(self, nodePath): + self.nodePath = nodePath + + def GetText(self): + type = self.nodePath.node().getType().getName() + name = self.nodePath.getName() + return type + " " + name + + def GetTextForEdit(self): + name = self.nodePath.getName() + return name + + def GetKey(self): + return self.nodePath.id() + + def IsEditable(self): + # All nodes' names can be edited nowadays. + return 1 + #return issubclass(self.nodePath.node().__class__, NamedNode) + + def SetText(self, text): + try: + messenger.send('SGE_changeName', [self.nodePath, text]) + except AttributeError: + pass + + def GetIconName(self): + return "sphere2" # XXX wish there was a "file" icon + + def IsExpandable(self): + return self.nodePath.getNumChildren() != 0 + + def GetSubList(self): + sublist = [] + for nodePath in self.nodePath.getChildrenAsList(): + item = SceneGraphExplorerItem(nodePath) + sublist.append(item) + return sublist + + def OnSelect(self, callback): + messenger.send('SGE_Flash', [self.nodePath]) + if not callback: + messenger.send('SGE_madeSelection', [self.nodePath, callback]) + else: + messenger.send('SGE_madeSelection', [self.nodePath]) + + def MenuCommand(self, command): + messenger.send('SGE_' + command, [self.nodePath]) + + +def explore(nodePath = render): + tl = Toplevel() + tl.title('Explore: ' + nodePath.getName()) + sge = seSceneGraphExplorer(parent = tl, nodePath = nodePath) + sge.pack(expand = 1, fill = 'both') + return sge diff --git a/doc/SceneEditor/seSelection.py b/doc/SceneEditor/seSelection.py new file mode 100644 index 0000000000..96e4772a98 --- /dev/null +++ b/doc/SceneEditor/seSelection.py @@ -0,0 +1,716 @@ +################################################################# +# seSelection.py +# Originally from DirectSelection.py +# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +# +# We didn't change anything essential. +# Just because we customized the seSession from DirectSession, +# So we need related files can follow the change. +# However, we don't want to change anything inside the original directool +# to let them can work with our scene editor. +# (If we do change original directools, it will force user has to install the latest version of OUR Panda) +# +################################################################# +from PandaObject import * +from DirectGlobals import * +from DirectUtil import * +from seGeometry import * +from quad import * +COA_ORIGIN = 0 +COA_CENTER = 1 + +# MRM: To do: handle broken node paths in selected and deselected dicts +class DirectNodePath(NodePath): + # A node path augmented with info, bounding box, and utility methods + def __init__(self, nodePath): + # Initialize the superclass + NodePath.__init__(self) + self.assign(nodePath) + # Create a bounding box + self.bbox = DirectBoundingBox(self) + center = self.bbox.getCenter() + # Create matrix to hold the offset between the nodepath + # and its center of action (COA) + self.mCoa2Dnp = Mat4(Mat4.identMat()) + if SEditor.coaMode == COA_CENTER: + self.mCoa2Dnp.setRow(3, Vec4(center[0], center[1], center[2], 1)) + + # Transform from nodePath to widget + self.tDnp2Widget = TransformState.makeIdentity() + + def highlight(self): + self.bbox.show() + + def dehighlight(self): + self.bbox.hide() + + def getCenter(self): + return self.bbox.getCenter() + + def getRadius(self): + return self.bbox.getRadius() + + def getMin(self): + return self.bbox.getMin() + + def getMax(self): + return self.bbox.getMax() + +class SelectedNodePaths(PandaObject): + def __init__(self): + self.reset() + + def reset(self): + self.selectedDict = {} + self.deselectedDict = {} + __builtins__["last"] = self.last = None + + def select(self, nodePath, fMultiSelect = 0): + """ Select the specified node path. Multiselect as required """ + # Do nothing if nothing selected + if not nodePath: + print 'Nothing selected!!' + return None + + # Reset selected objects and highlight if multiSelect is false + if not fMultiSelect: + self.deselectAll() + + # Get this pointer + id = nodePath.id() + # First see if its already in the selected dictionary + dnp = self.getSelectedDict(id) + # If so, we're done + if not dnp: + # See if it is in the deselected dictionary + dnp = self.getDeselectedDict(id) + if dnp: + # Remove it from the deselected dictionary + del(self.deselectedDict[id]) + # Show its bounding box + dnp.highlight() + else: + # Didn't find it, create a new selectedNodePath instance + dnp = DirectNodePath(nodePath) + # Show its bounding box + dnp.highlight() + # Add it to the selected dictionary + self.selectedDict[dnp.id()] = dnp + # And update last + __builtins__["last"] = self.last = dnp + return dnp + + def deselect(self, nodePath): + """ Deselect the specified node path """ + # Get this pointer + id = nodePath.id() + # See if it is in the selected dictionary + dnp = self.getSelectedDict(id) + if dnp: + # It was selected: + # Hide its bounding box + dnp.dehighlight() + # Remove it from the selected dictionary + del(self.selectedDict[id]) + # And keep track of it in the deselected dictionary + self.deselectedDict[id] = dnp + # Send a message + messenger.send('DIRECT_deselectedNodePath', [dnp]) + return dnp + + def getSelectedAsList(self): + """ + Return a list of all selected node paths. No verification of + connectivity is performed on the members of the list + """ + return self.selectedDict.values()[:] + + def __getitem__(self,index): + return self.getSelectedAsList()[index] + + def getSelectedDict(self, id): + """ + Search selectedDict for node path, try to repair broken node paths. + """ + dnp = self.selectedDict.get(id, None) + if dnp: + return dnp + else: + # Not in selected dictionary + return None + + def getDeselectedAsList(self): + return self.deselectedDict.values()[:] + + def getDeselectedDict(self, id): + """ + Search deselectedDict for node path, try to repair broken node paths. + """ + dnp = self.deselectedDict.get(id, None) + if dnp: + # Yes + return dnp + else: + # Not in deselected dictionary + return None + + def forEachSelectedNodePathDo(self, func): + """ + Perform given func on selected node paths. No node path + connectivity verification performed + """ + selectedNodePaths = self.getSelectedAsList() + for nodePath in selectedNodePaths: + func(nodePath) + + def forEachDeselectedNodePathDo(self, func): + """ + Perform given func on deselected node paths. No node path + connectivity verification performed + """ + deselectedNodePaths = self.getDeselectedAsList() + for nodePath in deselectedNodePaths: + func(nodePath) + + def getWrtAll(self): + self.forEachSelectedNodePathDo(self.getWrt) + + def getWrt(self, nodePath): + nodePath.tDnp2Widget = nodePath.getTransform(SEditor.widget) + + def moveWrtWidgetAll(self): + self.forEachSelectedNodePathDo(self.moveWrtWidget) + + def moveWrtWidget(self, nodePath): + nodePath.setTransform(SEditor.widget, nodePath.tDnp2Widget) + + def deselectAll(self): + self.forEachSelectedNodePathDo(self.deselect) + + def highlightAll(self): + self.forEachSelectedNodePathDo(DirectNodePath.highlight) + + def dehighlightAll(self): + self.forEachSelectedNodePathDo(DirectNodePath.dehighlight) + + def removeSelected(self): + selected = self.last + if selected: + selected.remove() + __builtins__["last"] = self.last = None + + def removeAll(self): + # Remove all selected nodePaths from the Scene Graph + self.forEachSelectedNodePathDo(NodePath.remove) + + def toggleVisSelected(self): + selected = self.last + # Toggle visibility of selected node paths + if selected: + selected.toggleVis() + + def toggleVisAll(self): + # Toggle viz for all selected node paths + self.forEachSelectedNodePathDo(NodePath.toggleVis) + + def isolateSelected(self): + selected = self.last + if selected: + selected.isolate() + + def getDirectNodePath(self, nodePath): + # Get this pointer + id = nodePath.id() + # First check selected dict + dnp = self.getSelectedDict(id) + if dnp: + return dnp + # Otherwise return result of deselected search + return self.getDeselectedDict(id) + + def getNumSelected(self): + return len(self.selectedDict.keys()) + + +class DirectBoundingBox: + def __init__(self, nodePath): + # Record the node path + self.nodePath = nodePath + # Compute bounds, min, max, etc. + self.computeTightBounds() + # Generate the bounding box + self.lines = self.createBBoxLines() + + def computeTightBounds(self): + # Compute bounding box using tighter calcTightBounds function + # Need to clear out existing transform on node path + tMat = Mat4() + tMat.assign(self.nodePath.getMat()) + self.nodePath.clearMat() + # Get bounds + self.min = Point3(0) + self.max = Point3(0) + self.nodePath.calcTightBounds(self.min,self.max) + # Calc center and radius + self.center = Point3((self.min + self.max)/2.0) + self.radius = Vec3(self.max - self.min).length() + # Restore transform + self.nodePath.setMat(tMat) + del tMat + + def computeBounds(self): + self.bounds = self.getBounds() + if self.bounds.isEmpty() or self.bounds.isInfinite(): + self.center = Point3(0) + self.radius = 1.0 + else: + self.center = self.bounds.getCenter() + self.radius = self.bounds.getRadius() + self.min = Point3(self.center - Point3(self.radius)) + self.max = Point3(self.center + Point3(self.radius)) + + def createBBoxLines(self): + # Create a line segments object for the bbox + lines = LineNodePath(hidden) + lines.node().setName('bboxLines') + lines.setColor( VBase4( 1., 0., 0., 1. ) ) + lines.setThickness( 0.5 ) + + minX = self.min[0] + minY = self.min[1] + minZ = self.min[2] + maxX = self.max[0] + maxY = self.max[1] + maxZ = self.max[2] + + # Bottom face + lines.moveTo( minX, minY, minZ ) + lines.drawTo( maxX, minY, minZ ) + lines.drawTo( maxX, maxY, minZ ) + lines.drawTo( minX, maxY, minZ ) + lines.drawTo( minX, minY, minZ ) + + # Front Edge/Top face + lines.drawTo( minX, minY, maxZ ) + lines.drawTo( maxX, minY, maxZ ) + lines.drawTo( maxX, maxY, maxZ ) + lines.drawTo( minX, maxY, maxZ ) + lines.drawTo( minX, minY, maxZ ) + + # Three remaining edges + lines.moveTo( maxX, minY, minZ ) + lines.drawTo( maxX, minY, maxZ ) + lines.moveTo( maxX, maxY, minZ ) + lines.drawTo( maxX, maxY, maxZ ) + lines.moveTo( minX, maxY, minZ ) + lines.drawTo( minX, maxY, maxZ ) + + # Create and return bbox lines + lines.create() + + # Make sure bbox is never lit or drawn in wireframe + useDirectRenderStyle(lines) + + return lines + + def updateBBoxLines(self): + ls = self.lines.lineSegs + + minX = self.min[0] + minY = self.min[1] + minZ = self.min[2] + maxX = self.max[0] + maxY = self.max[1] + maxZ = self.max[2] + + # Bottom face + ls.setVertex( 0, minX, minY, minZ ) + ls.setVertex( 1, maxX, minY, minZ ) + ls.setVertex( 2, maxX, maxY, minZ ) + ls.setVertex( 3, minX, maxY, minZ ) + ls.setVertex( 4, minX, minY, minZ ) + + # Front Edge/Top face + ls.setVertex( 5, minX, minY, maxZ ) + ls.setVertex( 6, maxX, minY, maxZ ) + ls.setVertex( 7, maxX, maxY, maxZ ) + ls.setVertex( 8, minX, maxY, maxZ ) + ls.setVertex( 9, minX, minY, maxZ ) + + # Three remaining edges + ls.setVertex( 10, maxX, minY, minZ ) + ls.setVertex( 11, maxX, minY, maxZ ) + ls.setVertex( 12, maxX, maxY, minZ ) + ls.setVertex( 13, maxX, maxY, maxZ ) + ls.setVertex( 14, minX, maxY, minZ ) + ls.setVertex( 15, minX, maxY, maxZ ) + + def getBounds(self): + # Get a node path's bounds + nodeBounds = BoundingSphere() + nodeBounds.extendBy(self.nodePath.node().getInternalBound()) + for child in self.nodePath.getChildrenAsList(): + nodeBounds.extendBy(child.getBounds()) + return nodeBounds.makeCopy() + + def show(self): + self.lines.reparentTo(self.nodePath) + + def hide(self): + self.lines.reparentTo(hidden) + + def getCenter(self): + return self.center + + def getRadius(self): + return self.radius + + def getMin(self): + return self.min + + def getMax(self): + return self.max + + def vecAsString(self, vec): + return '%.2f %.2f %.2f' % (vec[0], vec[1], vec[2]) + + def __repr__(self): + return (`self.__class__` + + '\nNodePath:\t%s\n' % self.nodePath.getName() + + 'Min:\t\t%s\n' % self.vecAsString(self.min) + + 'Max:\t\t%s\n' % self.vecAsString(self.max) + + 'Center:\t\t%s\n' % self.vecAsString(self.center) + + 'Radius:\t\t%.2f' % self.radius + ) + + +class SelectionQueue(CollisionHandlerQueue): + def __init__(self, parentNP = render): + # Initialize the superclass + CollisionHandlerQueue.__init__(self) + # Current index and entry in collision queue + self.index = -1 + self.entry = None + self.skipFlags = SKIP_NONE + # Create a collision node path attached to the given NP + self.collisionNodePath = NodePath(CollisionNode("collisionNP")) + self.setParentNP(parentNP) + # Don't pay the penalty of drawing this collision ray + self.collisionNodePath.hide() + self.collisionNode = self.collisionNodePath.node() + # Intersect with geometry to begin with + self.collideWithGeom() + # And a traverser to do the actual collision tests + self.ct = CollisionTraverser() + # Let the traverser know about the collision node and the queue + self.ct.addCollider(self.collisionNode, self) + # List of objects that can't be selected + self.unpickable = UNPICKABLE + # Derived class must add Collider to complete initialization + + def setParentNP(self, parentNP): + # Update collisionNodePath's parent + self.collisionNodePath.reparentTo(parentNP) + + def addCollider(self, collider): + # Inherited class must call this function to specify collider object + # Record collision object + self.collider = collider + # Add the collider to the collision Node + self.collisionNode.addSolid( self.collider ) + + def collideWithBitMask(self, bitMask): + # The into collide mask is the bit pattern colliders look at + # when deciding whether or not to test for a collision "into" + # this collision solid. Set to all Off so this collision solid + # will not be considered in any collision tests + self.collisionNode.setIntoCollideMask(BitMask32().allOff()) + # The from collide mask is the bit pattern *this* collision solid + # compares against the into collide mask of candidate collision solids + # Turn this mask all off since we're not testing for collisions against + # collision solids + self.collisionNode.setFromCollideMask(bitMask) + # Don't test against actual geometry + self.collisionNode.setCollideGeom(0) + + def collideWithGeom(self): + # The into collide mask is the bit pattern colliders look at + # when deciding whether or not to test for a collision "into" + # this collision solid. Set to all Off so this collision solid + # will not be considered in any collision tests + self.collisionNode.setIntoCollideMask(BitMask32().allOff()) + # The from collide mask is the bit pattern *this* collision solid + # compares against the into collide mask of candidate collision solids + # Turn this mask all off since we're not testing for collisions against + # collision solids + self.collisionNode.setFromCollideMask(BitMask32().allOff()) + # What we want to test against is actual geometry + self.collisionNode.setCollideGeom(1) + + def collideWithWidget(self): + # This collision node should not be tested against by any other + # collision solids + self.collisionNode.setIntoCollideMask(BitMask32().allOff()) + # This collision node will test for collisions with any collision + # solids with a bit mask set to 0x80000000 + mask = BitMask32() + mask.setWord(0x80000000) + self.collisionNode.setFromCollideMask(mask) + # But in this case, don't test against actual geometry + self.collisionNode.setCollideGeom(0) + + def addUnpickable(self, item): + if item not in self.unpickable: + self.unpickable.append(item) + + def removeUnpickable(self, item): + if item in self.unpickable: + self.unpickable.remove(item) + + def setCurrentIndex(self, index): + if (index < 0) or (index >= self.getNumEntries()): + self.index = -1 + else: + self.index = index + + def setCurrentEntry(self, entry): + self.entry = entry + + def getCurrentEntry(self): + return self.entry + + def isEntryBackfacing(self, entry): + # If dot product of collision point surface normal and + # ray from camera to collision point is positive, we are + # looking at the backface of the polygon + if not entry.hasFromSurfaceNormal(): + # Well, no way to tell. Assume we're not backfacing. + return 0 + + v = Vec3(entry.getFromIntersectionPoint()) + n = entry.getFromSurfaceNormal() + # Convert to camera space for backfacing test + if self.collisionNodePath.getParent() != base.cam: + # Problem: assumes base.cam is the camera in question + p2cam = self.collisionNodePath.getParent().getMat(base.cam) + v = Vec3(p2cam.xformPoint(v)) + n = p2cam.xformVec(n) + # Normalize and check angle between to vectors + v.normalize() + return v.dot(n) >= 0 + + def findNextCollisionEntry(self, skipFlags = SKIP_NONE): + return self.findCollisionEntry(skipFlags, self.index + 1) + + def findCollisionEntry(self, skipFlags = SKIP_NONE, startIndex = 0 ): + # Init self.index and self.entry + self.setCurrentIndex(-1) + self.setCurrentEntry(None) + # Pick out the closest object that isn't a widget + for i in range(startIndex,self.getNumEntries()): + entry = self.getEntry(i) + nodePath = entry.getIntoNodePath() + if (skipFlags & SKIP_HIDDEN) and nodePath.isHidden(): + # Skip if hidden node + pass + elif (skipFlags & SKIP_BACKFACE) and self.isEntryBackfacing(entry): + # Skip, if backfacing poly + pass + elif ((skipFlags & SKIP_CAMERA) and + (camera in nodePath.getAncestry())): + # Skip if parented to a camera. + pass + # Can pick unpickable, use the first visible node + elif ((skipFlags & SKIP_UNPICKABLE) and + (nodePath.getName() in self.unpickable)): + # Skip if in unpickable list + pass + else: + self.setCurrentIndex(i) + self.setCurrentEntry(entry) + break + return self.getCurrentEntry() + +class SelectionRay(SelectionQueue): + def __init__(self, parentNP = render): + # Initialize the superclass + SelectionQueue.__init__(self, parentNP) + self.addCollider(CollisionRay()) + + def pick(self, targetNodePath, xy = None): + # Determine ray direction based upon the mouse coordinates + if xy: + mx = xy[0] + my = xy[1] + elif direct: + mx = SEditor.dr.mouseX + my = SEditor.dr.mouseY + else: + if not base.mouseWatcherNode.hasMouse(): + # No mouse in window. + self.clearEntries() + return + mx = base.mouseWatcherNode.getMouseX() + my = base.mouseWatcherNode.getMouseY() + #base.mouseWatcherNode.setDisplayRegion(base.win.getDisplayRegion(0)) + #mx = base.mouseWatcherNode.getMouseX()+1 + #my = base.mouseWatcherNode.getMouseY()+1 + #print base.camNode.getName() + #print "Arrived X" + str(mx) + " Arrived Y " + str(my) + self.collider.setFromLens( base.camNode, mx, my ) + + self.ct.traverse( targetNodePath ) + self.sortEntries() + + + def pickBitMask(self, bitMask = BitMask32.allOff(), + targetNodePath = render, + skipFlags = SKIP_ALL ): + self.collideWithBitMask(bitMask) + self.pick(targetNodePath) + # Determine collision entry + return self.findCollisionEntry(skipFlags) + + def pickGeom(self, targetNodePath = render, skipFlags = SKIP_ALL, + xy = None): + self.collideWithGeom() + self.pick(targetNodePath, xy = xy) + # Determine collision entry + return self.findCollisionEntry(skipFlags) + + def pickWidget(self, targetNodePath = render, skipFlags = SKIP_NONE ): + self.collideWithWidget() + self.pick(targetNodePath) + # Determine collision entry + return self.findCollisionEntry(skipFlags) + + def pick3D(self, targetNodePath, origin, dir): + # Determine ray direction based upon the mouse coordinates + self.collider.setOrigin( origin ) + self.collider.setDirection( dir ) + self.ct.traverse( targetNodePath ) + self.sortEntries() + + def pickGeom3D(self, targetNodePath = render, + origin = Point3(0), dir = Vec3(0,0,-1), + skipFlags = SKIP_HIDDEN | SKIP_CAMERA ): + self.collideWithGeom() + self.pick3D(targetNodePath, origin, dir) + # Determine collision entry + return self.findCollisionEntry(skipFlags) + + def pickBitMask3D(self, bitMask = BitMask32.allOff(), + targetNodePath = render, + origin = Point3(0), dir = Vec3(0,0,-1), + skipFlags = SKIP_ALL ): + self.collideWithBitMask(bitMask) + self.pick3D(targetNodePath, origin, dir) + # Determine collision entry + return self.findCollisionEntry(skipFlags) + + +class SelectionSegment(SelectionQueue): + # Like a selection ray but with two endpoints instead of an endpoint + # and a direction + def __init__(self, parentNP = render, numSegments = 1): + # Initialize the superclass + SelectionQueue.__init__(self, parentNP) + self.colliders = [] + self.numColliders = 0 + for i in range(numSegments): + self.addCollider(CollisionSegment()) + + def addCollider(self, collider): + # Record new collision object + self.colliders.append(collider) + # Add the collider to the collision Node + self.collisionNode.addSolid( collider ) + self.numColliders += 1 + + def pickGeom(self, targetNodePath = render, endPointList = [], + skipFlags = SKIP_HIDDEN | SKIP_CAMERA ): + self.collideWithGeom() + for i in range(min(len(endPointList), self.numColliders)): + pointA, pointB = endPointList[i] + collider = self.colliders[i] + collider.setPointA( pointA ) + collider.setPointB( pointB ) + self.ct.traverse( targetNodePath ) + # Determine collision entry + return self.findCollisionEntry(skipFlags) + + def pickBitMask(self, bitMask = BitMask32.allOff(), + targetNodePath = render, endPointList = [], + skipFlags = SKIP_HIDDEN | SKIP_CAMERA ): + self.collideWithBitMask(bitMask) + for i in range(min(len(endPointList), self.numColliders)): + pointA, pointB = endPointList[i] + collider = self.colliders[i] + collider.setPointA( pointA ) + collider.setPointB( pointB ) + self.ct.traverse( targetNodePath ) + # Determine collision entry + return self.findCollisionEntry(skipFlags) + + +class SelectionSphere(SelectionQueue): + # Wrapper around collision sphere + def __init__(self, parentNP = render, numSpheres = 1): + # Initialize the superclass + SelectionQueue.__init__(self, parentNP) + self.colliders = [] + self.numColliders = 0 + for i in range(numSpheres): + self.addCollider(CollisionSphere(Point3(0), 1)) + + def addCollider(self, collider): + # Record new collision object + self.colliders.append(collider) + # Add the collider to the collision Node + self.collisionNode.addSolid( collider ) + self.numColliders += 1 + + def setCenter(self, i, center): + c = self.colliders[i] + c.setCenter(center) + + def setRadius(self, i, radius): + c = self.colliders[i] + c.setRadius(radius) + + def setCenterRadius(self, i, center, radius): + c = self.colliders[i] + c.setCenter(center) + c.setRadius(radius) + + def isEntryBackfacing(self, entry): + # If dot product of collision point surface normal and + # ray from sphere origin to collision point is positive, + # center is on the backside of the polygon + v = Vec3(entry.getFromIntersectionPoint() - + entry.getFrom().getCenter()) + n = entry.getFromSurfaceNormal() + # If points almost on top of each other, reject face + # (treat as backfacing) + if v.length() < 0.05: + return 1 + # Normalize and check angle between to vectors + v.normalize() + return v.dot(n) >= 0 + + def pick(self, targetNodePath, skipFlags): + self.ct.traverse( targetNodePath ) + self.sortEntries() + return self.findCollisionEntry(skipFlags) + + def pickGeom(self, targetNodePath = render, + skipFlags = SKIP_HIDDEN | SKIP_CAMERA ): + self.collideWithGeom() + return self.pick(targetNodePath, skipFlags) + + def pickBitMask(self, bitMask = BitMask32.allOff(), + targetNodePath = render, + skipFlags = SKIP_HIDDEN | SKIP_CAMERA ): + self.collideWithBitMask(bitMask) + return self.pick(targetNodePath, skipFlags) + diff --git a/doc/SceneEditor/seTree.py b/doc/SceneEditor/seTree.py new file mode 100644 index 0000000000..12f26103b4 --- /dev/null +++ b/doc/SceneEditor/seTree.py @@ -0,0 +1,421 @@ +################################################################# +# seTree.py +# Originally from Tree.py +# Altered by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004 +# +# This class actually decides the behavior of the sceneGraphExplorer +# You might feel it realy looks like the original one, but we actually did a lots of change in it. +# such as, when selection happend in other place, such as picking directly inside the scene. or, +# when user removed something by hot key. +# The rename process has also been changed. It won't be rename in here anymore. +# Instead, here we will send out a message to sceneEditor to reaname the target. +# +################################################################# + +import os +import sys +import string +from TkGlobal import * +from PandaObject import * + +# Initialize icon directory +f = Filename('icons') +f.resolveFilename(getModelPath()) +ICONDIR = f.toOsSpecific() +if not os.path.isdir(ICONDIR): + raise RuntimeError, "can't find DIRECT icon directory (%s)" % `ICONDIR` + +class TreeNode: + + def __init__(self, canvas, parent, item, menuList = []): + self.canvas = canvas + self.parent = parent + self.item = item + self.state = 'collapsed' + self.selected = 0 + self.children = {} + self.kidKeys = [] + self.x = self.y = None + self.iconimages = {} # cache of PhotoImage instances for icons + self.menuList = menuList + self.menuVar = IntVar() + self.menuVar.set(0) + self._popupMenu = None + self.image_id = None + if self.menuList: + if self.menuList[-1] == 'Separator': + self.menuList = self.menuList[:-1] + self._popupMenu = Menu(self.canvas, tearoff = 0) + for i in range(len(self.menuList)): + item = self.menuList[i] + if item == 'Separator': + self._popupMenu.add_separator() + else: + self._popupMenu.add_radiobutton( + label = item, + variable = self.menuVar, + value = i, + indicatoron = 0, + command = self.popupMenuCommand) + + def destroy(self): + for key in self.kidKeys: + c = self.children[key] + del self.children[key] + c.destroy() + self.parent = None + + def geticonimage(self, name): + try: + return self.iconimages[name] + except KeyError: + pass + file, ext = os.path.splitext(name) + ext = ext or ".gif" + fullname = os.path.join(ICONDIR, file + ext) + image = PhotoImage(master=self.canvas, file=fullname) + self.iconimages[name] = image + return image + + def select(self, event=None): + if self.selected: + return + self.deselectall() + self.selected = 1 + if self.parent != None: + if self.parent.state == 'expanded': + self.canvas.delete(self.image_id) + self.drawicon() + self.drawtext() + self.item.OnSelect(event) + + def deselect(self, event=None): + if not self.selected: + return + self.selected = 0 + if self.parent != None: + if self.parent.state == 'expanded': + self.canvas.delete(self.image_id) + self.drawicon() + self.drawtext() + + def deselectall(self): + if self.parent: + self.parent.deselectall() + else: + self.deselecttree() + + def deselecttree(self): + if self.selected: + self.deselect() + for key in self.kidKeys: + child = self.children[key] + child.deselecttree() + + def flip(self, event=None): + if self.state == 'expanded': + self.collapse() + else: + self.expand() + self.item.OnDoubleClick() + return "break" + + def popupMenu(self, event=None): + if self._popupMenu: + self._popupMenu.post(event.widget.winfo_pointerx(), + event.widget.winfo_pointery()) + return "break" + + def popupMenuCommand(self): + command = self.menuList[self.menuVar.get()] + self.item.MenuCommand(command) + if self.parent and (command != 'Update Explorer'): + # Update parent to try to keep explorer up to date + self.parent.update() + + def expand(self, event=None): + if not self.item.IsExpandable(): + return + if self.state != 'expanded': + self.state = 'expanded' + self.update() + self.view() + + def collapse(self, event=None): + if self.state != 'collapsed': + self.state = 'collapsed' + self.update() + + def view(self): + top = self.y - 2 + bottom = self.lastvisiblechild().y + 17 + height = bottom - top + visible_top = self.canvas.canvasy(0) + visible_height = self.canvas.winfo_height() + visible_bottom = self.canvas.canvasy(visible_height) + if visible_top <= top and bottom <= visible_bottom: + return + x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion']) + if top >= visible_top and height <= visible_height: + fraction = top + height - visible_height + else: + fraction = top + fraction = float(fraction) / y1 + self.canvas.yview_moveto(fraction) + + def reveal(self): + # Make sure all parent nodes are marked as expanded + parent = self.parent + while parent: + if parent.state == 'collapsed': + parent.state = 'expanded' + parent = parent.parent + else: + break + # Redraw tree accordingly + self.update() + # Bring this item into view + self.view() + + def lastvisiblechild(self): + if self.kidKeys and self.state == 'expanded': + return self.children[self.kidKeys[-1]].lastvisiblechild() + else: + return self + + def update(self): + if self.parent: + self.parent.update() + else: + oldcursor = self.canvas['cursor'] + self.canvas['cursor'] = "watch" + self.canvas.update() + self.canvas.delete(ALL) # XXX could be more subtle + self.draw(7, 2) + x0, y0, x1, y1 = self.canvas.bbox(ALL) + self.canvas.configure(scrollregion=(0, 0, x1, y1)) + self.canvas['cursor'] = oldcursor + + def draw(self, x, y): + # XXX This hard-codes too many geometry constants! + self.x, self.y = x, y + self.drawicon() + self.drawtext() + if self.state != 'expanded': + return y+17 + # draw children + sublist = self.item._GetSubList() + if not sublist: + # IsExpandable() was mistaken; that's allowed + return y+17 + self.kidKeys = [] + for item in sublist: + key = item.GetKey() + if self.children.has_key(key): + child = self.children[key] + else: + child = TreeNode(self.canvas, self, item, self.menuList) + self.children[key] = child + self.kidKeys.append(key) + # Remove unused children + for key in self.children.keys(): + if key not in self.kidKeys: + del(self.children[key]) + cx = x+20 + cy = y+17 + cylast = 0 + for key in self.kidKeys: + child = self.children[key] + cylast = cy + self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50") + cy = child.draw(cx, cy) + if child.item.IsExpandable(): + if child.state == 'expanded': + iconname = "minusnode" + callback = child.collapse + else: + iconname = "plusnode" + callback = child.expand + image = self.geticonimage(iconname) + id = self.canvas.create_image(x+9, cylast+7, image=image) + # XXX This leaks bindings until canvas is deleted: + self.canvas.tag_bind(id, "<1>", callback) + self.canvas.tag_bind(id, "", lambda x: None) + id = self.canvas.create_line(x+9, y+10, x+9, cylast+7, + ##stipple="gray50", # XXX Seems broken in Tk 8.0.x + fill="gray50") + self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2 + return cy + + def drawicon(self): + if self.selected: + imagename = (self.item.GetSelectedIconName() or + self.item.GetIconName() or + "openfolder") + else: + imagename = self.item.GetIconName() or "folder" + image = self.geticonimage(imagename) + id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image) + self.image_id = id + self.canvas.tag_bind(id, "<1>", self.select) + self.canvas.tag_bind(id, "", self.flip) + self.canvas.tag_bind(id, "<3>", self.popupMenu) + + def drawtext(self, text=None): + textx = self.x+20-1 + texty = self.y-1 + labeltext = self.item.GetLabelText() + if labeltext: + id = self.canvas.create_text(textx, texty, anchor="nw", + text=labeltext) + self.canvas.tag_bind(id, "<1>", self.select) + self.canvas.tag_bind(id, "", self.flip) + x0, y0, x1, y1 = self.canvas.bbox(id) + textx = max(x1, 200) + 10 + if text==None: + text = self.item.GetText() or "" + try: + self.entry + except AttributeError: + pass + else: + self.edit_finish() + try: + label = self.label + except AttributeError: + # padding carefully selected (on Windows) to match Entry widget: + self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2) + if self.selected: + self.label.configure(fg="white", bg="darkblue") + else: + fg = self.item.GetTextFg() + self.label.configure(fg=fg, bg="white") + id = self.canvas.create_window(textx, texty, + anchor="nw", window=self.label) + self.label.bind("<1>", self.select_or_edit) + self.label.bind("", self.flip) + self.label.bind("<3>", self.popupMenu) + # Update text if necessary + if text != self.label['text']: + self.label['text'] = text + self.text_id = id + + def select_or_edit(self, event=None): + if self.selected and self.item.IsEditable(): + text = self.item.GetTextForEdit() + self.label['text'] = text + self.drawtext(text) + self.edit(event) + else: + self.select(event) + + def edit(self, event=None): + self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0) + self.entry.insert(0, self.label['text']) + self.entry.selection_range(0, END) + self.entry.pack(ipadx=5) + self.entry.focus_set() + self.entry.bind("", self.edit_finish) + self.entry.bind("", self.edit_cancel) + + def edit_finish(self, event=None): + try: + entry = self.entry + del self.entry + except AttributeError: + return + text = entry.get() + entry.destroy() + if text and text != self.item.GetText(): + self.item.SetText(text) + text = self.item.GetText() + self.label['text'] = text + self.drawtext() + self.canvas.focus_set() + + def edit_cancel(self, event=None): + self.drawtext() + self.canvas.focus_set() + + def find(self, searchKey): + # Search for a node who's key matches the given key + # Is it this node + if searchKey == self.item.GetKey(): + return self + # Nope, check the children + sublist = self.item._GetSubList() + for item in sublist: + key = item.GetKey() + # Use existing child or create new TreeNode if none exists + if self.children.has_key(key): + child = self.children[key] + else: + child = TreeNode(self.canvas, self, item, self.menuList) + # Update local list of children and keys + self.children[key] = child + self.kidKeys.append(key) + # See if node is child (or one of child's descendants) + retVal = child.find(searchKey) + if retVal: + return retVal + # Not here + return None + +class TreeItem: + + """Abstract class representing tree items. + + Methods should typically be overridden, otherwise a default action + is used. + + """ + + def __init__(self): + """Constructor. Do whatever you need to do.""" + + def GetText(self): + """Return text string to display.""" + + def GetTextFg(self): + return "black" + + def GetLabelText(self): + """Return label text string to display in front of text (if any).""" + + def IsExpandable(self): + """Return whether there are subitems.""" + return 1 + + def _GetSubList(self): + """Do not override! Called by TreeNode.""" + if not self.IsExpandable(): + return [] + sublist = self.GetSubList() + return sublist + + def IsEditable(self): + """Return whether the item's text may be edited.""" + + def SetText(self, text): + """Change the item's text (if it is editable).""" + + def GetIconName(self): + """Return name of icon to be displayed normally.""" + + def GetSelectedIconName(self): + """Return name of icon to be displayed when selected.""" + + def GetSubList(self): + """Return list of items forming sublist.""" + + def OnDoubleClick(self): + """Called on a double-click on the item.""" + + def OnSelect(self): + """Called when item selected.""" + + def GetTextForEdit(self): + """Called before editting the item.""" + + + diff --git a/dtool/src/parser-inc/MainHelix.h b/dtool/src/parser-inc/MainHelix.h index e69de29bb2..deeefca994 100644 --- a/dtool/src/parser-inc/MainHelix.h +++ b/dtool/src/parser-inc/MainHelix.h @@ -0,0 +1,9 @@ +#ifndef MAINHELIX_H +#define MAINHELIX_H + +// This file is a stub header file. +class DLLAccess {}; +class IHXClientEngine {}; +class IHXPlayer {}; + +#endif diff --git a/dtool/src/parser-inc/dllpath.h b/dtool/src/parser-inc/dllpath.h index e69de29bb2..127db5b8e2 100644 --- a/dtool/src/parser-inc/dllpath.h +++ b/dtool/src/parser-inc/dllpath.h @@ -0,0 +1,6 @@ +#ifndef DLLPATH_H +#define DLLPATH_H + +// This file is a stub header file. + +#endif diff --git a/dtool/src/parser-inc/hxcom.h b/dtool/src/parser-inc/hxcom.h index e69de29bb2..8c733fddd3 100644 --- a/dtool/src/parser-inc/hxcom.h +++ b/dtool/src/parser-inc/hxcom.h @@ -0,0 +1,6 @@ +#ifndef HXCOM_H +#define HXCOM_H + +// This file is a stub header file. + +#endif diff --git a/dtool/src/parser-inc/hxcomm.h b/dtool/src/parser-inc/hxcomm.h index e69de29bb2..5e1137c6d7 100644 --- a/dtool/src/parser-inc/hxcomm.h +++ b/dtool/src/parser-inc/hxcomm.h @@ -0,0 +1,6 @@ +#ifndef HXCOMM_H +#define HXCOMM_H + +// This file is a stub header file. + +#endif diff --git a/dtool/src/parser-inc/hxcore.h b/dtool/src/parser-inc/hxcore.h index e69de29bb2..4848d39d34 100644 --- a/dtool/src/parser-inc/hxcore.h +++ b/dtool/src/parser-inc/hxcore.h @@ -0,0 +1,6 @@ +#ifndef HXCORE_H +#define HXCORE_H + +// This file is a stub header file. + +#endif diff --git a/dtool/src/parser-inc/hxengin.h b/dtool/src/parser-inc/hxengin.h index e69de29bb2..0b6467baae 100644 --- a/dtool/src/parser-inc/hxengin.h +++ b/dtool/src/parser-inc/hxengin.h @@ -0,0 +1,6 @@ +#ifndef HXENGIN_H +#define HXENGIN_H + +// This file is a stub header file. + +#endif diff --git a/dtool/src/parser-inc/hxerror.h b/dtool/src/parser-inc/hxerror.h index e69de29bb2..b3cf207ade 100644 --- a/dtool/src/parser-inc/hxerror.h +++ b/dtool/src/parser-inc/hxerror.h @@ -0,0 +1,6 @@ +#ifndef HXERROR_H +#define HXERROR_H + +// This file is a stub header file. + +#endif diff --git a/dtool/src/parser-inc/hxfiles.h b/dtool/src/parser-inc/hxfiles.h index e69de29bb2..4616b8bd11 100644 --- a/dtool/src/parser-inc/hxfiles.h +++ b/dtool/src/parser-inc/hxfiles.h @@ -0,0 +1,6 @@ +#ifndef HXFILES_H +#define HXFILES_H + +// This file is a stub header file. + +#endif diff --git a/dtool/src/parser-inc/hxtbuf.h b/dtool/src/parser-inc/hxtbuf.h index e69de29bb2..12210512a1 100644 --- a/dtool/src/parser-inc/hxtbuf.h +++ b/dtool/src/parser-inc/hxtbuf.h @@ -0,0 +1,6 @@ +#ifndef HXTBUF_H +#define HXTBUF_H + +// This file is a stub header file. + +#endif diff --git a/dtool/src/parser-inc/hxtbuff.h b/dtool/src/parser-inc/hxtbuff.h index e69de29bb2..5ba15806f5 100644 --- a/dtool/src/parser-inc/hxtbuff.h +++ b/dtool/src/parser-inc/hxtbuff.h @@ -0,0 +1,6 @@ +#ifndef HXTBUFF_H +#define HXTBUFF_H + +// This file is a stub header file. + +#endif diff --git a/dtool/src/parser-inc/hxwin.h b/dtool/src/parser-inc/hxwin.h index e69de29bb2..b8601c2a63 100644 --- a/dtool/src/parser-inc/hxwin.h +++ b/dtool/src/parser-inc/hxwin.h @@ -0,0 +1,6 @@ +#ifndef HXWIN_H +#define HXWIN_H + +// This file is a stub header file. + +#endif diff --git a/panda/src/helix/HelixClient.cxx b/panda/src/helix/HelixClient.cxx new file mode 100644 index 0000000000..30333100ee --- /dev/null +++ b/panda/src/helix/HelixClient.cxx @@ -0,0 +1,801 @@ +// Filename: HelixClient.cxx +// Created by: jjtaylor (10Feb04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Panda Header Files +//////////////////////////////////////////////////////////////////// +#include "helixclient.h" + +// Initialize Static Data Members. +TypeHandle HelixClient::_type_handle; + +// Static Members used for the DoEvents Method. These might +// be removed in the future since Panda calls the Win32 API +// Message calls internally. +const int HelixClient::_time_delta = 2000; +const int HelixClient::_sleep_time = 10; +const int HelixClient::_guid_length = 64; + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::HelixClient() +// Access: PUBLISHED +// Purpose: This is the default constructor for creating the +// the wrapper object that encapsulates the Helix Engine. +// The constructor calls the init() which loads the main +// Helix DLL, clntcore.dll. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +HelixClient::HelixClient() +: _engine(0), + _dll_access(0) { + const char * envName = "PLAYER"; + char* envPath = getenv( envName ); + _dll_home = string(string(envPath) + "\\ExtLib\\HelixLib\\."); + init(); +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::~HelixClient() +// Access: PUBLISHED +// Purpose: This is the destructor for the HelixClient proxy class. +// The destructor simply invokes the shutdown(), which +// closes any open players on the engine, successfully +// frees the DLLAccess and engine objects. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +HelixClient::~HelixClient() { + shutdown(); +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::do_event() +// Access: PUBLISHED +// Purpose: This method is was used primarily in the Win32 +// test application to give the Helix Engine the necessary +// time slices to keep it running properly. In addition, +// it has been modified to manually set each texture in +// the _textures map to dirty. This means that the image +// will be loaded once again into memory, essentially +// updating the texture each frame with the new video +// image applied to it. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::do_event() { + // Legacy Win32 API code from the test application. This can + // probably be removed since Panda already calls these methods + // for retreiving and dispatching Win32 messages. + MSG msg; + GetMessage(&msg, NULL, 0, 0); + DispatchMessage(&msg); + + // Simply iterate through the _textures map and set the texture + // dirty so that it will be updated in memory. + TEXTURES::iterator iter; + for(iter = _textures.begin(); iter != _textures.end(); iter++) { + iter->second->mark_dirty(Texture::DF_image); + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::do_events() +// Access: PUBLISHED +// Purpose: This method was used primarily in the Win32 +// test application to give the Helix Engine the necessary +// time slices to keep it running properly. It is still +// used in the HelixClient::begin() to get the engine +// started for playback, but it most likely can be safely +// removed in the future. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::do_events() { + MSG msg; + DWORD start_time, end_time, i; + BOOL sleep = TRUE; + static const int check_interval = 10; + start_time = GetTickCount(); + end_time = start_time + _time_delta; + i = 0; + while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + DispatchMessage(&msg); + if((i % check_interval) == 0) { + if(GetTickCount() > end_time) { + break; + } + ++i; + } + sleep = FALSE; + } + if(sleep) { + Sleep(_sleep_time); + } +} + + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::create_player(...) +// Access: PUBLISHED +// Purpose: This wrapper method encapsulates the creation of a +// player object for the Helix Engine. +//////////////////////////////////////////////////////////////////// +// Params: const string &name - Desired name of the Player +// Texture* tex - Panda Texture that is to be udpated with +// the video that this player will run. +// bool sink_on - Determines with the HxAdviseSink Object +// should print out the presentation stats +// in the command prompt. +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::create_player(const string &name, Texture* tex, bool sink_on) { + // Check to make certain that the engine interface has been + // instantiated, otherwise, players cannot be created! + if (_engine != 0) { + + // For now, check if there is an actual Panda texture present. If not, + // then this player should not be created. + + // NOTE: This portion of the interface must be redesigned as the current + // design forces the user to specify a valid texture. A more suitable + // approach would be to automatically generate a panda texture and allow + // the user to retreive that texture and set it for any piece of geometry + // within the world. + if( tex == 0 ) { + STDOUT("ERROR: HelixClient::CreatePlayer, INVALID Texture Object!\n"); + STDOUT("ERROR: NO PLAYER CREATED!!\n"); + return; + } + + // Initialize necessary Helix pointers. + IHXPlayer* tmp_player = 0; + HxClientContext* context = 0; + IHXErrorSinkControl* controller = 0; + IHXErrorSink* error_sink = 0; + + // Tell the texture object to keep the actual image buffer in memory. If + // the image buffer is deallocated, which is the default in panda, Helix + // and Panda because the engine will try to Blit to an invalid buffer. + tex->set_keep_ram_image(true); + + // Try and create a valid helix player on the engine + if (HXR_OK != _engine->CreatePlayer(tmp_player)) { + STDOUT("ERROR: HelixClient::CreatePlayer, Failed Player Creation!\n"); + } + else { + // Since a player has been successfully instantiated, a context + // for that player must be created. + context = new HxClientContext(_players.size()); + if(context != 0) { + context->AddRef(); + + // Specify a default GUID. Not really necessary, but here for + // convenience if needed later on. + char guid[_guid_length + 1]; + IHXPreferences * pref = 0; + guid[0] = '\0'; + + // Query the Preferences Interface for the player. + tmp_player->QueryInterface(IID_IHXPreferences, (void**)&pref); + + // Send the Texture buffer down into the Context. It will then + // ship the buffer to the site supplier, where the actual site + // and surface are generated. + context->init(tmp_player, pref, guid, sink_on, tex); + tmp_player->SetClientContext(context); + HX_RELEASE(pref); + + // Query the Error Sink Controller + tmp_player->QueryInterface(IID_IHXErrorSinkControl, (void**)&controller); + if(controller != 0) { + context->QueryInterface(IID_IHXErrorSink, (void**)&error_sink); + if(error_sink != 0) { + controller->AddErrorSink(error_sink, HXLOG_EMERG, HXLOG_INFO); + } + HX_RELEASE(error_sink); + error_sink = 0; + } + HX_RELEASE(controller); + controller = 0; + context = 0; + } + + // Create a new map object for the player and its respective texture. + pair player(name, tmp_player); + pair texture(name, tex); + + // Now that the pair has been created, set the tmp_player + // address to 0 to protect against dangling references. + tmp_player = 0; + + // Now, actually insert the pairs into their respective maps. + _players.insert(player); + _textures.insert(texture); + } + } + else { + STDOUT("ERROR: In HelixClient::CreatePlayer, pEngine = NULL"); + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::close_player(...) +// Access: PUBLISHED +// Purpose: This wrapper method encapsulates the closing of a +// player object for the Helix Engine. This method +// determines if a player with the designated name exists +// by searching the _players map. If it does, it properly +// closes the player, deletes it from memory, and removes +// the pair instance from the map as well as its texture +// counter part. +//////////////////////////////////////////////////////////////////// +// Params: const string &name - Desired name of the Player to close. +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::close_player(const string &name) { + if (_engine != 0) { + IUnknown* context = 0; + // Determine if the player is in the map. If so, go about + // removing that player from the engine. + PLAYERS::iterator iter = _players.find(name); + if (iter != _players.end()) { + iter->second->Stop(); + iter->second->GetClientContext(context); + context->Release(); + context = 0; + _engine->ClosePlayer(iter->second); + iter->second->Release(); + iter->second = 0; + // Remove the player its associated texture from their + // respective maps. + _players.erase(name); + _textures.erase(name); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::close_all_players() +// Access: PUBLISHED +// Purpose: This wrapper method encapsulates the closing of every +// player object for the Helix Engine. This method simply +// iterates through the _players map, and invokes the +// the close_player routine to actually close the player. +//////////////////////////////////////////////////////////////////// +// Params: none +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::close_all_players() { + if (_engine != 0) { + if (!_players.empty()) { + IUnknown* context = 0; + PLAYERS::iterator iter; + + // Iterate through the _players map to close each player + // currently associated with the engine. + unsigned int total_players = _players.size(); + for (unsigned int cntr = 0; cntr <= total_players; cntr++) { + close_player(_players.begin()->first); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::get_player_count() +// Access: PUBLISHED +// Purpose: This wrapper method simply returns the current number +// of players that are associated with the Helix Engine. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: Int - the number of players currently associated with +// the Helix Engine. +//////////////////////////////////////////////////////////////////// +int HelixClient::get_player_count() { + if (_engine != 0) { + return _engine->GetPlayerCount(); + } + else { + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::init() +// Access: Private +// Purpose: This member function provides all of the dirty work +// for instantiating a Helix Engine object. First it +// searches for the main Helix DLL, clntcore.dll, and then +// it instantiates the actual engine itself. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: bool - True if the engine is successfully initialized. +//////////////////////////////////////////////////////////////////// +bool HelixClient::init() { + // Local Variable Defination and Initialization + FPRMCREATEENGINE create_engine = 0; + FPRMSETDLLACCESSPATH set_dll_access_path = 0; + // Instantiate a DLLAccess object which will be used to properly + // load the clntcore.dll at runtime. + _dll_access = new DLLAccess(); + + // Attempt to load the clntcore.dll. If this fails, an error message will + // be generated and printed to the console. + STDOUT("HelixClient is looking for the client core at %s\n", _dll_home.c_str()); + if(DLLAccess::DLL_OK != _dll_access->open(string(_dll_home+"\\clntcore.dll").c_str())) { + const char * error_string = 0; + error_string = _dll_access->getErrorString(); + STDERR("HelixClient: %s\n\n", error_string); + + // Print out an explanation of what is wrong. + STDERR("You must tell the player where to find the client core and\n"); + STDERR("all of its supporting DLLs and codecs.\n"); + + // Deallocate the memory from the heap to avoid a memory leak. + delete _dll_access; + _dll_access = 0; + return false; + } + + // Now that the client core has been loaded, retrieve the function pointer + // to the engine and the DLL access path. + create_engine = (FPRMCREATEENGINE) _dll_access->getSymbol("CreateEngine"); + set_dll_access_path = (FPRMSETDLLACCESSPATH) _dll_access->getSymbol("SetDLLAccessPath"); + + // If either function pointer was not retreived, print an error message to + // the console, deallocate the DLLAccess object from memory, and abort the + // initialization of the engine. + if((create_engine == 0) || (set_dll_access_path == 0)) { + STDOUT("---{ERROR: HelixClient::Init, set_dll_access_path}---"); + + delete _dll_access; + _dll_access = 0; + return false; + } + + // Now that the clntcore.dll has been succesfully loaded. Specify where it + // can find all of the accompanying DLLs that are necessary for Helix to + // function. + char paths[256]; + char* path_next_position = paths; + memset(paths, 0, 256); + UINT32 bytes_left = 256; + + char next_path[256]; + memset(next_path, 0, 256); + + // Apply the Common DLL Path to the paths string. For simplicity, + // this is the same path as the clntcore.dll path. + SafeSprintf(next_path, 256, "DT_Common=%s", _dll_home.c_str()); + STDERR("Common DLL path %s\n", next_path ); + UINT32 bytes_to_copy = strlen(next_path) + 1; + if (bytes_to_copy <= bytes_left) + { + memcpy(path_next_position, next_path, bytes_to_copy); + path_next_position += bytes_to_copy; + bytes_left -= bytes_to_copy; + } + + // Apply the Plug-in DLL Path to the paths string. For simplicity, + // this is the same path as the clntcore.dll path. + SafeSprintf(next_path, 256, "DT_Plugins=%s", _dll_home.c_str()); + STDERR("Plugin path %s\n", next_path ); + bytes_to_copy = strlen(next_path) + 1; + + if (bytes_to_copy <= bytes_left) + { + memcpy(path_next_position, next_path, bytes_to_copy); + path_next_position += bytes_to_copy; + bytes_left -= bytes_to_copy; + } + + // Apply the Codecs DLL Path to the paths string. For simplicity, + // this is the same path as the clntcore.dll path. + SafeSprintf(next_path, 256, "DT_Codecs=%s", _dll_home.c_str()); + bytes_to_copy = strlen(next_path) + 1; + if (bytes_to_copy <= bytes_left) + { + memcpy(path_next_position, next_path, bytes_to_copy); + path_next_position += bytes_to_copy; + bytes_left -= bytes_to_copy; + *path_next_position='\0'; + } + STDOUT((char*)paths); + set_dll_access_path((char*)paths); + + // The dll_access_path has been set, so the engine can now be initialized. If + // this fails, then deallocate the DLLAccess object from memory and abort the + // initialization of the engine. + if (HXR_OK != create_engine((IHXClientEngine**) &_engine)) { + STDOUT("---{ERROR: HelixClient::Init, Creating Engine Problem}---"); + + delete _dll_access; + _dll_access = 0; + return false; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::shutdown() +// Access: PUBLISHED +// Purpose: This member function closes all open players before it +// proceeds to close the engine itself. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::shutdown() { + if (_engine != 0) { + // Close All Open Players that are associated with the + // given Helix Engine. + close_all_players(); + + + // Retrieve the Function Pointer for closing the Helix engine. + FPRMCLOSEENGINE close_engine = (FPRMCLOSEENGINE) _dll_access->getSymbol("CloseEngine"); + + // If the function pointer was successfully retrieved, close + // the helix engine. + if (close_engine != 0) { + STDOUT("Closing Helix Engine...\n"); + close_engine(_engine); + _engine = 0; + } + + // Close the link with the clntCore.dll and freeing the DLLAccess + // object from memory to prevent a memory leak on the heap. + STDOUT("Closing the link to clntCore.dll...\n"); + _dll_access->close(); + delete _dll_access; + _dll_access = 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::begin(...) +// Access: PUBLISHED +// Purpose: This wrapper function encapsulates the Helix Player +// Begin method. It first searches for the name of the +// specified player within the _players map. If that +// is found, it calls the IHXPlayer::Begin method. +//////////////////////////////////////////////////////////////////// +// Params: const string &name - Name of the Player to begin playback. +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::begin(const string &name) { + if (_engine != 0) { + + // Search for the specified player in the map. If the player + // is found, begin playback of the media file. + PLAYERS::iterator iter = _players.find(name); + if (iter != _players.end()) { + iter->second->Begin(); + } + + // This is legacy code from the test application, however, it + // was necessary for helix to function since do_events was called + // to give helix the necessary time-slice of the CPU. I think this + // most likely can be removed since Panda calls the necessary + // Win32 API Message calls. + //UINT curr_time = 0; + //UINT32 start_time = GetTickCount(); + //UINT32 end_time = start_time + _time_delta; + //while(1) { + // do_events(); + //curr_time = GetTickCount(); + //if(curr_time >= end_time) + // break; + //} + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::begin_all() +// Access: PUBLISHED +// Purpose: This wrapper function simply calls the IHXPlayer::Begin +// routine for each player currently associated with the +// the engine. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::begin_all() { + if (_engine != 0) { + + // Iterate through the _players map and call the Begin + // routine to start playback of the media. + PLAYERS::iterator iter; + for(iter = _players.begin(); iter != _players.end(); iter++) { + iter->second->Begin(); + } + + // This is legacy code from the test application, however, it + // was necessary for helix to function since do_events was called + // to give helix the necessary time-slice of the CPU. I think this + // most likely can be removed since Panda calls the necessary + // Win32 API Message calls. +// UINT curr_time = 0; +// UINT32 start_time = GetTickCount(); +// UINT32 end_time = start_time + _time_delta; +// while(1) { +// do_events(); +// curr_time = GetTickCount(); +// if (curr_time >= end_time) + // break; +// } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::pause(...) +// Access: PUBLISHED +// Purpose: This wrapper function encapsulates the Helix Player +// Pause method. It first searches for the name of the +// specified player within the _players map. If that +// is found, it calls the IHXPlayer::Pause method. +//////////////////////////////////////////////////////////////////// +// Params: const string &name - Name of the Player to pause playback. +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::pause(const string &name) { + // Search for the specified player in the map. If the player + // is found, pause the playback of the media file. + if (_engine != 0) { + PLAYERS::iterator iter = _players.find(name); + if (iter != _players.end()) { + iter->second->Pause(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::pause_all() +// Access: PUBLISHED +// Purpose: This wrapper function simply calls the IHXPlayer::Pause +// routine for each player currently associated with the +// the engine. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::pause_all() { + if (_engine != 0) { + // Iterate through the _players map and call the Pause + // routine to start playback of the media. + PLAYERS::iterator iter; + for(iter = _players.begin(); iter != _players.end(); iter++) { + iter->second->Pause(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::seek(...) +// Access: PUBLISHED +// Purpose: This wrapper function encapsulates the Helix Player +// Pause method. It first searches for the name of the +// specified player within the _players map. If that +// is found, it calls the IHXPlayer::Pause method. +//////////////////////////////////////////////////////////////////// +// Params: const string &name - Name of the Player to seek to a +// current time in the playback. +// long &time - the time to seek to in the playback. +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::seek_time(const string &name, long time) { + if (_engine != 0) { + // Search for the specified player in the map. If the player + // is found, seek to the current time in the playback of the + // media file. + PLAYERS::iterator iter = _players.find(name); + if (iter != _players.end()) { + iter->second->Seek(time); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::stop(...) +// Access: PUBLISHED +// Purpose: This wrapper function encapsulates the Helix Player +// Stop method. It first searches for the name of the +// specified player within the _players map. If that +// is found, it calls the IHXPlayer::Stop method which +// stops the playback of the media. +//////////////////////////////////////////////////////////////////// +// Params: const string &name - Name of the Player to stop playback. +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::stop(const string &name) { + if (_engine != 0) { + // Search for the specified player in the map. If the player + // is found, stop the playback of the media file. + PLAYERS::iterator iter = _players.find(name); + if (iter != _players.end()) { + iter->second->Stop(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::stop_all() +// Access: PUBLISHED +// Purpose: This wrapper function simply calls the IHXPlayer::Stop +// routine for each player currently associated with the +// the engine. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::stop_all() { + if (_engine != 0) { + // Iterate through the _players map and call the Pause + // routine to stop playback of the media. + PLAYERS::iterator iter; + for(iter = _players.begin(); iter != _players.end(); iter++) { + iter->second->Stop(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::open_url(...) +// Access: PUBLISHED +// Purpose: This wrapper function encapsulates the Helix Player +// OpenURL method. It first searches for the name of the +// specified player within the _players map. If that +// is found, it calls the IHXPlayer::OpenURL method which +// actually opens the specified media format. URLs can +// come in two forms: +// +// Remote File: rtsp://www.realone.com/somevideo.ram +// Local File: file://f:\\smildemohurl.ram +//////////////////////////////////////////////////////////////////// +// Params: const string &name - Name of the Player to open the URL. +// string url - Name of the URL to be opened for playback. +// Return: None +//////////////////////////////////////////////////////////////////// +void HelixClient::open_url(const string &name, string url) { + if (_engine != 0) { + // Determine if a URL has been specified within the string. + // If it hasn't, assume that the request has been made for + // a local file, so insert, file://, at the beginning of the + // string. + if (url.find("://")==string::npos) { + url.insert(0,"file://"); + } + + // Check wheter the player specified exists + // within the map. If it does, call the IHXPlayer::OpenURL(...) + PLAYERS::iterator iter = _players.find(name); + if (iter != _players.end()) { + iter->second->OpenURL(url.c_str()); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::is_live(...) +// Access: PUBLISHED +// Purpose: This wrapper function encapsulates the Helix Player +// IsLive method. It first searches for the name of the +// specified player within the _players map. If that +// is found, it calls the IHXPlayer::IsLive method. This +// indicates whether the player contains a live source +// or not. +//////////////////////////////////////////////////////////////////// +// Params: const string &name - Name of the Player to check. +// Return: None +//////////////////////////////////////////////////////////////////// +bool HelixClient::is_live(const string &name) { + if (_engine != 0) { + // Search for the specified player in the map. If the player + // is found, check to see if it contains a live media source. + PLAYERS::iterator iter = _players.find(name); + if (iter != _players.end()) { + return (iter->second->IsLive()) ? true : false; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::is_done(...) +// Access: PUBLISHED +// Purpose: This wrapper function encapsulates the Helix Player +// IsDone method. It first searches for the name of the +// specified player within the _players map. If that +// is found, it calls the IHXPlayer::IsDone method. This +// indicates whether the player is finished playing the +// the current presentation. +//////////////////////////////////////////////////////////////////// +// Params: const string &name - Name of the Player to check. +// Return: bool - true if the player has finished playback, +// otherwise it is false. +//////////////////////////////////////////////////////////////////// +bool HelixClient::is_done(const string &name) { + if (_engine != 0) { + // Search for the specified player in the map. If the player + // is found, check to see if it has finished playing the + // the current presentation. + PLAYERS::iterator iter = _players.find(name); + if (iter != _players.end()) { + return iter->second->IsDone(); + } + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::are_players_done() +// Access: PUBLISHED +// Purpose: This wrapper function simply calls the IHXPlayer::IsDone +// routine for each player currently associated with the +// the engine. If all of the players have finished with +// their respective presentations, it returns true. +// Otherwise it would return false. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: bool - true if all players finished playback, otherwise +// it is false. +//////////////////////////////////////////////////////////////////// +bool HelixClient::are_players_done() { + if (_engine != 0) { + bool done = true; + PLAYERS::iterator iter; + for(iter = _players.begin(); iter != _players.end(); iter++) { + if(!iter->second->IsDone()) { + done = false; + } + } + return done; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Method: HelixClient::get_source_count() +// Access: PUBLISHED +// Purpose: This wrapper function simply calls the +// IHXPlayer::GetSourceCount routine. This returns the +// number of stream source instances currently used by +// the player. In case of audio, it would be 1. In case +// of a movie file, it would be 2. +//////////////////////////////////////////////////////////////////// +// Params: const string &name - Name of the Player to check. +// Return: Int - value of source count or -1 if there is no player +// or engine. +//////////////////////////////////////////////////////////////////// +int HelixClient::get_source_count(const string &name) { + if (_engine != 0) { + // Search for the specified player in the map. If the player + // is found, check to see if it has finished playing the + // the current presentation. + PLAYERS::iterator iter = _players.find(name); + if (iter != _players.end()) { + return iter->second->GetSourceCount(); + } + } + return -1; +} \ No newline at end of file diff --git a/panda/src/helix/HelixClient.h b/panda/src/helix/HelixClient.h new file mode 100644 index 0000000000..8fd474c2fc --- /dev/null +++ b/panda/src/helix/HelixClient.h @@ -0,0 +1,123 @@ +// Filename: HelixClient.h +// Created by: jjtaylor (10Feb04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// +#ifndef HELIXCLIENT_H +#define HELIXCLIENT_H + +//////////////////////////////////////////////////////////////////// +// Panda Header Files +//////////////////////////////////////////////////////////////////// +#include "pandabase.h" + +#include "typedObject.h" +#include "texture.h" +#include "pmap.h" + +//////////////////////////////////////////////////////////////////// +// Helix Main Header Files +//////////////////////////////////////////////////////////////////// +#include + +//////////////////////////////////////////////////////////////////// +// Normal Header Files +//////////////////////////////////////////////////////////////////// +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class: HelixClient +// Purpose: This proxy class provides the top-level interface for +// loading the Helix Client Engine, creating players on +// the Helix engine, and initializing the media playback. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA HelixClient { +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + register_type(_type_handle, "HelixClient"); + } +private: + static TypeHandle _type_handle; +PUBLISHED: + HelixClient(); + ~HelixClient(); + +//////////////////////////////////////////////////////////////////// +// IHXClientEngine Interface Methods +// Purpose: These are wrapper methods that invoke the actual Helix +// Engine Interface method calls. For instance, the method +// create_player invotes the Helix Engine method +// engine->CreatePlayer(). +//////////////////////////////////////////////////////////////////// + void create_player(const string &name, Texture* tex = 0, bool sink_on = false); + void close_player(const string &name); + void close_all_players(); + int get_player_count(); + +//////////////////////////////////////////////////////////////////// +// IHXPlayer Interface Methods Prototypes +// Purpose: These are wrapper methods that invoke the actual Helix +// Player Interface method calls. For instance, the method +// begin invotes the Helix Engine method player->Begin(). +//////////////////////////////////////////////////////////////////// + void begin(const string &name); + void pause(const string &name); + void seek_time(const string &name, long time); + void stop(const string &name); + + void begin_all(); + void pause_all(); + void stop_all(); + + void open_url(const string &name, string url); + int get_source_count(const string &name); + bool is_done(const string &name); + bool is_live(const string &name); + bool are_players_done(); + +//////////////////////////////////////////////////////////////////// +// Utility Method Prototypes +// Purpose: These methods provide basic utility functions which are +// necessary for the Helix engine to play and close. +//////////////////////////////////////////////////////////////////// + void do_event(); + void do_events(); + void shutdown(); +private: + // Private Member Functions + bool init(); + + // Private Member variables + DLLAccess* _dll_access; + + string _dll_home; + static const int _time_delta; + static const int _sleep_time; + static const int _guid_length; + + // Where the Engine will actually reside. + IHXClientEngine* _engine; + + typedef pmap PLAYERS; + typedef pmap TEXTURES; + + PLAYERS _players; + TEXTURES _textures; +}; +#endif \ No newline at end of file diff --git a/panda/src/helix/HelixDefs.h b/panda/src/helix/HelixDefs.h new file mode 100644 index 0000000000..277df8f58d --- /dev/null +++ b/panda/src/helix/HelixDefs.h @@ -0,0 +1,421 @@ +#ifndef HELIX_CONFIG_MS_PSDK +#define HELIX_CONFIG_MS_PSDK 1 +#endif +#ifndef HELIX_FEATURE_3GPP +#define HELIX_FEATURE_3GPP 1 +#endif +#ifndef HELIX_FEATURE_ADVANCEDGROUPMGR +#define HELIX_FEATURE_ADVANCEDGROUPMGR 1 +#endif +#ifndef HELIX_FEATURE_ALT_URL +#define HELIX_FEATURE_ALT_URL 1 +#endif +#ifndef HELIX_FEATURE_ASM +#define HELIX_FEATURE_ASM 1 +#endif +#ifndef HELIX_FEATURE_AUDIO +#define HELIX_FEATURE_AUDIO 1 +#endif +#ifndef HELIX_FEATURE_AUDIOHOOK +#define HELIX_FEATURE_AUDIOHOOK 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_28_8 +#define HELIX_FEATURE_AUDIO_CODEC_28_8 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_AMRNB +#define HELIX_FEATURE_AUDIO_CODEC_AMRNB 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_AMRWB +#define HELIX_FEATURE_AUDIO_CODEC_AMRWB 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_GECKO +#define HELIX_FEATURE_AUDIO_CODEC_GECKO 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_INTERLEAVE_ALL +#define HELIX_FEATURE_AUDIO_CODEC_INTERLEAVE_ALL 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_O5_6 +#define HELIX_FEATURE_AUDIO_CODEC_O5_6 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_RAAC +#define HELIX_FEATURE_AUDIO_CODEC_RAAC 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_SIPRO +#define HELIX_FEATURE_AUDIO_CODEC_SIPRO 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_CODEC_TOKYO +#define HELIX_FEATURE_AUDIO_CODEC_TOKYO 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_INACCURATESAMPLING +#define HELIX_FEATURE_AUDIO_INACCURATESAMPLING 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_INCOMPLETESAMPLE +#define HELIX_FEATURE_AUDIO_INCOMPLETESAMPLE 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_MPA_LAYER1 +#define HELIX_FEATURE_AUDIO_MPA_LAYER1 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_MPA_LAYER2 +#define HELIX_FEATURE_AUDIO_MPA_LAYER2 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_MPA_LAYER3 +#define HELIX_FEATURE_AUDIO_MPA_LAYER3 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_MPEG4 +#define HELIX_FEATURE_AUDIO_MPEG4 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_MULTIPLAYER_PAUSE +#define HELIX_FEATURE_AUDIO_MULTIPLAYER_PAUSE 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_POSTMIXHOOK +#define HELIX_FEATURE_AUDIO_POSTMIXHOOK 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_PREMIXHOOK +#define HELIX_FEATURE_AUDIO_PREMIXHOOK 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_RALF +#define HELIX_FEATURE_AUDIO_RALF 1 +#endif +#ifndef HELIX_FEATURE_AUDIO_REAL +#define HELIX_FEATURE_AUDIO_REAL 1 +#endif +#ifndef HELIX_FEATURE_AUTHENTICATION +#define HELIX_FEATURE_AUTHENTICATION 1 +#endif +#ifndef HELIX_FEATURE_AUTOUPGRADE +#define HELIX_FEATURE_AUTOUPGRADE 1 +#endif +#ifndef HELIX_FEATURE_BASICGROUPMGR +#define HELIX_FEATURE_BASICGROUPMGR 1 +#endif +#ifndef HELIX_FEATURE_CHUNKRES +#define HELIX_FEATURE_CHUNKRES 1 +#endif +#ifndef HELIX_FEATURE_COOKIES +#define HELIX_FEATURE_COOKIES 1 +#endif +#ifndef HELIX_FEATURE_CORECOMM +#define HELIX_FEATURE_CORECOMM 1 +#endif +#ifndef HELIX_FEATURE_CROSSFADE +#define HELIX_FEATURE_CROSSFADE 1 +#endif +#ifndef HELIX_FEATURE_DBG_LOG +#define HELIX_FEATURE_DBG_LOG 1 +#endif +#ifndef HELIX_FEATURE_DIRECT_SOUND +#define HELIX_FEATURE_DIRECT_SOUND 1 +#endif +#ifndef HELIX_FEATURE_DRM +#define HELIX_FEATURE_DRM 1 +#endif +#ifndef HELIX_FEATURE_DTDR_AUDIO_DECODER +#define HELIX_FEATURE_DTDR_AUDIO_DECODER 1 +#endif +#ifndef HELIX_FEATURE_DTDR_AVSYNC_ADJUSTER +#define HELIX_FEATURE_DTDR_AVSYNC_ADJUSTER 1 +#endif +#ifndef HELIX_FEATURE_DTDR_DECRYPTER +#define HELIX_FEATURE_DTDR_DECRYPTER 1 +#endif +#ifndef HELIX_FEATURE_DTDR_ENCODER +#define HELIX_FEATURE_DTDR_ENCODER 1 +#endif +#ifndef HELIX_FEATURE_EVENTMANAGER +#define HELIX_FEATURE_EVENTMANAGER 1 +#endif +#ifndef HELIX_FEATURE_FIFOCACHE +#define HELIX_FEATURE_FIFOCACHE 1 +#endif +#ifndef HELIX_FEATURE_FILESYSTEMMGR +#define HELIX_FEATURE_FILESYSTEMMGR 1 +#endif +#ifndef HELIX_FEATURE_FRAGMENTBUFFER +#define HELIX_FEATURE_FRAGMENTBUFFER 1 +#endif +#ifndef HELIX_FEATURE_FULLGUID +#define HELIX_FEATURE_FULLGUID 1 +#endif +#ifndef HELIX_FEATURE_GAINTOOL +#define HELIX_FEATURE_GAINTOOL 1 +#endif +#ifndef HELIX_FEATURE_GIF_BROKENIMAGE +#define HELIX_FEATURE_GIF_BROKENIMAGE 1 +#endif +#ifndef HELIX_FEATURE_GROUPMGR +#define HELIX_FEATURE_GROUPMGR 1 +#endif +#ifndef HELIX_FEATURE_HTTPCLOAK +#define HELIX_FEATURE_HTTPCLOAK 1 +#endif +#ifndef HELIX_FEATURE_HTTP_GZIP +#define HELIX_FEATURE_HTTP_GZIP 1 +#endif +#ifndef HELIX_FEATURE_HYPER_NAVIGATE +#define HELIX_FEATURE_HYPER_NAVIGATE 1 +#endif +#ifndef HELIX_FEATURE_IGNORE_SIGPIPE +#define HELIX_FEATURE_IGNORE_SIGPIPE 1 +#endif +#ifndef HELIX_FEATURE_ISMA +#define HELIX_FEATURE_ISMA 1 +#endif +#ifndef HELIX_FEATURE_LIMITER +#define HELIX_FEATURE_LIMITER 1 +#endif +#ifndef HELIX_FEATURE_MASTERTAC +#define HELIX_FEATURE_MASTERTAC 1 +#endif +#ifndef HELIX_FEATURE_MEDIAMARKER +#define HELIX_FEATURE_MEDIAMARKER 1 +#endif +#ifndef HELIX_FEATURE_MEMMAP_IO +#define HELIX_FEATURE_MEMMAP_IO 1 +#endif +#ifndef HELIX_FEATURE_META +#define HELIX_FEATURE_META 1 +#endif +#ifndef HELIX_FEATURE_MIXER +#define HELIX_FEATURE_MIXER 1 +#endif +#ifndef HELIX_FEATURE_MP3FF_LENIENT +#define HELIX_FEATURE_MP3FF_LENIENT 1 +#endif +#ifndef HELIX_FEATURE_MP3FF_ONDEMANDMETAINFO +#define HELIX_FEATURE_MP3FF_ONDEMANDMETAINFO 1 +#endif +#ifndef HELIX_FEATURE_MP3FF_SHOUTCAST +#define HELIX_FEATURE_MP3FF_SHOUTCAST 1 +#endif +#ifndef HELIX_FEATURE_NESTEDMETA +#define HELIX_FEATURE_NESTEDMETA 1 +#endif +#ifndef HELIX_FEATURE_NETCHECK +#define HELIX_FEATURE_NETCHECK 1 +#endif +#ifndef HELIX_FEATURE_NETINTERFACES +#define HELIX_FEATURE_NETINTERFACES 1 +#endif +#ifndef HELIX_FEATURE_NEXTGROUPMGR +#define HELIX_FEATURE_NEXTGROUPMGR 1 +#endif +#ifndef HELIX_FEATURE_OPTIMIZED_SCHEDULER +#define HELIX_FEATURE_OPTIMIZED_SCHEDULER 1 +#endif +#ifndef HELIX_FEATURE_OVERLAYMGR +#define HELIX_FEATURE_OVERLAYMGR 1 +#endif +#ifndef HELIX_FEATURE_PAC +#define HELIX_FEATURE_PAC 1 +#endif +#ifndef HELIX_FEATURE_PACKETHOOKMGR +#define HELIX_FEATURE_PACKETHOOKMGR 1 +#endif +#ifndef HELIX_FEATURE_PLAYBACK_LOCAL +#define HELIX_FEATURE_PLAYBACK_LOCAL 1 +#endif +#ifndef HELIX_FEATURE_PLAYBACK_NET +#define HELIX_FEATURE_PLAYBACK_NET 1 +#endif +#ifndef HELIX_FEATURE_PLAYERNAVIGATOR +#define HELIX_FEATURE_PLAYERNAVIGATOR 1 +#endif +#ifndef HELIX_FEATURE_PLUGINHANDLER2 +#define HELIX_FEATURE_PLUGINHANDLER2 1 +#endif +#ifndef HELIX_FEATURE_PNA +#define HELIX_FEATURE_PNA 1 +#endif +#ifndef HELIX_FEATURE_PREFERENCES +#define HELIX_FEATURE_PREFERENCES 1 +#endif +#ifndef HELIX_FEATURE_PREFETCH +#define HELIX_FEATURE_PREFETCH 1 +#endif +#ifndef HELIX_FEATURE_PROXYMGR +#define HELIX_FEATURE_PROXYMGR 1 +#endif +#ifndef HELIX_FEATURE_RAREND_ADV_PACKET_FEEDER +#define HELIX_FEATURE_RAREND_ADV_PACKET_FEEDER 1 +#endif +#ifndef HELIX_FEATURE_RAREND_BANDWIDTH_LISTER +#define HELIX_FEATURE_RAREND_BANDWIDTH_LISTER 1 +#endif +#ifndef HELIX_FEATURE_RAREND_PREREDSTONE_SUPPORT +#define HELIX_FEATURE_RAREND_PREREDSTONE_SUPPORT 1 +#endif +#ifndef HELIX_FEATURE_RAREND_SURESTREAM +#define HELIX_FEATURE_RAREND_SURESTREAM 1 +#endif +#ifndef HELIX_FEATURE_RDT +#define HELIX_FEATURE_RDT 1 +#endif +#ifndef HELIX_FEATURE_RECORDCONTROL +#define HELIX_FEATURE_RECORDCONTROL 1 +#endif +#ifndef HELIX_FEATURE_REGISTRY +#define HELIX_FEATURE_REGISTRY 1 +#endif +#ifndef HELIX_FEATURE_RESAMPLER +#define HELIX_FEATURE_RESAMPLER 1 +#endif +#ifndef HELIX_FEATURE_RESOURCEMGR +#define HELIX_FEATURE_RESOURCEMGR 1 +#endif +#ifndef HELIX_FEATURE_REVERTER +#define HELIX_FEATURE_REVERTER 1 +#endif +#ifndef HELIX_FEATURE_RMFF_BANDWIDTH_NEGOTIATOR +#define HELIX_FEATURE_RMFF_BANDWIDTH_NEGOTIATOR 1 +#endif +#ifndef HELIX_FEATURE_RMFF_DYNAMICASM +#define HELIX_FEATURE_RMFF_DYNAMICASM 1 +#endif +#ifndef HELIX_FEATURE_RMFF_LEGACYAUDIO +#define HELIX_FEATURE_RMFF_LEGACYAUDIO 1 +#endif +#ifndef HELIX_FEATURE_RMFF_LICENSING +#define HELIX_FEATURE_RMFF_LICENSING 1 +#endif +#ifndef HELIX_FEATURE_RMFF_LIVEINFO +#define HELIX_FEATURE_RMFF_LIVEINFO 1 +#endif +#ifndef HELIX_FEATURE_RMFF_MULTIRATE +#define HELIX_FEATURE_RMFF_MULTIRATE 1 +#endif +#ifndef HELIX_FEATURE_RMFF_ONDEMANDMETAINFO +#define HELIX_FEATURE_RMFF_ONDEMANDMETAINFO 1 +#endif +#ifndef HELIX_FEATURE_RMFF_TIMEOFFSET_FIXUP +#define HELIX_FEATURE_RMFF_TIMEOFFSET_FIXUP 1 +#endif +#ifndef HELIX_FEATURE_RMFF_TIMEOFFSET_HANDLER +#define HELIX_FEATURE_RMFF_TIMEOFFSET_HANDLER 1 +#endif +#ifndef HELIX_FEATURE_RMFF_VIEWSOURCE +#define HELIX_FEATURE_RMFF_VIEWSOURCE 1 +#endif +#ifndef HELIX_FEATURE_RTP +#define HELIX_FEATURE_RTP 1 +#endif +#ifndef HELIX_FEATURE_SECURECONN +#define HELIX_FEATURE_SECURECONN 1 +#endif +#ifndef HELIX_FEATURE_SETSRCPROPS +#define HELIX_FEATURE_SETSRCPROPS 1 +#endif +#ifndef HELIX_FEATURE_SINKCONTROL +#define HELIX_FEATURE_SINKCONTROL 1 +#endif +#ifndef HELIX_FEATURE_SMARTERNETWORK +#define HELIX_FEATURE_SMARTERNETWORK 1 +#endif +#ifndef HELIX_FEATURE_SMIL1 +#define HELIX_FEATURE_SMIL1 1 +#endif +#ifndef HELIX_FEATURE_SMIL2 +#define HELIX_FEATURE_SMIL2 1 +#endif +#ifndef HELIX_FEATURE_SMIL2_ANIMATION +#define HELIX_FEATURE_SMIL2_ANIMATION 1 +#endif +#ifndef HELIX_FEATURE_SMIL2_BRUSH +#define HELIX_FEATURE_SMIL2_BRUSH 1 +#endif +#ifndef HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT +#define HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT 1 +#endif +#ifndef HELIX_FEATURE_SMIL2_TRANSITIONS +#define HELIX_FEATURE_SMIL2_TRANSITIONS 1 +#endif +#ifndef HELIX_FEATURE_SMIL2_VALIDATION +#define HELIX_FEATURE_SMIL2_VALIDATION 1 +#endif +#ifndef HELIX_FEATURE_SMIL_REPEAT +#define HELIX_FEATURE_SMIL_REPEAT 1 +#endif +#ifndef HELIX_FEATURE_SMIL_SOUNDLEVEL +#define HELIX_FEATURE_SMIL_SOUNDLEVEL 1 +#endif +#ifndef HELIX_FEATURE_STATS +#define HELIX_FEATURE_STATS 1 +#endif +#ifndef HELIX_FEATURE_SYSTEMREQUIRED +#define HELIX_FEATURE_SYSTEMREQUIRED 1 +#endif +#ifndef HELIX_FEATURE_THREADSAFE_MEMMAP_IO +#define HELIX_FEATURE_THREADSAFE_MEMMAP_IO 1 +#endif +#ifndef HELIX_FEATURE_TIMEDTEXT +#define HELIX_FEATURE_TIMEDTEXT 1 +#endif +#ifndef HELIX_FEATURE_TRANSPORT_MULTICAST +#define HELIX_FEATURE_TRANSPORT_MULTICAST 1 +#endif +#ifndef HELIX_FEATURE_TURBOPLAY +#define HELIX_FEATURE_TURBOPLAY 1 +#endif +#ifndef HELIX_FEATURE_VIDEO +#define HELIX_FEATURE_VIDEO 1 +#endif +#ifndef HELIX_FEATURE_VIDEO_CODEC_RV20 +#define HELIX_FEATURE_VIDEO_CODEC_RV20 1 +#endif +#ifndef HELIX_FEATURE_VIDEO_CODEC_RV30 +#define HELIX_FEATURE_VIDEO_CODEC_RV30 1 +#endif +#ifndef HELIX_FEATURE_VIDEO_CODEC_RV40 +#define HELIX_FEATURE_VIDEO_CODEC_RV40 1 +#endif +#ifndef HELIX_FEATURE_VIDEO_H263 +#define HELIX_FEATURE_VIDEO_H263 1 +#endif +#ifndef HELIX_FEATURE_VIDEO_MPEG4 +#define HELIX_FEATURE_VIDEO_MPEG4 1 +#endif +#ifndef HELIX_FEATURE_VIDEO_REAL +#define HELIX_FEATURE_VIDEO_REAL 1 +#endif +#ifndef HELIX_FEATURE_VIDREND_OPTIMIZEDVIDEO +#define HELIX_FEATURE_VIDREND_OPTIMIZEDVIDEO 1 +#endif +#ifndef HELIX_FEATURE_VIDREND_SYNCSMOOTHING +#define HELIX_FEATURE_VIDREND_SYNCSMOOTHING 1 +#endif +#ifndef HELIX_FEATURE_VIEWPORT +#define HELIX_FEATURE_VIEWPORT 1 +#endif +#ifndef HELIX_FEATURE_VIEWSOURCE +#define HELIX_FEATURE_VIEWSOURCE 1 +#endif +#ifndef HELIX_FEATURE_VOLUME +#define HELIX_FEATURE_VOLUME 1 +#endif +#ifndef HELIX_FEATURE_XMLPARSER +#define HELIX_FEATURE_XMLPARSER 1 +#endif +#ifndef HELIX_FEATURE_PANDA3D +#define HELIX_FEATURE_PANDA3D 1 +#endif +#ifndef STRICT +#define STRICT 1 +#endif +#ifndef THREADS_SUPPORTED +#define THREADS_SUPPORTED 1 +#endif +#ifndef WIN32 +#define WIN32 1 +#endif +#ifndef _LITTLE_ENDIAN +#define _LITTLE_ENDIAN 1 +#endif +#ifndef _M_IX86 +#define _M_IX86 1 +#endif +#ifndef _WIN32 +#define _WIN32 1 +#endif +#ifndef _WINDOWS +#define _WINDOWS 1 +#endif + diff --git a/panda/src/helix/HxAdviseSink.cxx b/panda/src/helix/HxAdviseSink.cxx new file mode 100644 index 0000000000..1ff87f3282 --- /dev/null +++ b/panda/src/helix/HxAdviseSink.cxx @@ -0,0 +1,653 @@ +// Filename: HxAdviseSink.cpp +// Created by: jjtaylor (27Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Panda Header Files +//////////////////////////////////////////////////////////////////// +#include "HxAdviseSink.h" + +//////////////////////////////////////////////////////////////////// +// Normal Header Files +//////////////////////////////////////////////////////////////////// +#include + +//////////////////////////////////////////////////////////////////// +// Method: HxAdviseSink::Constructor +// Access: Public +// Purpose: Initializes member variables and sets up object +// related interfaces. +//////////////////////////////////////////////////////////////////// +// Input: pUnknown - Pointer to an IUnknown Interface +// client_index - Client Instance Identifier +// Output: None +//////////////////////////////////////////////////////////////////// +HxAdviseSink::HxAdviseSink(IUnknown* unknown, LONG32 client_index, bool sink_on) + : _ref_count (0), + _client_index (client_index), + _unknown (0), + _registry (0), + _scheduler (0), + _current_bandwidth(0), + _average_bandwidth(0), + _on_stop(0), + _sink_on(sink_on) +{ + if (unknown != 0) { + _unknown = unknown; + _unknown->AddRef(); + + if (HXR_OK != _unknown->QueryInterface(IID_IHXRegistry, (void**)&_registry)) { + _registry = 0; + } + + if (HXR_OK != _unknown->QueryInterface(IID_IHXScheduler, (void**)&_scheduler)) { + _scheduler = 0; + } + + IHXPlayer* player; + if(HXR_OK == _unknown->QueryInterface(IID_IHXPlayer, (void**)&player)) { + player->AddAdviseSink(this); + player->Release(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::~HxClientAdviseSink +// Access: Private +// Purpose: Class destructor which releases corresponding +// interfaces which are related to this class. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +HxAdviseSink::~HxAdviseSink() { + // Release components and set the member variables to the + // NULL-state. + if (_scheduler != 0) { + _scheduler->Release(); + _scheduler = 0; + } + + if (_registry != 0) { + _registry->Release(); + _registry = 0; + } + + if (_unknown != 0) { + _unknown->Release(); + _unknown = 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::QueryInterface +// Access: Public +// Purpose: Queries this class to determine whether it supports +// supports a specific interface. If the call succeeds +// then the methods of that interface are made +// available for use. +//////////////////////////////////////////////////////////////////// +// Params: id - Indicates the reference identifier of the +// the interface being queried. +// interface_obj - Points to an interface pointer that is +// filled in if the query succeeds. +// Return: HX_RESULT - Varies based on wheter the interface is +// supported or not. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::QueryInterface(REFIID id, void** interface_obj) { + // Determine if the IUnknown and ClientAdviseSink interfaces + // are supported. + if (IsEqualIID(id, IID_IUnknown)) { + // Increase the reference count, set the Interface Object, + // and return that the interface is supported within this + // object. + AddRef(); + *interface_obj = (IUnknown*)(IHXClientAdviseSink*)this; + } + else if (IsEqualIID(id, IID_IHXClientAdviseSink)) { + // Same as above. + AddRef(); + *interface_obj = (IHXClientAdviseSink*)this; + } + else { + // This Interface is not supported by this object. Set the + // Interface Object to the NULL-state and return. + *interface_obj = 0; + return HXR_NOINTERFACE; + } + return HXR_OK; +} + + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::AddRef +// Access: Public +// Purpose: Increases the classes reference count by one. +// Whenever an object is created, it's reference count +// begins at 1. If an application calls IUnknown::AddRef, +// queries an interface belonging to a specific object, +// or uses a creation function like HXCreateInstance, +// the reference count is incremented by 1. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: Returns the new reference count. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP_(ULONG32) HxAdviseSink::AddRef() { + return InterlockedIncrement(&_ref_count); +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::Release +// Access: Public +// Purpose: Decreases the object's reference count by one. +// Every call to IUnknown::AddRef, +// IUnknown::QueryInterface, or a creation function +// such as HXCreateInstance must have a corresponding +// call to IUnknown::Release. When the reference count +// reaches 0 (zero), the object is destroyed. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: Returns the new reference count. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP_(ULONG32) HxAdviseSink::Release() { + // As long as the reference count is greater than 0, then this + // object is still in "scope." + if (InterlockedDecrement(&_ref_count) > 0 ) { + return _ref_count; + } + + // Otherwise, this object is no longer necessary and should be + // removed from memory. + delete this; + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnPosLength +// Access: Public +// Purpose: Advises the client that the position or length of +// the current playback context has changed +//////////////////////////////////////////////////////////////////// +// Params: ulPosition - The new position of the playback. +// ulLength - The new length of the playback. +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnPosLength(ULONG32 ulPosition, ULONG32 ulLength) { + // Initialize Variables + if(_sink_on) { + STDOUT("OnPosLength(%ld, %ld)\n", ulPosition, ulLength); + } + return HXR_OK; +} + + + + + + + + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnPresentationOpened +// Access: Public +// Purpose: Advises the client that a presentation has been +// opened. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnPresentationOpened() { + // For now, check if advise sinking is enabled. If so, print that + // a presenatation has been opened. + if(_sink_on) { + STDOUT("OnPresentationOpened()\n"); + } + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnPresentationClosed() +// Access: Public +// Purpose: Advises the cleint that a presentation has been +// closed. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnPresentationClosed() { + // For now, check if advise sinking is enabled. If so, print that + // a presenatation has been closed. + if(_sink_on) { + STDOUT("OnPresentationClosed()\n"); + } + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Method: HxAdviseSink::get_statistics +// Access: Private +// Purpose: Retrieves the statistic for the particular key that +// is in the Helix Registry Interface. +//////////////////////////////////////////////////////////////////// +// Params: char* registry_key - the key whose value we wish to +// retrieve from the registry. +// Return: None +//////////////////////////////////////////////////////////////////// +void HxAdviseSink::get_statistics(char* registry_key) { + if(_sink_on) { + char RegistryValue[MAX_DISPLAY_NAME] = {0}; + INT32 lValue = 0; + INT32 i = 0; + INT32 lStatistics = 8; + UINT32 *plValue; + + // Collect all of the necessary statistics from the registry + // and print them to the screen. + for (i = 0; i < lStatistics; i++) { + plValue = NULL; + switch (i) { + case 0: + SafeSprintf(RegistryValue, MAX_DISPLAY_NAME, "%s.Normal", registry_key); + break; + case 1: + SafeSprintf(RegistryValue, MAX_DISPLAY_NAME, "%s.Recovered", registry_key); + break; + case 2: + SafeSprintf(RegistryValue, MAX_DISPLAY_NAME, "%s.Received", registry_key); + break; + case 3: + SafeSprintf(RegistryValue, MAX_DISPLAY_NAME, "%s.Lost", registry_key); + break; + case 4: + SafeSprintf(RegistryValue, MAX_DISPLAY_NAME, "%s.Late", registry_key); + break; + case 5: + SafeSprintf(RegistryValue, MAX_DISPLAY_NAME, "%s.ClipBandwidth", registry_key); + break; + case 6: + SafeSprintf(RegistryValue, MAX_DISPLAY_NAME, "%s._average_bandwidth", registry_key); + plValue = &_average_bandwidth; + break; + case 7: + SafeSprintf(RegistryValue, MAX_DISPLAY_NAME, "%s._current_bandwidth", registry_key); + plValue = &_current_bandwidth; + break; + default: + break; + } + + _registry->GetIntByName(RegistryValue, lValue); + if (plValue) { + if (_on_stop || lValue == 0) { + lValue = *plValue; + } + else { + *plValue = lValue; + } + } + STDOUT("%s = %ld\n", RegistryValue, lValue); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::get_all_statistics +// Access: Private +// Purpose: Displays the content of the entire registry. Not +// truly necessary for Panda, however, it is useful +// for debugging purposes of streaming files. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +void HxAdviseSink::get_all_statistics() { + if(_sink_on) { + UINT32 PlayerIndex = 0; + UINT32 SourceIndex = 0; + UINT32 StreamIndex = 0; + + char* RegistryPrefix = "Statistics"; + char RegistryName[MAX_DISPLAY_NAME] = {0}; + + // Display the content of whole statistic registry + if (_registry) { + // Start from the first player. + SafeSprintf(RegistryName, MAX_DISPLAY_NAME, "%s.Player%ld", RegistryPrefix, _client_index); + if (PT_COMPOSITE == _registry->GetTypeByName(RegistryName)) { + // Display player statistic + get_statistics(RegistryName); + + SafeSprintf(RegistryName, MAX_DISPLAY_NAME, "%s.Source%ld", RegistryName, SourceIndex); + while (PT_COMPOSITE == _registry->GetTypeByName(RegistryName)) { + // Display source statistic + get_statistics(RegistryName); + + + SafeSprintf(RegistryName, MAX_DISPLAY_NAME, "%s.Stream%ld", RegistryName, StreamIndex); + while (PT_COMPOSITE == _registry->GetTypeByName(RegistryName)) { + // Display stream statistic + get_statistics(RegistryName); + + StreamIndex++; + + SafeSprintf(RegistryName, MAX_DISPLAY_NAME, "%s.Player%ld.Source%ld.Stream%ld", + RegistryPrefix, PlayerIndex, SourceIndex, StreamIndex); + } + SourceIndex++; + SafeSprintf(RegistryName, MAX_DISPLAY_NAME, "%s.Player%ld.Source%ld", + RegistryPrefix, PlayerIndex, SourceIndex); + } + PlayerIndex++; + SafeSprintf(RegistryName, MAX_DISPLAY_NAME, "%s.Player%ld", + RegistryPrefix, PlayerIndex); + } + } + } +} +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnStatisticsChanged +// Access: Public +// Purpose: Advises the client that the presentation statistics +// have changed and prints those statistics out. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnStatisticsChanged() { + if(_sink_on) { + char Buff[1024]; + HX_RESULT res = HXR_OK; + UINT16 Player = 0; + + STDOUT("OnStatisticsChanged():\n"); + + SafeSprintf(Buff, 1024, "Statistics.Player%u", Player); + while( HXR_OK == res ) { + res = dump_reg_tree( Buff ); + Player++; + SafeSprintf(Buff, 1024, "Statistics.Player%u", Player ); + } + } + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::DumpRegTree +// Access: Private +// Purpose: Prints the contents of the Registry tree for this +// particular object. +//////////////////////////////////////////////////////////////////// +// Params: pszTreeName - The name of the Registry tree that should +// be printed. +// Return: HX_RESULT - result varies. +//////////////////////////////////////////////////////////////////// +HX_RESULT HxAdviseSink::dump_reg_tree(const char* tree_name) { + //Initialize Local Variables related to the Registry Tree + const char* pszName = NULL; + ULONG32 ulRegID = 0; + HX_RESULT res = HXR_OK; + INT32 nVal = 0; + IHXBuffer* pBuff = NULL; + IHXValues* pValues = NULL; + + + // Check if the name exists in the registry. If its not + // return a failure. + res = _registry->GetPropListByName(tree_name, pValues); + if (HXR_OK!=res || !pValues) { + return HXR_FAIL; + } + + // Make sure this is a PT_COMPOSITE type registry entry. If + // its not, return a failure. + if (PT_COMPOSITE != _registry->GetTypeByName(tree_name)) { + return HXR_FAIL; + } + + // Print out the value of each member of this tree. + res = pValues->GetFirstPropertyULONG32( pszName, ulRegID ); + while( HXR_OK == res ) { + // There is at least one entry in the Registry. Now + // check the type. + HXPropType pt = _registry->GetTypeById(ulRegID); + switch(pt) { + case PT_COMPOSITE: + dump_reg_tree(pszName); + break; + case PT_INTEGER: + nVal = 0; + _registry->GetIntById( ulRegID, nVal ); + STDOUT("%s : %d\n", pszName, nVal ); + break; + case PT_INTREF : + nVal = 0; + _registry->GetIntById( ulRegID, nVal ); + STDOUT("%s : %d\n", pszName, nVal ); + break; + case PT_STRING : + pBuff = NULL; + _registry->GetStrById( ulRegID, pBuff ); + STDOUT("%s : \"", pszName ); + + if( pBuff ) { + STDOUT("%s", (const char *)(pBuff->GetBuffer()) ); + } + STDOUT("\"\n" ); + HX_RELEASE(pBuff); + break; + case PT_BUFFER : + STDOUT("%s : BUFFER TYPE NOT SHOWN\n", pszName, nVal ); + break; + case PT_UNKNOWN: + STDOUT("%s Unkown registry type entry\n", pszName ); + break; + default: + STDOUT("%s Unkown registry type entry\n", pszName ); + break; + } + res = pValues->GetNextPropertyULONG32(pszName, ulRegID); + } + + HX_RELEASE( pValues ); + return HXR_OK; +} + + + + + + + + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnPreSeek +// Access: Public +// Purpose: Informs the client that a seek is about to occur. +// The client is informed of the last time in the +// stream's time line before the seek, as well as the +// first new time in the stream's time line after the +// seek is completed. +//////////////////////////////////////////////////////////////////// +// Params: ulOldTime - The end of the stream's time line before the +// current seek. +// ulNewTime - The beginning of the stream's time line after +// the current seek. +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnPreSeek(ULONG32 ulOldTime, ULONG32 ulNewTime) { + // For now, check if advise sinking is enabled. If so, print that + // a seek is about to happen. + if(_sink_on) { + STDOUT("OnPreSeek(%ld, %ld)\n", ulOldTime, ulNewTime); + } + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnPostSeek +// Access: Public +// Purpose: Informs the client that a seek has just occured. +// The client is informed of the last time in the +// stream's time line before the seek, as well as the +// first new time in the stream's time line after the +// seek is completed. +//////////////////////////////////////////////////////////////////// +// Params: ulOldTime - The end of the stream's time line before the +// current seek. +// ulNewTime - The beginning of the stream's time line after +// the current seek. +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnPostSeek(ULONG32 ulOldTime, ULONG32 ulNewTime) { + // For now, check if advise sinking is enabled. If so, print that + // a seek just happened. + if(_sink_on) { + STDOUT("OnPostSeek(%ld, %ld)\n", ulOldTime, ulNewTime); + } + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnStop +// Access: Public +// Purpose: Informs the client that a stop has just occured +// in a presentation, and calculates the total time +// that a file has been playing. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnStop() { + if(_sink_on) { + HXTimeval curr_time; + + STDOUT("OnStop()\n"); + + STDOUT("Player %ld stopped.\n", _client_index); + _on_stop = TRUE; + get_all_statistics(); + + // Retrieve the current time and subtract the beginning time to figure out + // how long the file has played. + curr_time = _scheduler->GetCurrentSchedulerTime(); + _stop_time = curr_time.tv_sec; + } + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnPause +// Access: Public +// Purpose: Informs the client that a pause has just occured. +//////////////////////////////////////////////////////////////////// +// Params: time - time in the stream's time line that the pause +// occured. +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnPause(ULONG32 time) { + // For now, check if advise sinking is enabled. If so, print that + // the a pause in the presentation has occured for debugging + // purposes. + if(_sink_on) { + STDOUT("OnPause(%ld)\n", time); + } + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnBegin +// Access: Public +// Purpose: Informs the client that a presenentation has begun. +//////////////////////////////////////////////////////////////////// +// Params: time - The time that the presentation has begun. +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnBegin(ULONG32 time) { + if(_sink_on) { + // Local Variable Declarations + HXTimeval curr_time; + + STDOUT("OnBegin(%ld)\n", time); + STDOUT("Player %ld beginning playback...\n", _client_index); + + // Record the current time that this begins so that the number + // of seconds the media file has been playing can be calculated. + curr_time = _scheduler->GetCurrentSchedulerTime(); + _start_time = curr_time.tv_sec; + } + return HXR_OK; +} + + + + + + + + + + + + + + + + + + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnBuffering +// Access: Public +// Purpose: Informs the client that buffering of data is occuring. +// The render is informed of the reason for the buffering +// (start-up of stream, seek has occured, network congestion, +// etc.), as well as percentage complete of the buffering process. +//////////////////////////////////////////////////////////////////// +// Params: flags - The reason for buffering. Can be any of the following: +// BUFFERING_START_UP, BUFFERING_SEEK, +// BUFFERING_CONGESTION, BUFFERING_LIVE_PAUSE +// percent_complete - The percentage fo the buffering that +// has been completed. +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnBuffering(ULONG32 flags, UINT16 percent_complete) { + // For now, check if advise sinking is enabled. If so, print that + // the a presentation is now buffering. + if(_sink_on) { + STDOUT("OnBuffering(%ld, %d)\n", flags, percent_complete); + } + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientAdviseSink::OnContacting +// Access: Public +// Purpose: Informs the client that the engine is contacting +// a host. +//////////////////////////////////////////////////////////////////// +// Params: host_name - Host name that the engine is contacting. +// Return: HXR_OK - Helix Specific result saying things are "Okay". +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAdviseSink::OnContacting(const char* host_name) { + // For now, check if advise sinking is enabled. If so, print that + // the client is contacting a host for streaming. + if(_sink_on) { + STDOUT("OnContacting(\"%s\")\n", host_name); + } + return HXR_OK; +} \ No newline at end of file diff --git a/panda/src/helix/HxAdviseSink.h b/panda/src/helix/HxAdviseSink.h new file mode 100644 index 0000000000..af5cde9137 --- /dev/null +++ b/panda/src/helix/HxAdviseSink.h @@ -0,0 +1,97 @@ +// Filename: HxAdviseSink.h +// Created by: jjtaylor (10Feb04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// +#ifndef HXADVISESINK_H +#define HXADVISESINK_H + +//////////////////////////////////////////////////////////////////// +// Panda Header Files +//////////////////////////////////////////////////////////////////// +#include "pandabase.h" + +//////////////////////////////////////////////////////////////////// +// Helix Main Header Files +//////////////////////////////////////////////////////////////////// +#include "mainHelix.h" + +//////////////////////////////////////////////////////////////////// +// Prototype/Struct/Class Forward Delcarations +//////////////////////////////////////////////////////////////////// +struct IHXClientAdviseSink; +struct IUnknown; +struct IHXRegistry; +struct IHXScheduler; + +//////////////////////////////////////////////////////////////////// +// Class: HxAdviseSink +// Purpose: This is a derived class from the IHXClientAdviseSink +// base interface. This class is meant to receive +// notifications from the client engine about changes in +// or about a presentation's playback status. +//////////////////////////////////////////////////////////////////// +class HxAdviseSink : public IHXClientAdviseSink { + public: + HxAdviseSink(IUnknown* unknown, LONG32 client_index, bool sink_on); + +//////////////////////////////////////////////////////////////////// +// IHUnkown Interface Methods Prototypes +// Purpose: Implements the basic COM interface methods for reference +// coutning and querying of related interfaces. +//////////////////////////////////////////////////////////////////// + STDMETHOD(QueryInterface) (THIS_ REFIID id, void** interface_obj); + STDMETHOD_(ULONG32,AddRef) (THIS); + STDMETHOD_(ULONG32,Release) (THIS); + +//////////////////////////////////////////////////////////////////// +// IHXClientAdviseSink Interface Methods Prototypes +// Purpose: Implements the necessary interface methods that may +// be called during a presentations status. +//////////////////////////////////////////////////////////////////// + STDMETHOD(OnPosLength) (THIS_ UINT32 position, UINT32 length); + STDMETHOD(OnPresentationOpened) (THIS); + STDMETHOD(OnPresentationClosed) (THIS); + STDMETHOD(OnStatisticsChanged) (THIS); + STDMETHOD(OnPreSeek) (THIS_ ULONG32 old_time, ULONG32 new_time); + STDMETHOD(OnPostSeek) (THIS_ ULONG32 old_time, ULONG32 new_time); + STDMETHOD(OnStop) (THIS); + STDMETHOD(OnPause) (THIS_ ULONG32 Time); + STDMETHOD(OnBegin) (THIS_ ULONG32 Time); + STDMETHOD(OnBuffering) (THIS_ ULONG32 flags, UINT16 percent_complete); + STDMETHOD(OnContacting) (THIS_ const char* host_name); + private: + // Private Member Functions + ~HxAdviseSink(); + HX_RESULT dump_reg_tree(const char* tree_name ); + void get_statistics (char* registry_key); + void get_all_statistics (void); + + // Private Member variables + LONG32 _ref_count; + LONG32 _client_index; + UINT32 _start_time; + UINT32 _stop_time; + UINT32 _current_bandwidth; + UINT32 _average_bandwidth; + BOOL _on_stop; + + bool _sink_on; + + IUnknown* _unknown; + IHXRegistry* _registry; + IHXScheduler* _scheduler; +}; +#endif \ No newline at end of file diff --git a/panda/src/helix/HxAuthenticationManager.cxx b/panda/src/helix/HxAuthenticationManager.cxx new file mode 100644 index 0000000000..4d54534344 --- /dev/null +++ b/panda/src/helix/HxAuthenticationManager.cxx @@ -0,0 +1,210 @@ +// Filename: HxAuthenticationManager.cxx +// Created by: jjtaylor (10Feb04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Panda Header Files +//////////////////////////////////////////////////////////////////// +#include "HxAuthenticationManager.h" +#include "print.h" + +//////////////////////////////////////////////////////////////////// +// Normal Header Files +//////////////////////////////////////////////////////////////////// +#include +#include + +//////////////////////////////////////////////////////////////////// +// Member: HxAuthenticationManager::HxAuthenticationManager +// Access: Public +// Purpose: A default constructor which initializes the member +// variables for the class. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +HxAuthenticationManager::HxAuthenticationManager() + : _ref_count(0), + _sent_password(FALSE) { +} + +//////////////////////////////////////////////////////////////////// +// Member: HxAuthenticationManager::Destructor +// Access: Public +// Purpose: The default destructor which removes existing +// COM interfaces that have not been freed from +// memory. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +HxAuthenticationManager::~HxAuthenticationManager() { +} + + + + + + + + + + + + +//////////////////////////////////////////////////////////////////// +// Member: HxAuthenticationManager::QueryInterface +// Access: Public +// Purpose: Queries this class to determine whether it supports +// supports a specific interface. If the call succeeds +// then the methods of that interface are made +// available for use. +//////////////////////////////////////////////////////////////////// +// Params: id - Indicates the reference identifier of the +// the interface being queried. +// ppInterfaceObj - Points to an interface pointer that is +// filled in if the query succeeds. +// Return: HX_RESULT - Varies based on wheter the interface is +// supported or not. +//////////////////////////////////////////////////////////////////// +HX_RESULT HxAuthenticationManager::QueryInterface(THIS_ REFIID id, void** ppInterfaceObj) { + // Determine if the IUnknown and AuthenticationManager interfaces + // are supported. + if (IsEqualIID(id, IID_IUnknown)) { + // Increase the reference count, set the Interface Object, + // and return that the interface is supported within this + // object. + AddRef(); + *ppInterfaceObj = (IUnknown*)(IHXAuthenticationManager *)this; + } + else if (IsEqualIID(id, IID_IHXAuthenticationManager)) { + // Same as above. + AddRef(); + *ppInterfaceObj = (IHXAuthenticationManager *)this; + } + else { + // This Interface is not supported by this object. Set the + // Interface Object to the NULL-state and return. + *ppInterfaceObj = 0; + return HXR_NOINTERFACE; + } + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Member: HxAuthenticationManager::AddRef +// Access: Public +// Purpose: Increases the object's reference count by one. +// Every call to IUnknown::AddRef, +// IUnknown::QueryInterface, or a creation function +// such as HXCreateInstance must have a corresponding +// call to IUnknown::Release. When the reference count +// reaches 0 (zero), the object is destroyed. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: ULONG32 - The new reference count. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP_(ULONG32) HxAuthenticationManager::AddRef(THIS) { + return InterlockedIncrement(&_ref_count); +} + + + + + + + + + + + + + +//////////////////////////////////////////////////////////////////// +// Member: HxAuthenticationManager::Release +// Access: Public +// Purpose: Decreases the object's reference count by one. +// Every call to IUnknown::AddRef, +// IUnknown::QueryInterface, or a creation function +// such as HXCreateInstance must have a corresponding +// call to IUnknown::Release. When the reference count +// reaches 0 (zero), the object is destroyed. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: ULONG32 - The new reference count. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP_(ULONG32) HxAuthenticationManager::Release(THIS) { + // As long as the reference count is greater than 0, then this + // object is still in "scope." + if (InterlockedDecrement(&_ref_count) > 0 ) { + return _ref_count; + } + + // Otherwise, this object is no longer necessary and should be + // removed from memory. + delete this; + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Member: HxAuthenticationManager:HandleAuthenticationRequest +// Access: Public +// Purpose: Retrieves the username and password which is +// needed to access a specific file or URL. +//////////////////////////////////////////////////////////////////// +// Params: pResponse - Pointer to a response interface that +// manages the response of the authentication +// of the username and password. +// Return: HX_RESULT - Based on the authentication process. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxAuthenticationManager::HandleAuthenticationRequest(IHXAuthenticationManagerResponse* pResponse) +{ + char username[1024] = ""; /* Flawfinder: ignore */ + char password[1024] = ""; /* Flawfinder: ignore */ + HX_RESULT res = HXR_FAIL; + + if( !_sent_password ) + { + res = HXR_OK; + + STDOUT("\nSending Username and Password...\n"); + + SafeStrCpy(username, "", 1024); + SafeStrCpy(password, "", 1024); + + //strip trailing whitespace + char* c; + for(c = username + strlen(username) - 1; + c > username && isspace(*c); + c--) + ; + *(c+1) = 0; + + for(c = password + strlen(password) - 1; + c > password && isspace(*c); + c--) + ; + *(c+1) = 0; + + _sent_password = TRUE; + } + if (FAILED(res)) + STDOUT("\nInvalid Username and/or Password.\n"); + + pResponse->AuthenticationRequestDone(res, username, password); + return res; +} \ No newline at end of file diff --git a/panda/src/helix/HxAuthenticationManager.h b/panda/src/helix/HxAuthenticationManager.h new file mode 100644 index 0000000000..8caea077aa --- /dev/null +++ b/panda/src/helix/HxAuthenticationManager.h @@ -0,0 +1,67 @@ +// Filename: hxAuthenticationManager.cxx +// Created by: jjtaylor (28Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// +#if !defined (_HXAUTHENTICATIONMANAGER_H_) +#define _HXAUTHENTICATIONMANAGER_H_ + +//////////////////////////////////////////////////////////////////// +// Panda Header Files +//////////////////////////////////////////////////////////////////// +#include "pandabase.h" + +//////////////////////////////////////////////////////////////////// +// Helix Main Header Files +//////////////////////////////////////////////////////////////////// +#include "mainHelix.h" + +//////////////////////////////////////////////////////////////////// +// Class: HxAuthenticationManager +// Purpose: This is a derived class from the IHXAuthenticationManager +// base interface. This class is meant to gain a user name +// and password for a restricted file. Typically, this +// occurs from a popup box in windows, but it would need +// to be specified in code to use with Panda. +//////////////////////////////////////////////////////////////////// +class HxAuthenticationManager : public IHXAuthenticationManager { + public: + // Public Class Method Declarations + HxAuthenticationManager(); + +//////////////////////////////////////////////////////////////////// +// IHXClientAdviseSink Interface Methods Prototypes +// Purpose: Implements the necessary interface methods that is +// called when a username and password is required. +//////////////////////////////////////////////////////////////////// + STDMETHOD (HandleAuthenticationRequest) (IHXAuthenticationManagerResponse* response); + +//////////////////////////////////////////////////////////////////// +// IHUnkown Interface Methods Prototypes +// Purpose: Implements the basic COM interface methods for reference +// coutning and querying of related interfaces. +//////////////////////////////////////////////////////////////////// + STDMETHOD (QueryInterface) (THIS_ REFIID id, void** ppInterfaceObj); + STDMETHOD_ (UINT32, AddRef) (THIS); + STDMETHOD_ (UINT32, Release) (THIS); + private: + // Private Class Method Declarations + ~HxAuthenticationManager(); + + // Private Class Variable Declarations + INT32 _ref_count; + BOOL _sent_password; +}; +#endif \ No newline at end of file diff --git a/panda/src/helix/HxClientContext.cxx b/panda/src/helix/HxClientContext.cxx new file mode 100644 index 0000000000..78824f2e0d --- /dev/null +++ b/panda/src/helix/HxClientContext.cxx @@ -0,0 +1,327 @@ +// Filename: HxClientContext.h +// Created by: jjtaylor (01Feb04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Helix Main Header Files +//////////////////////////////////////////////////////////////////// +#include "mainHelix.h" + +//////////////////////////////////////////////////////////////////// +// Member: HxClientContext::Constructor +// Access: Public +// Purpose: The constructor simply initializes all of the +// member variables. +//////////////////////////////////////////////////////////////////// +// Params: LONG32 client_index - the player/client count +// Return: None +//////////////////////////////////////////////////////////////////// +HxClientContext::HxClientContext(LONG32 client_index) + : _ref_count(0), + _client_index(client_index), + _client_sink(0), + _error_sink(0), + _auth_mgr(0), + _site_supplier(0), + _default_prefs(0) { +} + +//////////////////////////////////////////////////////////////////// +// Member: HxClientContext::Destructor +// Access: Public +// Purpose: The destructor simply calls the close member +// function. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +HxClientContext::~HxClientContext(){ + close(); +} + + + + + + + + + + + + + + +//////////////////////////////////////////////////////////////////// +// Member: HxClientContext::init +// Access: Public +// Purpose: This member performs all of the dirty work for +// initializing the the client context for a player +// as well as instantiating all of the necessary +// Helix interface objects that must be declared so +// the engine can properly communicate with the player +//////////////////////////////////////////////////////////////////// +// Params: IUnknown* unknown - a Helix Interface Object +// IHXPreferences* prefs - +// char* guid - a GUID, if applicable. +// bool sink_on - True if the AdviseSink should broadcast +// presentation statistics. +// Texture* tex - a Panda Texture Object which will be +// updated with the video each frame. +// Return: None +//////////////////////////////////////////////////////////////////// +void HxClientContext::init(IUnknown* unknown, IHXPreferences* prefs, char* guid, bool sink_on, Texture* tex) { + char * cipher = 0; + + _client_sink = new HxAdviseSink(unknown, _client_index, sink_on); + _error_sink = new HxErrorSink(unknown); + _auth_mgr = new HxAuthenticationManager(); +#if defined(HELIX_FEATURE_VIDEO) + // From this point, we need to create a site supplier object since we are dealing with video. + // In addtion, we must send the Panda Texture buffer down to the site supplier, where it will + // inevitably be sent to the video surface object so that it can be updated. + //cout << "Buffer: " << buffer << endl; + _site_supplier = new HxSiteSupplier(unknown, tex); +#endif + + if (_client_sink != 0) { + _client_sink->AddRef(); + } + + if (_error_sink != 0) { + _error_sink->AddRef(); + } + + if (_auth_mgr != 0) { + _auth_mgr->AddRef(); + } + + if (_site_supplier != 0) { + _site_supplier->AddRef(); + } + + if (prefs != 0) { + _default_prefs = prefs; + _default_prefs->AddRef(); + } + + if (guid && *guid) { + // Encode GUID + cipher = Cipher(guid); + SafeStrCpy(_guid, cipher, 256); + } + else { + _guid[0] = '\0'; + } +} + + + + + + +//////////////////////////////////////////////////////////////////// +// Member: HxClientContext::close +// Access: Public +// Purpose: This method simply releases each of the helix +// interface objects that are associated with the +// player. For instance, the site supplier and the +// error sink. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +void HxClientContext::close() { + HX_RELEASE(_client_sink); + HX_RELEASE(_error_sink); + HX_RELEASE(_auth_mgr); + HX_RELEASE(_site_supplier); + HX_RELEASE(_default_prefs); +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientContext::QueryInterface +// Access: Public +// Purpose: Queries this class to determine whether it supports +// supports a specific interface. If the call succeeds +// then the methods of that interface are made +// available for use. +//////////////////////////////////////////////////////////////////// +// Params: id - Indicates the reference identifier of the +// the interface being queried. +// interface_obj - Points to an interface pointer that is +// filled in if the query succeeds. +// Return: HX_RESULT - Varies based on wheter the interface is +// supported or not. +//////////////////////////////////////////////////////////////////// +HX_RESULT HxClientContext::QueryInterface(THIS_ REFIID id, void** interface_obj) { + HX_RESULT result = HXR_NOINTERFACE; + // Determine if the IUnknown and ClientAdviseSink interfaces + // are supported. + if (IsEqualIID(id, IID_IUnknown)) { + // Increase the reference count, set the Interface Object, + // and return that the interface is supported within this + // object. + AddRef(); + *interface_obj = (IUnknown*)(IHXClientAdviseSink*)this; + result = HXR_OK; + } + else if (IsEqualIID(id, IID_IHXPreferences)) { + // Same as above. + AddRef(); + *interface_obj = (IHXPreferences*)this; + result = HXR_OK; + } + else if ((_client_sink != 0) && + (_client_sink->QueryInterface(id, interface_obj) == HXR_OK)) { + result = HXR_OK; + } + else if ((_error_sink != 0) && + (_error_sink->QueryInterface(id, interface_obj) == HXR_OK)) { + result = HXR_OK; + } + else if ((_auth_mgr != 0) && + (_auth_mgr->QueryInterface(id, interface_obj) == HXR_OK)) { + result = HXR_OK; + } + else if ((_site_supplier != 0) && + (_site_supplier->QueryInterface(id, interface_obj) == HXR_OK)) { + result = HXR_OK; + } + else { + // This Interface is not supported by this object. Set the + // Interface Object to the NULL-state and return. + *interface_obj = 0; + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientContex::AddRef +// Access: Public +// Purpose: Increases the classes reference count by one. +// Whenever an object is created, it's reference count +// begins at 1. If an application calls IUnknown::AddRef, +// queries an interface belonging to a specific object, +// or uses a creation function like HXCreateInstance, +// the reference count is incremented by 1. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: Returns the new reference count. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP_(ULONG32) HxClientContext::AddRef(THIS) { + return InterlockedIncrement(&_ref_count); +} + +//////////////////////////////////////////////////////////////////// +// Method: HxClientContext::Release +// Access: Public +// Purpose: Decreases the object's reference count by one. +// Every call to IUnknown::AddRef, +// IUnknown::QueryInterface, or a creation function +// such as HXCreateInstance must have a corresponding +// call to IUnknown::Release. When the reference count +// reaches 0 (zero), the object is destroyed. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: Returns the new reference count. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP_(ULONG32) HxClientContext::Release(THIS) { + // As long as the reference count is greater than 0, then this + // object is still in "scope." + if (InterlockedDecrement(&_ref_count) > 0 ) { + return _ref_count; + } + + // Otherwise, this object is no longer necessary and should be + // removed from memory. + delete this; + return 0; +} + + + + + + + + + + + + + + + + + + +//////////////////////////////////////////////////////////////////// +// Member: HxClientContext::ReadPref +// Access: Public +// Purpose: This method reads a preference from the client +// registry. +//////////////////////////////////////////////////////////////////// +// Params: const char* pref_key - preference key to add to the +// registry. +// IHXBuffer* buffer - a buffer that manages the value of +// the preference. +// Return: IHXBuffer* buffer - returns a buffer that manages +// the value of the preference. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxClientContext::ReadPref(const char* pref_key, IHXBuffer*& buffer) { + HX_RESULT result = HXR_OK; + char * cipher = 0; + + if ((stricmp(pref_key, CLIENT_GUID_REGNAME) == 0) && + (*_guid != 0 )) { + // Create a buffer + buffer = new CHXBuffer(); + buffer->AddRef(); + + buffer->Set((UCHAR*)_guid, strlen(_guid) + 1); + } + else if (_default_prefs != 0) { + result = _default_prefs->ReadPref(pref_key, buffer); + } + else { + result = HXR_NOTIMPL; + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Member: HxClientContext::WritePref +// Access: Public +// Purpose: This method writes a preference to the helix +// client registry. +//////////////////////////////////////////////////////////////////// +// Params: const char* pref_key - preference key to add to the +// registry. +// IHXBuffer* buffer - a buffer that manages the value of +// the preference. +// Return: IHXBuffer* buffer - returns a buffer that manages +// the value of the preference. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxClientContext::WritePref(const char* pref_key, IHXBuffer *buffer) { + if (_default_prefs != 0) { + return _default_prefs->WritePref(pref_key, buffer); + } + else { + return HXR_OK; + } +} diff --git a/panda/src/helix/HxClientContext.h b/panda/src/helix/HxClientContext.h new file mode 100644 index 0000000000..5da1e487ac --- /dev/null +++ b/panda/src/helix/HxClientContext.h @@ -0,0 +1,88 @@ +// Filename: HxClientContext.h +// Created by: jjtaylor (01Feb04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// +#ifndef HXCLIENTCONTEXT_H +#define HXCLIENTCONTEXT_H + +//////////////////////////////////////////////////////////////////// +// Panda Header Files +//////////////////////////////////////////////////////////////////// +#include "pandabase.h" +#include "texture.h" + +//////////////////////////////////////////////////////////////////// +// Helix Main Header Files +//////////////////////////////////////////////////////////////////// +#include "mainHelix.h" + +//////////////////////////////////////////////////////////////////// +// Prototype/Struct/Class Forward Delcarations +//////////////////////////////////////////////////////////////////// +struct IUnknown; +struct IHXPreferences; +class HxAdviseSink; +class HxErrorSink; +class HxAuthenticationManager; + +//////////////////////////////////////////////////////////////////// +// Class: HxClientContext +// Purpose: This is a derived class from the IHXPreferences +// base interface. This class is meant to set the +// default preferences for a player. It contains the +// necessary interface objects such as the Advise Sink, +// Error Sink, and Site Supplier. +//////////////////////////////////////////////////////////////////// +class HxClientContext : public IHXPreferences { +public: + HxClientContext(LONG32 client_index); + ~HxClientContext(); + void init(IUnknown* unknown, IHXPreferences* prefs, char* guid, bool sink_on, Texture* tex); + void close(); + +//////////////////////////////////////////////////////////////////// +// IHUnkown Interface Methods Prototypes +// Purpose: Implements the basic COM interface methods for reference +// coutning and querying of related interfaces. +//////////////////////////////////////////////////////////////////// + STDMETHOD (QueryInterface) (THIS_ REFIID id, void** interface_obj); + STDMETHOD_ (ULONG32, AddRef) (THIS); + STDMETHOD_ (ULONG32, Release) (THIS); + + + + + +//////////////////////////////////////////////////////////////////// +// IHXPreference Interface Methods Prototypes +// Purpose: Implements the necessary interface methods that are +// called to read or write from the Helix client registry. +//////////////////////////////////////////////////////////////////// + STDMETHOD(ReadPref) (THIS_ const char* pref_key, IHXBuffer*& buffer); + STDMETHOD(WritePref) (THIS_ const char* pref_key, IHXBuffer* buffer); +private: + // Private Class Variable Declarations + LONG32 _ref_count; + LONG32 _client_index; + char _guid[256]; + + HxAdviseSink* _client_sink; + HxErrorSink* _error_sink; + HxAuthenticationManager* _auth_mgr; + HxSiteSupplier * _site_supplier; + IHXPreferences * _default_prefs; +}; +#endif \ No newline at end of file diff --git a/panda/src/helix/HxErrorSink.cxx b/panda/src/helix/HxErrorSink.cxx new file mode 100644 index 0000000000..994c7aaadb --- /dev/null +++ b/panda/src/helix/HxErrorSink.cxx @@ -0,0 +1,229 @@ +// Filename: hxErrorSink.cpp +// Created by: jjtaylor (27Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Panda Header Files +//////////////////////////////////////////////////////////////////// +#include "HxErrorSink.h" +#include "print.h" + +//////////////////////////////////////////////////////////////////// +// Normal Header Files +//////////////////////////////////////////////////////////////////// +#include + +//////////////////////////////////////////////////////////////////// +// Member: HxErrorSink::Constructor +// Access: Public +// Purpose: The default constructor of the class which +// initializes the member variables. +//////////////////////////////////////////////////////////////////// +// Params: IUnknown * - Pointer to an IUnknown COM Interface +// Return: None +//////////////////////////////////////////////////////////////////// +HxErrorSink::HxErrorSink(IUnknown* unknown) + : ref_count(0), + _player(0) +{ + IHXClientEngine* pEngine = 0; + unknown->QueryInterface(IID_IHXClientEngine, (void**)&pEngine ); + if( pEngine != 0 ) + { + IUnknown* pTemp = 0; + pEngine->GetPlayer(0, pTemp); + _player = (IHXPlayer*)pTemp; + } + + HX_RELEASE(pEngine); + HX_ASSERT(_player); +} + +///////////////////////////////////////////////////////////////////// +// Member: HxErrorSink::Destructor +// Access: Private +// Purpose: The default destructor of the class that releases +// the interface member variables from memory. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +HxErrorSink::~HxErrorSink() { + HX_RELEASE(_player); +} + + +//////////////////////////////////////////////////////////////////// +// Member: HxErrorSink::QueryInterface +// Access: +// Purpose: Queries this class to determine whether it supports +// supports a specific interface. If the call succeeds +// then the methods of that interface are made +// available for use. +///////////////////////////////////////////////////////////////////// +// Params: _id - Indicates the reference identifier of the +// the interface being queried. +// _interface_obj - Points to an interface pointer that is +// filled in if the query succeeds. +// Return: HX_RESULT - Varies based on wheter the interface is +// supported or not. +//////////////////////////////////////////////////////////////////// +HX_RESULT HxErrorSink::QueryInterface(REFIID _id, void** _interface_obj) { + // Determine if the IUnknown and ErrorSink interfaces + // are supported. + if (IsEqualIID(_id, IID_IUnknown)) { + // Increase the reference count, set the Interface Object, + // and return that the interface is supported within this + // object. + AddRef(); + *_interface_obj = (IUnknown*)(IHXErrorSink*)this; + } + else if (IsEqualIID(_id, IID_IHXErrorSink)) { + // Same as above. + AddRef(); + *_interface_obj = (IHXErrorSink*)this; + } + else { + // This Interface is not supported by this object. Set the + // Interface Object to the NULL-state and return. + *_interface_obj = 0; + return HXR_NOINTERFACE; + } + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Member: HxErrorSink::AddRef +// Access: Public +// Purpose: Increases the object's reference count by one. +// Whenever an object is created, it's reference count +// begins at 1. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: ULONG32 - The new reference count. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP_(ULONG32) HxErrorSink::AddRef() { + return InterlockedIncrement(&ref_count); +} + + + + + + + + + + + + + + + + +//////////////////////////////////////////////////////////////////// +// Member: HxErrorSink::Release +// Access: Public +// Purpose: Decreases the object's reference count by one. +// Every call to IUnknown::AddRef, +// IUnknown::QueryInterface, or a creation function +// such as HXCreateInstance must have a corresponding +// call to IUnknown::Release. When the reference count +// reaches 0 (zero), the object is destroyed. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: ULONG32 - The new reference count. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP_(ULONG32) HxErrorSink::Release() { + // As long as the reference count is greater than 0, then this + // object is still in "scope." + if (InterlockedDecrement(&ref_count) > 0 ) { + return ref_count; + } + + // Otherwise, this object is no longer necessary and should be + // removed from memory. + delete this; + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Member: HxErrorSink::ErrorOccurred +// Access: Public +// Description: Reports an error, event, or status message +//////////////////////////////////////////////////////////////////// +// Params: severity - Severity or level of the error message. +// hx_code - Specific Error Code +// user_code - User-Specific Error Code +// user_string - Pointer to a user-specific string. +// more_info_url - Pointer to a specific more information +// URL string. +// Return: None +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxErrorSink::ErrorOccurred(const UINT8 severity, const UINT32 hx_code, + const UINT32 user_code, const char* user_string, + const char* more_info_url) { + // Initialize Local Variables to the Method. + char HXDefine[256]; + + // Store the error code that was generated. + convert_to_string(hx_code, HXDefine, 256); + + STDOUT("Report(%d, %ld, \"%s\", %ld, \"%s\", \"%s\")\n", severity, + hx_code, (user_string && *user_string) ? user_string : "(NULL)", + user_code, (more_info_url && *more_info_url) ? more_info_url : "(NULL)", + HXDefine); + + return HXR_OK; +} + + + + + + + + + + + + + +//////////////////////////////////////////////////////////////////// +// Member: HxErrorSink::ConvertErrorToText +// Access: Protected +// Purpose: Converts the Helix error code to text for the +// client to display. +//////////////////////////////////////////////////////////////////// +// Params: hx_code - the error code to be translated to text. +// buffer - Text buffer that holds the code. +// buffer_length - The length of the text buffer. +// Return: None +//////////////////////////////////////////////////////////////////// +void HxErrorSink::convert_to_string(const ULONG32 hx_code, char* buffer, UINT32 buffer_length) { + // Initialize Local Variables to this method. + //IHXErrorMessages * pErrMsg = 0; + + // If the buffer is not empty, then there is nothing to do. + if( !buffer ) { + return; + } + buffer[0] = '\0'; + + if (strlen(buffer) == 0) { + SafeSprintf(buffer, buffer_length, "Can't convert the error code %p", hx_code); + } +} \ No newline at end of file diff --git a/panda/src/helix/HxErrorSink.h b/panda/src/helix/HxErrorSink.h new file mode 100644 index 0000000000..617397f8ae --- /dev/null +++ b/panda/src/helix/HxErrorSink.h @@ -0,0 +1,77 @@ +// Filename: hxErrorSink.h +// Created by: jjtaylor (29Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// +#if !defined (_HXERRORSINK_H_) +#define _HXERRORSINK_H_ + +//////////////////////////////////////////////////////////////////// +// Panda Header Files +//////////////////////////////////////////////////////////////////// +#include "pandabase.h" + +//////////////////////////////////////////////////////////////////// +// Helix Main Header Files +//////////////////////////////////////////////////////////////////// +#include "mainHelix.h" + +//////////////////////////////////////////////////////////////////// +// Prototype/Struct/Class Forward Delcarations +//////////////////////////////////////////////////////////////////// +struct IUnknown; +struct IHXErrorMessages; +struct IHXPlayer; + +//////////////////////////////////////////////////////////////////// +// Class: HxErrorSink +// Purpose: This is a derived class from the IHXErrorSink +// base interface. This class is meant to receive +// error messages from the client engine. +//////////////////////////////////////////////////////////////////// +class HxErrorSink : public IHXErrorSink { +public: + HxErrorSink(IUnknown* unknown); + ~HxErrorSink(); + +//////////////////////////////////////////////////////////////////// +// IHXErrorSink Interface Methods Prototypes +// Purpose: Implements the necessary interface methods that may +// be called when an error message is generated. +//////////////////////////////////////////////////////////////////// + STDMETHOD (ErrorOccurred) (THIS_ const UINT8 severity, const UINT32 hx_code, + const UINT32 user_code, const char* user_string, + const char* more_info_url); + +//////////////////////////////////////////////////////////////////// +// IHUnkown Interface Methods Prototypes +// Purpose: Implements the basic COM interface methods for reference +// coutning and querying of related interfaces. +//////////////////////////////////////////////////////////////////// + STDMETHOD (QueryInterface) (THIS_ REFIID _id, void** _interface_obj); + STDMETHOD_ (ULONG32, AddRef) (THIS); + STDMETHOD_ (ULONG32, Release) (THIS); + + + +protected: + // Protected Class Method Declarations + void convert_to_string(const ULONG32 hx_code, char * buffer, UINT32 buffer_length); + + // Protected Class Variable Declarations + INT32 ref_count; + IHXPlayer* _player; +}; +#endif \ No newline at end of file diff --git a/panda/src/helix/HxSiteSupplier.cxx b/panda/src/helix/HxSiteSupplier.cxx new file mode 100644 index 0000000000..30fc0f4446 --- /dev/null +++ b/panda/src/helix/HxSiteSupplier.cxx @@ -0,0 +1,322 @@ +// Filename: hxSiteSupplier.cxx +// Created by: jjtaylor (29Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Helix Main Header Files +//////////////////////////////////////////////////////////////////// +#include "mainHelix.h" + +//////////////////////////////////////////////////////////////////// +// Normal Header Files +//////////////////////////////////////////////////////////////////// +#include + +//////////////////////////////////////////////////////////////////// +// Member: HxSiteSupplier::Constructor +// Access: Public +// Purpose: The default constructor of the class which +// initializes the member variables, and queries +// the appropriate Helix Interfaces. +//////////////////////////////////////////////////////////////////// +// Params: IUnknown * - Pointer to an IUnknown COM Interface +// Texture * tex- Pointer to the Panda texture object. +// Return: None +//////////////////////////////////////////////////////////////////// +HxSiteSupplier::HxSiteSupplier(IUnknown* unknown, Texture* tex) + : _ref_count(0), + _site_manager(0), + _ccf(0), + _unknown(unknown), + _dest_buffer(0), + _texture(tex) { + + if(_unknown) { + _unknown->QueryInterface(IID_IHXSiteManager, (void**)&_site_manager); + _unknown->QueryInterface(IID_IHXCommonClassFactory, (void**)&_ccf); + _unknown->AddRef(); + } +} + +///////////////////////////////////////////////////////////////////// +// Member: HxSiteSupplier:Destructor +// Access: Private +// Purpose: The default destructor of the class that releases +// the interface member variables from memory. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +HxSiteSupplier::~HxSiteSupplier() { + HX_RELEASE(_site_manager); + HX_RELEASE(_ccf); + HX_RELEASE(_unknown); +} +//////////////////////////////////////////////////////////////////// +// Member: HxSiteSupplier::QueryInterface +// Access: +// Purpose: Queries this class to determine whether it supports +// supports a specific interface. If the call succeeds +// then the methods of that interface are made +// available for use. +///////////////////////////////////////////////////////////////////// +// Params: _id - Indicates the reference identifier of the +// the interface being queried. +// _interface_obj - Points to an interface pointer that is +// filled in if the query succeeds. +// Return: HX_RESULT - Varies based on wheter the interface is +// supported or not. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxSiteSupplier::QueryInterface(REFIID _id, void** _interface_obj) { + if (IsEqualIID(_id, IID_IUnknown)) { + AddRef(); + *_interface_obj = (IUnknown*)(IHXSiteSupplier*)this; + return HXR_OK; + } + else if (IsEqualIID(_id, IID_IHXSiteSupplier)) { + AddRef(); + *_interface_obj = (IHXSiteSupplier*)this; + return HXR_OK; + } + *_interface_obj = NULL; + return HXR_NOINTERFACE; +} + +//////////////////////////////////////////////////////////////////// +// Member: HxSiteSupplier::AddRef +// Access: Public +// Purpose: Increases the object's reference count by one. +// Whenever an object is created, it's reference count +// begins at 1. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: ULONG32 - The new reference count. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP_(ULONG32) HxSiteSupplier::AddRef() { + return InterlockedIncrement(&_ref_count); +} + +//////////////////////////////////////////////////////////////////// +// Member: HxSite::Release +// Access: Public +// Purpose: Decreases the object's reference count by one. +// Every call to IUnknown::AddRef, +// IUnknown::QueryInterface, or a creation function +// such as HXCreateInstance must have a corresponding +// call to IUnknown::Release. When the reference count +// reaches 0 (zero), the object is destroyed. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: ULONG32 - The new reference count. +//////////////////////////////////////////////////////////////////// +STDMETHODIMP_(ULONG32)HxSiteSupplier::Release() { + if (InterlockedDecrement(&_ref_count) > 0) { + return _ref_count; + } + delete this; + return 0; +} + + + + +//////////////////////////////////////////////////////////////////// +// Member: HxSiteSupplier::SitesNeeded +// Access: Public +// Purpose: Informs the site supplier that a site with a +// particular set of characteristics is needed. If the +// supplier can fulfill the requestion, it should call +// the site manager and add a new site to it. +//////////////////////////////////////////////////////////////////// +// Params: UINT32 request_id - ID used to map between corresponding +// "sites needed" and "sites not needed" +// calls. +// IHXValues * pProps - The properties of the requested site +// Return: None +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxSiteSupplier::SitesNeeded(UINT32 request_id, IHXValues* pProps) { + // Determine if there are valid properties. If not then a site + // can not properly be created. + if (!pProps) { + return HXR_INVALID_PARAMETER; + } + + // Local Variable Declaration and Intialization + HRESULT hres = HXR_OK; + IHXValues* site_props = NULL; + IHXSiteWindowed* pSiteWindowed = NULL; + IHXBuffer* pValue = NULL; + UINT32 style = 0; + IHXSite* pSite = NULL; + + // Just let the Helix client core create a windowed site for us. + hres = _ccf->CreateInstance(CLSID_IHXSiteWindowed,(void**)&pSiteWindowed); + if (HXR_OK != hres) { + goto exit; + } + + hres = pSiteWindowed->QueryInterface(IID_IHXSite,(void**)&pSite); + if (HXR_OK != hres) { + goto exit; + } + + hres = pSiteWindowed->QueryInterface(IID_IHXValues,(void**)&site_props); + if (HXR_OK != hres) { + goto exit; + } + + // Figures out what type of site must be created. + hres = pProps->GetPropertyCString("playto",pValue); + if (HXR_OK == hres) { + site_props->SetPropertyCString("channel",pValue); + HX_RELEASE(pValue); + } + else { + hres = pProps->GetPropertyCString("name",pValue); + if (HXR_OK == hres) { + site_props->SetPropertyCString("LayoutGroup",pValue); + HX_RELEASE(pValue); + } + } + +#ifdef _WINDOWS + style = WS_OVERLAPPED | WS_VISIBLE | WS_CLIPCHILDREN; +#endif + + // Determine if there is a valid Panda buffer available + // so that it may be sent down into the sites video + // surface object. + if( _texture->has_ram_image() ) { + pSiteWindowed->SetDstBuffer(_texture->_pbuffer->_image, + _texture->_pbuffer->get_xsize(), + _texture->_pbuffer->get_ysize()); + } + else { + cout << "--- {{ HxSiteSupplier.cxx, RELOADED RAM IMAGE!!! }}---" << endl; + PixelBuffer* fake = _texture->get_ram_image(); + if( fake ) { + pSiteWindowed->SetDstBuffer(fake->_image, + _texture->_pbuffer->get_xsize(), + _texture->_pbuffer->get_ysize()); + } + else { + cout << "--- {{ HxSiteSupplier.cxx, NO RAM IMAGE PRESENT!!! }} ---" << endl; + } + } + + // Create the window. Not necessary later on. + hres = pSiteWindowed->Create(NULL, style); + //hres = pSiteWindowed->AttachWindow(NULL); + if (HXR_OK != hres) { + goto exit; + } + + // Add the site to the site manager. + hres = _site_manager->AddSite(pSite); + if (HXR_OK != hres) { + goto exit; + } + + _created_sites.SetAt((void*)request_id,pSite); + //pair site(request_id, pSite); + //_created_sites.insert(site); + + pSite->AddRef(); + + exit: + HX_RELEASE(site_props); + HX_RELEASE(pSiteWindowed); + HX_RELEASE(pSite); + + return hres; +} + +//////////////////////////////////////////////////////////////////// +// Member: HxSiteSupplier::SitesNotNeeded +// Access: Public +// Purpose: Informs the site supplier that all sites from a +// previous site request are no longer needed. +//////////////////////////////////////////////////////////////////// +// Params: UINT32 request_id - ID used to map between corresponding +// "sites needed" and "sites not needed" +// calls. +// Return: None +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxSiteSupplier::SitesNotNeeded(UINT32 request_id) { + // Local Variable Declaration and Initialization + IHXSite* pSite = NULL; + IHXSiteWindowed* pSiteWindowed = NULL; + void* pVoid = NULL; + + // search for the site id in the map + if (!_created_sites.Lookup((void*)request_id,pVoid)) { + return HXR_INVALID_PARAMETER; + } + //SITES::iterator iter = _created_sites.find(request_id); + //if(iter == _created_sites.end()) { + // return HXR_INVALID_PARAMETER; + //} + pSite = (IHXSite*)pVoid; + + // Remove the site from the site manager. + _site_manager->RemoveSite(pSite); + + // Need to actually do the work on destroying the window + // and all that jazz. + pSite->QueryInterface(IID_IHXSiteWindowed,(void**)&pSiteWindowed); + + pSiteWindowed->Destroy(); + + // ref count = 2 + pSiteWindowed->Release(); + + // ref count = 1; deleted from this object's view! + pSite->Release(); + + // Remove the site from the map. + _created_sites.RemoveKey((void*)request_id); + //_created_sites.erase(request_id); + + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Member: HxSiteSupplier::BeginChangeLayout +// Access: Public +// Purpose: Informs the site supplier that a layout change +// has begun and that it can expect to receive calls +// to the SitesNeeded and SitesNotNeeded method. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxSiteSupplier::BeginChangeLayout() { + return HXR_OK; +} + +//////////////////////////////////////////////////////////////////// +// Member: HxSiteSupplier::DoneChangeLayout +// Access: Public +// Purpose: Informs the site supplier that the layout changes +// are completed. +//////////////////////////////////////////////////////////////////// +// Params: None +// Return: None +//////////////////////////////////////////////////////////////////// +STDMETHODIMP HxSiteSupplier::DoneChangeLayout() { + return HXR_OK; +} \ No newline at end of file diff --git a/panda/src/helix/HxSiteSupplier.h b/panda/src/helix/HxSiteSupplier.h new file mode 100644 index 0000000000..ddf8c1e783 --- /dev/null +++ b/panda/src/helix/HxSiteSupplier.h @@ -0,0 +1,67 @@ +// Filename: HxSiteSupplier.h +// Created by: jjtaylor (29Jan04) +// +//////////////////////////////////////////////////////////////////// +// +// +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// +#ifndef HXSITESUPPLIER_H +#define HXSITESUPPLIER_H + +//////////////////////////////////////////////////////////////////// +// Panda Header Files +//////////////////////////////////////////////////////////////////// +#include "pandabase.h" +#include "pmap.h" +#include "texture.h" +#include "fivemmap.h" + +//////////////////////////////////////////////////////////////////// +// Class: HxSiteSupplier +// Purpose: This is a derived class from the IHXSiteSupplier +// base interface. This class is meant to create or +// remove Helix Sites. +//////////////////////////////////////////////////////////////////// +class HxSiteSupplier : public IHXSiteSupplier { +public: + HxSiteSupplier(IUnknown* unknown, Texture* tex); + ~HxSiteSupplier(); + +//////////////////////////////////////////////////////////////////// +// IHUnkown Interface Methods Prototypes +// Purpose: Implements the basic COM interface methods for reference +// coutning and querying of related interfaces. +//////////////////////////////////////////////////////////////////// + STDMETHOD(QueryInterface) (THIS_ REFIID id, void** interface_obj); + STDMETHOD_(ULONG32,AddRef) (THIS); + STDMETHOD_(ULONG32,Release) (THIS); + +//////////////////////////////////////////////////////////////////// +// IHXClientAdviseSink Interface Methods Prototypes +// Purpose: Implements the necessary interface methods that may +// be called when a site must be created or closed. +//////////////////////////////////////////////////////////////////// + STDMETHOD(SitesNeeded) (THIS_ UINT32 request_id, IHXValues* site_props); + STDMETHOD(SitesNotNeeded) (THIS_ UINT32 request_id); + STDMETHOD(BeginChangeLayout) (THIS); + STDMETHOD(DoneChangeLayout) (THIS); +private: + LONG32 _ref_count; + IHXSiteManager* _site_manager; + IHXCommonClassFactory* _ccf; + IUnknown* _unknown; + FiveMinuteMap _created_sites; + UCHAR* _dest_buffer; + Texture* _texture; +}; +#endif \ No newline at end of file diff --git a/panda/src/helix/MainHelix.h b/panda/src/helix/MainHelix.h new file mode 100644 index 0000000000..8a319797ab --- /dev/null +++ b/panda/src/helix/MainHelix.h @@ -0,0 +1,74 @@ +#if !defined (_MAINHELIX_H_) +#define _MAINHELIX_H_ + +// This file basically just contains all of the header +// files that I will need to invoke the client core +// and start an engine. +#include "pandabase.h" +#include "helixDefs.h" + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include "fivemmap.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include "print.h" + +typedef HX_RESULT (HXEXPORT_PTR FPRMSETDLLACCESSPATH) (const char*); + +#endif +/* + +#include "hxtypes.h> + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include "fivemmap.h" +#include +#include +#include */ \ No newline at end of file diff --git a/panda/src/helix/Sources.pp b/panda/src/helix/Sources.pp new file mode 100644 index 0000000000..d86428efa1 --- /dev/null +++ b/panda/src/helix/Sources.pp @@ -0,0 +1,28 @@ +#define BUILD_DIRECTORY $[HAVE_HELIX] + +#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \ + dtoolutil:c dtoolbase:c dtool:m + +#begin lib_target + #define TARGET helix + #define LOCAL_LIBS \ + dgraph + #define WIN_SYS_LIBS $[WIN_SYS_LIBS] user32.lib advapi32.lib winmm.lib + #define USE_PACKAGES helix + + #define SOURCES \ + config_helix.cxx config_helix.h fivemmap.cxx fivemmap.h \ + HelixClient.cxx HelixClient.h HxAdviseSink.cxx HxAdviseSink.h \ + HxAuthenticationManager.cxx HxAuthenticationManager.h \ + HxClientContext.cxx HxClientContext.h HxErrorSink.cxx HxErrorSink.h \ + HxSiteSupplier.cxx HxSiteSupplier.h iids.cxx MainHelix.h print.cxx print.h + + + #define INSTALL_HEADERS \ + config_helix.h \ + HelixClient.h + + #define IGATESCAN \ + HelixClient.cxx + +#end lib_target diff --git a/panda/src/helix/config_helix.cxx b/panda/src/helix/config_helix.cxx new file mode 100644 index 0000000000..6099705ff5 --- /dev/null +++ b/panda/src/helix/config_helix.cxx @@ -0,0 +1,36 @@ +// Filename: config_helix.h +// Created by: jjtaylor (27Feb04) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "config_helix.h" +#include "helixClient.h" + +#include + +DLLAccessPath statClnt; +DLLAccessPath* GetDLLAccessPath() +{ + return &statClnt; +} + +Configure(config_helix); +NotifyCategoryDef(helix, ""); + +ConfigureFn(config_helix) { + HelixClient::init_type(); +} + diff --git a/panda/src/helix/config_helix.h b/panda/src/helix/config_helix.h new file mode 100644 index 0000000000..9d4ce58219 --- /dev/null +++ b/panda/src/helix/config_helix.h @@ -0,0 +1,28 @@ +// Filename: config_helix.h +// Created by: jjtaylor (27Feb04) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_HELIX_H +#define CONFIG_HELIX_H + +#include "pandabase.h" +#include "notifyCategoryProxy.h" + +NotifyCategoryDecl(helix, EXPCL_PANDA, EXPTP_PANDA); + + +#endif diff --git a/panda/src/helix/fivemmap.cxx b/panda/src/helix/fivemmap.cxx new file mode 100644 index 0000000000..acd7cae127 --- /dev/null +++ b/panda/src/helix/fivemmap.cxx @@ -0,0 +1,175 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +#include +//#include "hxtypes.h" +#include "fivemmap.h" + +void* FiveMinuteMap::GetFirstValue() +{ + m_nCursor = 0; + + if (m_nMapSize) + { + return ValueArray[m_nCursor]; + } + else + { + return NULL; + } +} + +void* FiveMinuteMap::GetNextValue() +{ + m_nCursor++; + + if (m_nCursor < m_nMapSize) + { + return ValueArray[m_nCursor]; + } + else + { + return NULL; + } +} + +BOOL FiveMinuteMap::Lookup(void* Key, void*& Value) const +{ + BOOL bFound = FALSE; + int nIndex = 0; + + // If Key is alrady in the list, replace value + for (; nIndex < m_nMapSize; nIndex++) + { + if (KeyArray[nIndex] == Key) + { + Value = ValueArray[nIndex]; + bFound = TRUE; + goto exit; + } + } + +exit: + return bFound; +} + +void FiveMinuteMap::RemoveKey(void* Key) +{ + BOOL bFound = FALSE; + int nIndex = 0; + + // If Key is alrady in the list, replace value + for (; nIndex < m_nMapSize; nIndex++) + { + if (KeyArray[nIndex] == Key) + { + if (nIndex < (m_nMapSize-1)) + { + memmove(&(KeyArray[nIndex]),&(KeyArray[nIndex+1]),sizeof(void*)*(m_nMapSize-(nIndex+1))); + memmove(&(ValueArray[nIndex]),&(ValueArray[nIndex+1]),sizeof(void*)*(m_nMapSize-(nIndex+1))); + } + m_nMapSize--; + goto exit; + } + } + +exit: + (NULL); // We're done! +} + +void FiveMinuteMap::RemoveValue(void* Value) +{ + BOOL bFound = FALSE; + int nIndex = 0; + + // If Value is alrady in the list, replace value + for (; nIndex < m_nMapSize; nIndex++) + { + if (ValueArray[nIndex] == Value) + { + if (nIndex < (m_nMapSize-1)) + { + memmove(&(KeyArray[nIndex]),&(KeyArray[nIndex+1]),sizeof(void*)*(m_nMapSize-(nIndex+1))); + memmove(&(ValueArray[nIndex]),&(ValueArray[nIndex+1]),sizeof(void*)*(m_nMapSize-(nIndex+1))); + } + m_nMapSize--; + goto exit; + } + } + +exit: + (NULL); // We're done! +} + + +void FiveMinuteMap::SetAt(void* Key, void* Value) +{ + int nIndex = 0; + + // If Key is alrady in the list, replace value + for (; nIndex < m_nMapSize; nIndex++) + { + if (KeyArray[nIndex] == Key) + { + ValueArray[nIndex] = Value; + goto exit; + } + } + + // If we have room, add it to the end! + if (m_nAllocSize == m_nMapSize) + { + m_nAllocSize += AllocationSize; + void** pNewKeys = new void*[m_nAllocSize]; + void** pNewValues = new void*[m_nAllocSize]; + + memcpy(pNewKeys,KeyArray,sizeof(void*)*m_nMapSize); /* Flawfinder: ignore */ + memcpy(pNewValues,ValueArray,sizeof(void*)*m_nMapSize); /* Flawfinder: ignore */ + + delete [] KeyArray; + delete [] ValueArray; + + KeyArray = pNewKeys; + ValueArray = pNewValues; + } + + KeyArray[m_nMapSize] = Key; + ValueArray[m_nMapSize] = Value; + m_nMapSize++; + +exit: + (NULL); // We're done! +} + diff --git a/panda/src/helix/fivemmap.h b/panda/src/helix/fivemmap.h new file mode 100644 index 0000000000..576ca6a8ee --- /dev/null +++ b/panda/src/helix/fivemmap.h @@ -0,0 +1,83 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _FIVEMMAP_H_ +#define _FIVEMMAP_H_ + +#include "pandabase.h" +#include "helixDefs.h" + +#include + +#ifndef _HXTYPES_H_ +#error FiveMinuteMap assumes pntypes.h. +#endif + +class FiveMinuteMap { + const int AllocationSize; + + void** KeyArray; + void** ValueArray; + int m_nMapSize; + int m_nAllocSize; + int m_nCursor; +public: + FiveMinuteMap() + : KeyArray(NULL) + , ValueArray(NULL) + , m_nMapSize(0) + , m_nAllocSize(0) + , m_nCursor(0) + , AllocationSize(10) + {}; + + ~FiveMinuteMap() + { + delete [] KeyArray; + delete [] ValueArray; + }; + + int GetCount() {return m_nMapSize;} + + void* GetFirstValue(); + void* GetNextValue(); + + BOOL Lookup(void* Key, void*& Value) const; + void RemoveKey(void* Key); + void RemoveValue(void* Value); + void SetAt(void* Key, void* Value); +}; + +#endif /* _FIVEMMAP_H_ */ diff --git a/panda/src/helix/iids.cxx b/panda/src/helix/iids.cxx new file mode 100644 index 0000000000..36740b81a2 --- /dev/null +++ b/panda/src/helix/iids.cxx @@ -0,0 +1,52 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +// define all guids here once... +#define INITGUID +#define NCIHACK +#include "pandabase.h" +#include "helixDefs.h" +#include "hxtypes.h" +#include "hxcom.h" +#include "hxiids.h" +#include "hxpiids.h" +#if defined _WINDOWS +#if defined (HELIX_FEATURE_DIRECT_SOUND) +#include "dsound.h" +#endif /* HELIX_FEATURE_DIRECT_SOUND */ +#if defined (HELIX_FEATURE_VIDEO) && !defined(_WINCE) +#include "ddraw.h" +#endif /* HELIX_FEATURE_VIDEO */ +#endif diff --git a/panda/src/helix/main.cpp b/panda/src/helix/main.cpp new file mode 100644 index 0000000000..cc1030146a --- /dev/null +++ b/panda/src/helix/main.cpp @@ -0,0 +1,62 @@ +#include "dllpath.h" + +#include "HelixClient.h" + + +// typedef for SetDLLAccessPath + +#if defined _DEBUG || defined DEBUG +#include "debug.h" +#endif + +#if defined(HELIX_CONFIG_NOSTATICS) +# include "globals/hxglobals.h" +#endif + +DLLAccessPath statClnt; +DLLAccessPath* GetDLLAccessPath() +{ + return &statClnt; +} + +int main( int argc, char *argv[] ) +{ + setvbuf(stdout, NULL, _IONBF, 0); + HelixClient* myClient = new HelixClient(); + STDOUT("%s", argv[1]); + myClient->create_player("myPlayer", true); + + //myClient->open_url("myPlayer", argv[1]); + myClient->open_url("myPlayer", "rage.mp3"); + + bool done = true; + myClient->begin("myPlayer"); + while (done != myClient->is_done("myPlayer")) { + //Do Nothing for now + MSG msg; + + GetMessage(&msg, NULL, 0, 0); + DispatchMessage(&msg); + } + + delete myClient; + myClient = 0; + + return 0; +} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/panda/src/helix/print.cxx b/panda/src/helix/print.cxx new file mode 100644 index 0000000000..d17bc1b12f --- /dev/null +++ b/panda/src/helix/print.cxx @@ -0,0 +1,80 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include "print.h" +#ifdef WIN32_PLATFORM_PSPC +#include "hlxosstr.h" +#include +#endif + +int print2stdout(const char* pFmt, ...) +{ + va_list args; + + va_start(args, pFmt); + +#ifdef WIN32_PLATFORM_PSPC + char Message[512]; + int ret = vsprintf(Message, pFmt, args); + OutputDebugString(OS_STRING(Message)); +#else + int ret = vfprintf(stdout, pFmt, args); +#endif + + va_end(args); + + return ret; +} + +int print2stderr(const char* pFmt, ...) +{ + va_list args; + + va_start(args, pFmt); + +#ifdef WIN32_PLATFORM_PSPC + char Message[512]; + int ret = vsprintf(Message, pFmt, args); + OutputDebugString(OS_STRING(Message)); +#else + int ret = vfprintf(stderr, pFmt, args); +#endif + + va_end(args); + + return ret; +} diff --git a/panda/src/helix/print.h b/panda/src/helix/print.h new file mode 100644 index 0000000000..d6fc13526d --- /dev/null +++ b/panda/src/helix/print.h @@ -0,0 +1,62 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: RCSL 1.0/RPSL 1.0 + * + * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. + * + * The contents of this file, and the files included with this file, are + * subject to the current version of the RealNetworks Public Source License + * Version 1.0 (the "RPSL") available at + * http://www.helixcommunity.org/content/rpsl unless you have licensed + * the file under the RealNetworks Community Source License Version 1.0 + * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, + * in which case the RCSL will apply. You may also obtain the license terms + * directly from RealNetworks. You may not use this file except in + * compliance with the RPSL or, if you have a valid RCSL with RealNetworks + * applicable to this file, the RCSL. Please see the applicable RPSL or + * RCSL for the rights, obligations and limitations governing use of the + * contents of the file. + * + * This file is part of the Helix DNA Technology. RealNetworks is the + * developer of the Original Code and owns the copyrights in the portions + * it created. + * + * This file, and the files included with this file, is distributed and made + * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * + * Technology Compatibility Kit Test Suite(s) Location: + * http://www.helixcommunity.org/content/tck + * + * Contributor(s): + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef PRINT_H +#define PRINT_H + +#include "pandabase.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _SYMBIAN +#include "platform/symbian/symbian_print.h" + +#define STDOUT symbianPrint +#define STDERR symbianPrint +#else +int print2stdout(const char* pFmt, ...); +int print2stderr(const char* pFmt, ...); +#define STDOUT print2stdout +#define STDERR print2stderr +#endif + +#ifdef __cplusplus +}; +#endif + + +#endif /* PRINT_H */