Browse Source

Merge remote-tracking branch 'origin/master' into deploy-ng

Mitchell Stokes 7 years ago
parent
commit
49c47164ff
100 changed files with 3245 additions and 1480 deletions
  1. 1 1
      .travis.yml
  2. 1 1
      contrib/src/sceneeditor/MetadataPanel.py
  3. 37 29
      contrib/src/sceneeditor/SideWindow.py
  4. 3 4
      contrib/src/sceneeditor/collisionWindow.py
  5. 186 180
      contrib/src/sceneeditor/controllerWindow.py
  6. 50 48
      contrib/src/sceneeditor/dataHolder.py
  7. 2 2
      contrib/src/sceneeditor/duplicateWindow.py
  8. 32 25
      contrib/src/sceneeditor/lightingPanel.py
  9. 4 5
      contrib/src/sceneeditor/propertyWindow.py
  10. 6 8
      contrib/src/sceneeditor/quad.py
  11. 48 37
      contrib/src/sceneeditor/sceneEditor.py
  12. 9 5
      contrib/src/sceneeditor/seAnimPanel.py
  13. 9 5
      contrib/src/sceneeditor/seBlendAnimPanel.py
  14. 10 11
      contrib/src/sceneeditor/seCameraControl.py
  15. 8 2
      contrib/src/sceneeditor/seColorEntry.py
  16. 18 18
      contrib/src/sceneeditor/seFileSaver.py
  17. 1 3
      contrib/src/sceneeditor/seForceGroup.py
  18. 8 8
      contrib/src/sceneeditor/seGeometry.py
  19. 10 11
      contrib/src/sceneeditor/seLights.py
  20. 1 1
      contrib/src/sceneeditor/seManipulation.py
  21. 153 146
      contrib/src/sceneeditor/seMopathRecorder.py
  22. 4 4
      contrib/src/sceneeditor/seParticleEffect.py
  23. 29 22
      contrib/src/sceneeditor/seParticlePanel.py
  24. 5 25
      contrib/src/sceneeditor/seParticles.py
  25. 34 27
      contrib/src/sceneeditor/sePlacer.py
  26. 16 9
      contrib/src/sceneeditor/seSceneGraphExplorer.py
  27. 20 11
      contrib/src/sceneeditor/seSelection.py
  28. 9 6
      contrib/src/sceneeditor/seSession.py
  29. 15 9
      contrib/src/sceneeditor/seTree.py
  30. 17 1
      direct/src/interval/MetaInterval.py
  31. 2 2
      direct/src/interval/cMetaInterval.cxx
  32. 20 12
      direct/src/showbase/Transitions.py
  33. 2 2
      direct/src/stdpy/threading.py
  34. 2 1
      direct/src/tkpanels/ParticlePanel.py
  35. 224 222
      dtool/src/cppparser/cppBison.cxx.prebuilt
  36. 2 2
      dtool/src/cppparser/cppBison.h.prebuilt
  37. 27 5
      dtool/src/cppparser/cppBison.yxx
  38. 4 0
      dtool/src/dtoolbase/dtoolbase_cc.h
  39. 7 0
      dtool/src/parser-inc/time.h
  40. 1 1
      dtool/src/parser-inc/ws2tcpip.h
  41. 18 19
      dtool/src/pystub/pystub.cxx
  42. 3 0
      makepanda/makepanda.py
  43. 0 4
      makepanda/makepanda.vcproj
  44. 2 1
      makepanda/makepandacore.py
  45. 4 1
      makepanda/makewheel.py
  46. 1 1
      panda/src/chan/partBundle.h
  47. 5 1
      panda/src/char/characterJointEffect.I
  48. 274 100
      panda/src/collide/collisionBox.cxx
  49. 9 0
      panda/src/collide/collisionBox.h
  50. 144 8
      panda/src/collide/collisionTube.cxx
  51. 10 3
      panda/src/collide/collisionTube.h
  52. 0 70
      panda/src/collide/test_collide.cxx
  53. 3 5
      panda/src/cull/cullBinBackToFront.cxx
  54. 3 5
      panda/src/cull/cullBinFixed.cxx
  55. 3 5
      panda/src/cull/cullBinFrontToBack.cxx
  56. 3 5
      panda/src/cull/cullBinStateSorted.cxx
  57. 3 5
      panda/src/cull/cullBinUnsorted.cxx
  58. 0 18
      panda/src/display/test_display.cxx
  59. 10 4
      panda/src/dxgsg9/dxGeomMunger9.cxx
  60. 6 0
      panda/src/egg/eggData.h
  61. 1 0
      panda/src/egg/eggGroupNode.h
  62. 9 6
      panda/src/egg/eggNode.h
  63. 20 0
      panda/src/express/pointerTo.h
  64. 1 0
      panda/src/express/pointerToBase.h
  65. 0 7
      panda/src/express/pointerToVoid.I
  66. 2 2
      panda/src/express/pointerToVoid.h
  67. 264 34
      panda/src/express/weakPointerTo.I
  68. 77 2
      panda/src/express/weakPointerTo.h
  69. 191 33
      panda/src/express/weakPointerToBase.I
  70. 21 1
      panda/src/express/weakPointerToBase.h
  71. 6 9
      panda/src/express/weakPointerToVoid.I
  72. 2 2
      panda/src/express/weakPointerToVoid.h
  73. 9 1
      panda/src/framework/pandaFramework.cxx
  74. 1 0
      panda/src/framework/pandaFramework.h
  75. 15 8
      panda/src/glstuff/glCgShaderContext_src.cxx
  76. 20 8
      panda/src/glstuff/glGeomMunger_src.cxx
  77. 87 15
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  78. 10 1
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  79. 99 22
      panda/src/glstuff/glShaderContext_src.cxx
  80. 631 31
      panda/src/glstuff/panda_glext.h
  81. 0 12
      panda/src/gobj/config_gobj.cxx
  82. 0 1
      panda/src/gobj/config_gobj.h
  83. 49 12
      panda/src/gobj/geom.cxx
  84. 11 3
      panda/src/gobj/geomPrimitive.I
  85. 1 0
      panda/src/gobj/geomPrimitive.h
  86. 16 10
      panda/src/gobj/lens.I
  87. 43 15
      panda/src/gobj/material.I
  88. 25 34
      panda/src/gobj/material.cxx
  89. 6 0
      panda/src/gobj/material.h
  90. 5 0
      panda/src/gobj/perspectiveLens.cxx
  91. 22 5
      panda/src/gobj/shader.cxx
  92. 10 9
      panda/src/gobj/shader.h
  93. 8 4
      panda/src/gobj/shaderBuffer.I
  94. 1 1
      panda/src/gobj/shaderBuffer.cxx
  95. 1 1
      panda/src/gobj/shaderBuffer.h
  96. 0 25
      panda/src/gobj/test_gobj.cxx
  97. 18 0
      panda/src/gobj/texture.I
  98. 1 0
      panda/src/gobj/texture.h
  99. 4 0
      panda/src/gobj/texturePeeker.cxx
  100. 20 0
      panda/src/gobj/texturePool.I

+ 1 - 1
.travis.yml

@@ -51,4 +51,4 @@ notifications:
     on_success: change
     on_failure: always
     use_notice: true
-    skip_join: true
+    skip_join: false

+ 1 - 1
contrib/src/sceneeditor/MetadataPanel.py

@@ -36,7 +36,7 @@ class MetadataPanel(AppShell,Pmw.MegaWidget):
 
 
     def appInit(self):
-        print "Metadata Panel"
+        print("Metadata Panel")
 
     def createInterface(self):
         interior = self.interior()

+ 37 - 29
contrib/src/sceneeditor/SideWindow.py

@@ -6,8 +6,16 @@ from direct.tkwidgets.AppShell import AppShell
 from direct.tkwidgets.VectorWidgets import ColorEntry
 from direct.showbase.TkGlobal import spawnTkLoop
 import seSceneGraphExplorer
-from Tkinter import Frame, IntVar, Checkbutton, Toplevel
-import Pmw, Tkinter
+
+import Pmw, sys
+
+if sys.version_info >= (3, 0):
+    from tkinter import Frame, IntVar, Checkbutton, Toplevel
+    import tkinter
+else:
+    from Tkinter import Frame, IntVar, Checkbutton, Toplevel
+    import Tkinter as tkinter
+
 
 class sideWindow(AppShell):
     #################################################################
@@ -65,7 +73,7 @@ class sideWindow(AppShell):
         self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
 
     def appInit(self):
-        print '----SideWindow is Initialized!!'
+        print('----SideWindow is Initialized!!')
 
     def createInterface(self):
         # The interior of the toplevel panel
@@ -73,7 +81,7 @@ class sideWindow(AppShell):
         mainFrame = Frame(interior)
         ## Creat NoteBook
         self.notebookFrame = Pmw.NoteBook(mainFrame)
-        self.notebookFrame.pack(fill=Tkinter.BOTH,expand=1)
+        self.notebookFrame.pack(fill=tkinter.BOTH,expand=1)
         sgePage = self.notebookFrame.add('Tree Graph')
         envPage = self.notebookFrame.add('World Setting')
         self.notebookFrame['raisecommand'] = self.updateInfo
@@ -83,7 +91,7 @@ class sideWindow(AppShell):
             sgePage, nodePath = render,
             scrolledCanvas_hull_width = 270,
             scrolledCanvas_hull_height = 570)
-        self.SGE.pack(fill = Tkinter.BOTH, expand = 0)
+        self.SGE.pack(fill = tkinter.BOTH, expand = 0)
 
         ## World Setting Page
         envPage = Frame(envPage)
@@ -95,8 +103,8 @@ class sideWindow(AppShell):
             text = 'Enable Lighting',
             variable = self.LightingVar,
             command = self.toggleLights)
-        self.LightingButton.pack(side=Tkinter.LEFT, expand=False)
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        self.LightingButton.pack(side=tkinter.LEFT, expand=False)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         pageFrame = Frame(envPage)
         self.CollisionVar = IntVar()
@@ -106,8 +114,8 @@ class sideWindow(AppShell):
             text = 'Show Collision Object',
             variable = self.CollisionVar,
             command = self.showCollision)
-        self.CollisionButton.pack(side=Tkinter.LEFT, expand=False)
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        self.CollisionButton.pack(side=tkinter.LEFT, expand=False)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         pageFrame = Frame(envPage)
         self.ParticleVar = IntVar()
@@ -117,8 +125,8 @@ class sideWindow(AppShell):
             text = 'Show Particle Dummy',
             variable = self.ParticleVar,
             command = self.enableParticle)
-        self.ParticleButton.pack(side=Tkinter.LEFT, expand=False)
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        self.ParticleButton.pack(side=tkinter.LEFT, expand=False)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         pageFrame = Frame(envPage)
         self.baseUseDriveVar = IntVar()
@@ -128,8 +136,8 @@ class sideWindow(AppShell):
             text = 'Enable base.usedrive',
             variable = self.baseUseDriveVar,
             command = self.enablebaseUseDrive)
-        self.baseUseDriveButton.pack(side=Tkinter.LEFT, expand=False)
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        self.baseUseDriveButton.pack(side=tkinter.LEFT, expand=False)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         pageFrame = Frame(envPage)
         self.backfaceVar = IntVar()
@@ -139,8 +147,8 @@ class sideWindow(AppShell):
             text = 'Enable BackFace',
             variable = self.backfaceVar,
             command = self.toggleBackface)
-        self.backfaceButton.pack(side=Tkinter.LEFT, expand=False)
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        self.backfaceButton.pack(side=tkinter.LEFT, expand=False)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         pageFrame = Frame(envPage)
         self.textureVar = IntVar()
@@ -150,8 +158,8 @@ class sideWindow(AppShell):
             text = 'Enable Texture',
             variable = self.textureVar,
             command = self.toggleTexture)
-        self.textureButton.pack(side=Tkinter.LEFT, expand=False)
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        self.textureButton.pack(side=tkinter.LEFT, expand=False)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         pageFrame = Frame(envPage)
         self.wireframeVar = IntVar()
@@ -161,8 +169,8 @@ class sideWindow(AppShell):
             text = 'Enable Wireframe',
             variable = self.wireframeVar,
             command = self.toggleWireframe)
-        self.wireframeButton.pack(side=Tkinter.LEFT, expand=False)
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        self.wireframeButton.pack(side=tkinter.LEFT, expand=False)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         pageFrame = Frame(envPage)
         self.gridVar = IntVar()
@@ -172,8 +180,8 @@ class sideWindow(AppShell):
             text = 'Enable Grid',
             variable = self.gridVar,
             command = self.toggleGrid)
-        self.gridButton.pack(side=Tkinter.LEFT, expand=False)
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        self.gridButton.pack(side=tkinter.LEFT, expand=False)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         pageFrame = Frame(envPage)
         self.widgetVisVar = IntVar()
@@ -183,8 +191,8 @@ class sideWindow(AppShell):
             text = 'Enable WidgetVisible',
             variable = self.widgetVisVar,
             command = self.togglewidgetVis)
-        self.widgetVisButton.pack(side=Tkinter.LEFT, expand=False)
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        self.widgetVisButton.pack(side=tkinter.LEFT, expand=False)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         pageFrame = Frame(envPage)
         self.enableAutoCameraVar = IntVar()
@@ -194,17 +202,17 @@ class sideWindow(AppShell):
             text = 'Enable Auto Camera Movement for Loading Objects',
             variable = self.enableAutoCameraVar,
             command = self.toggleAutoCamera)
-        self.enableAutoCameraButton.pack(side=Tkinter.LEFT, expand=False)
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        self.enableAutoCameraButton.pack(side=tkinter.LEFT, expand=False)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         pageFrame = Frame(envPage)
         self.backgroundColor = ColorEntry(
             pageFrame, text = 'BG Color', value=self.worldColor)
         self.backgroundColor['command'] = self.setBackgroundColorVec
         self.backgroundColor['resetValue'] = [0,0,0,0]
-        self.backgroundColor.pack(side=Tkinter.LEFT, expand=False)
+        self.backgroundColor.pack(side=tkinter.LEFT, expand=False)
         self.bind(self.backgroundColor, 'Set background color')
-        pageFrame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True)
+        pageFrame.pack(side=tkinter.TOP, fill=tkinter.X, expand=True)
 
         envPage.pack(expand=False)
 
@@ -320,11 +328,11 @@ class sideWindow(AppShell):
         #
         #################################################################
         if self.enableBaseUseDrive==0:
-            print 'Enabled'
+            print('Enabled')
             base.useDrive()
             self.enableBaseUseDrive = 1
         else:
-            print 'disabled'
+            print('disabled')
             #base.useTrackball()
             base.disableMouse()
             self.enableBaseUseDrive = 0

+ 3 - 4
contrib/src/sceneeditor/collisionWindow.py

@@ -9,9 +9,8 @@ from seColorEntry import *
 from direct.tkwidgets import VectorWidgets
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Slider
-from Tkinter import *
 import string, math, types
-from pandac.PandaModules import *
+from panda3d.core import *
 
 
 class collisionWindow(AppShell):
@@ -195,7 +194,7 @@ class collisionWindow(AppShell):
         # put the object into a CollisionNode and attach it to the target nodePath
         #################################################################
         collisionObject = None
-        print self.objType
+        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()),
@@ -236,7 +235,7 @@ class collisionWindow(AppShell):
                            float(self.widgetDict['RayDirection'][1]._entry.get()),
                            float(self.widgetDict['RayDirection'][2]._entry.get()))
 
-            print vector, point
+            print(vector, point)
 
             collisionObject = CollisionRay()
             collisionObject.setOrigin(point)

+ 186 - 180
contrib/src/sceneeditor/controllerWindow.py

@@ -4,8 +4,14 @@
 #################################################################
 
 from direct.tkwidgets.AppShell import AppShell
-from Tkinter import Frame, Label, Button
-import string, Pmw, Tkinter
+import sys, Pmw
+
+if sys.version_info >= (3, 0):
+    from tkinter import Frame, Label, Button
+    import tkinter
+else:
+    from Tkinter import Frame, Label, Button
+    import Tkinter as tkinter
 
 # Define the Category
 KEYBOARD = 'Keyboard-'
@@ -75,11 +81,11 @@ class controllerWindow(AppShell):
         self.cotrollerTypeEntry = self.createcomponent(
             'Controller Type', (), None,
             Pmw.ComboBox, (frame,),
-            labelpos = Tkinter.W, label_text='Controller Type:', entry_width = 20,entry_state = Tkinter.DISABLED,
+            labelpos = tkinter.W, label_text='Controller Type:', entry_width = 20,entry_state = tkinter.DISABLED,
             selectioncommand = self.setControllerType,
             scrolledlist_items = self.controllerList)
-        self.cotrollerTypeEntry.pack(side=Tkinter.LEFT)
-        frame.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=False, pady = 3)
+        self.cotrollerTypeEntry.pack(side=tkinter.LEFT)
+        frame.pack(side=tkinter.TOP, fill=tkinter.X, expand=False, pady = 3)
         self.cotrollerTypeEntry.selectitem('Keyboard', setentry=True)
 
         self.inputZone = Pmw.Group(mainFrame, tag_pyclass = None)
@@ -102,7 +108,7 @@ class controllerWindow(AppShell):
         keyboardPage = self.objNotebook.add('Keyboard')
         tarckerPage = self.objNotebook.add('Tracker')
         self.objNotebook.selectpage('Keyboard')
-        self.objNotebook.pack(side = Tkinter.TOP, fill='both',expand=False)
+        self.objNotebook.pack(side = tkinter.TOP, fill='both',expand=False)
         # Put this here so it isn't called right away
         self.objNotebook['raisecommand'] = self.updateControlInfo
 
@@ -113,11 +119,11 @@ class controllerWindow(AppShell):
         widget = self.createcomponent(
             'Target Type', (), None,
             Pmw.ComboBox, (Interior,),
-            labelpos = Tkinter.W, label_text='Target Object:', entry_width = 20, entry_state = Tkinter.DISABLED,
+            labelpos = tkinter.W, label_text='Target Object:', entry_width = 20, entry_state = tkinter.DISABLED,
             selectioncommand = self.setTargetObj,
             scrolledlist_items = self.listOfObj)
-        widget.pack(side=Tkinter.LEFT, padx=3)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 5)
+        widget.pack(side=tkinter.LEFT, padx=3)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 5)
         widget.selectitem(self.nameOfNode, setentry=True)
         self.widgetsDict[KEYBOARD+'ObjList'] = widget
 
@@ -126,411 +132,411 @@ class controllerWindow(AppShell):
         settingFrame = inputZone.interior()
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Assign a Key For:').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True,pady = 6 )
+        widget = Label(Interior, text = 'Assign a Key For:').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True,pady = 6 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Forward   :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Forward   :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Forward key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyForward'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyForward'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Forward Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedForward'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedForward'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Backward  :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Backward  :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Backward key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyBackward'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyBackward'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Backward Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedBackward'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedBackward'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Right     :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Right     :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Right key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyRight'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyRight'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Right Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedRight'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedRight'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Left      :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Left      :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Left key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyLeft'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyLeft'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Left Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedLeft'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedLeft'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Up        :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Up        :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Up key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyUp'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyUp'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Up Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedUp'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedUp'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Down      :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Down      :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Down key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyDown'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyDown'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Down Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedDown'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedDown'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Turn Right:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Turn Right:', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Turn Right key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyTurnRight'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyTurnRight'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Turn Right Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedTurnRight'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedTurnRight'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Turn Left :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Turn Left :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Turn Left key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyTurnLeft'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyTurnLeft'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Turn Left Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedTurnLeft'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedTurnLeft'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Turn UP   :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Turn UP   :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Turn UP key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyTurnUp'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyTurnUp'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Turn UP Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedTurnUp'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedTurnUp'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Turn Down :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Turn Down :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Turn Down key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyTurnDown'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyTurnDown'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Turn Down Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedTurnDown'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedTurnDown'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Roll Right:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Roll Right:', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Roll Right key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyRollRight'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyRollRight'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Roll Right Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedRollRight'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedRollRight'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Roll Left :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Roll Left :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Roll Left key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyRollLeft'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyRollLeft'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Roll Left Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedRollLeft'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedRollLeft'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Scale UP :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Scale UP :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale UP key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyScaleUp'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyScaleUp'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale UP Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedScaleUp'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedScaleUp'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Scale Down:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Scale Down:', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Down key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyScaleDown'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyScaleDown'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Down Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedScaleDown'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedScaleDown'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Scale X UP :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Scale X UP :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale X UP key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyScaleXUp'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyScaleXUp'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale X UP Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedScaleXUp'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedScaleXUp'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Scale X Down:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Scale X Down:', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale X Down key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyScaleXDown'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyScaleXDown'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Down X Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedScaleXDown'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedScaleXDown'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Scale Y UP :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Scale Y UP :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Y UP key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyScaleYUp'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyScaleYUp'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Y UP Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedScaleYUp'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedScaleYUp'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Scale Y Down:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Scale Y Down:', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Y Down key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyScaleYDown'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyScaleYDown'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Down XY Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedScaleYDown'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedScaleYDown'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Scale Z UP :', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Scale Z UP :', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Z UP key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyScaleZUp'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyScaleZUp'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Z UP Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedScaleZUp'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedScaleZUp'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
         Interior = Frame(settingFrame)
-        widget = Label(Interior, text = 'Scale Z Down:', width = 20, anchor = Tkinter.W).pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = 'Scale Z Down:', width = 20, anchor = tkinter.W).pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Z Down key', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardMapDict['KeyScaleZDown'],
-            labelpos = Tkinter.W, label_text='Key :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Key :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'KeyScaleZDown'] = widget
-        widget = Label(Interior, text = '   ').pack(side=Tkinter.LEFT, expand = False)
+        widget = Label(Interior, text = '   ').pack(side=tkinter.LEFT, expand = False)
         widget = self.createcomponent(
             'Scale Down Z Speed', (), None,
             Pmw.EntryField, (Interior,),
             value = self.keyboardSpeedDict['SpeedScaleZDown'],
-            labelpos = Tkinter.W, label_text='Speed :', entry_width = 10)
-        widget.pack(side=Tkinter.LEFT, expand = False)
+            labelpos = tkinter.W, label_text='Speed :', entry_width = 10)
+        widget.pack(side=tkinter.LEFT, expand = False)
         self.widgetsDict[KEYBOARD+'SpeedScaleZDown'] = widget
-        widget = Label(Interior, text = 'Per Second').pack(side=Tkinter.LEFT, expand = False)
-        Interior.pack(side=Tkinter.TOP, fill=Tkinter.X, expand=True, pady = 4 )
+        widget = Label(Interior, text = 'Per Second').pack(side=tkinter.LEFT, expand = False)
+        Interior.pack(side=tkinter.TOP, fill=tkinter.X, expand=True, pady = 4 )
 
-        assignFrame.pack(side=Tkinter.TOP, expand=True, fill = Tkinter.X)
-        keyboardPage.pack(side=Tkinter.TOP, expand=True, fill = Tkinter.X)
+        assignFrame.pack(side=tkinter.TOP, expand=True, fill = tkinter.X)
+        keyboardPage.pack(side=tkinter.TOP, expand=True, fill = tkinter.X)
 
         ####################################################################
         ####################################################################
@@ -539,12 +545,12 @@ class controllerWindow(AppShell):
         ####################################################################
         # Pack the mainFrame
         frame = Frame(mainFrame)
-        widget = Button(frame, text='OK', width = 13, command=self.ok_press).pack(side=Tkinter.RIGHT)
-        widget = Button(frame, text='Enable Control', width = 13, command=self.enableControl).pack(side=Tkinter.LEFT)
-        widget = Button(frame, text='Disable Control', width = 13, command=self.disableControl).pack(side=Tkinter.LEFT)
-        widget = Button(frame, text='Save & Keep', width = 13, command=self.saveKeepControl).pack(side=Tkinter.LEFT)
-        frame.pack(side = Tkinter.BOTTOM, expand=1, fill = Tkinter.X)
-        mainFrame.pack(expand=1, fill = Tkinter.BOTH)
+        widget = Button(frame, text='OK', width = 13, command=self.ok_press).pack(side=tkinter.RIGHT)
+        widget = Button(frame, text='Enable Control', width = 13, command=self.enableControl).pack(side=tkinter.LEFT)
+        widget = Button(frame, text='Disable Control', width = 13, command=self.disableControl).pack(side=tkinter.LEFT)
+        widget = Button(frame, text='Save & Keep', width = 13, command=self.saveKeepControl).pack(side=tkinter.LEFT)
+        frame.pack(side = tkinter.BOTTOM, expand=1, fill = tkinter.X)
+        mainFrame.pack(expand=1, fill = tkinter.BOTH)
 
     def onDestroy(self, event):
         # Check if user wish to keep the control after the window closed.
@@ -688,7 +694,7 @@ class controllerWindow(AppShell):
                 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
+            print(self.nodePath)
             messenger.send('ControlW_saveSetting', ['Keyboard', [self.nodePath, self.keyboardMapDict, self.keyboardSpeedDict]])
         return
 

+ 50 - 48
contrib/src/sceneeditor/dataHolder.py

@@ -2,13 +2,15 @@
 # TK and PMW INTERFACE MODULES#
 ###############################
 from direct.showbase.TkGlobal import*
-from tkFileDialog import *
 import Pmw
-import tkFileDialog
-import tkMessageBox
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Floater
 
+if sys.version_info >= (3, 0):
+    from tkinter.filedialog import askopenfilename
+else:
+    from tkFileDialog import askopenfilename
+
 
 #############################
 # Scene Editor Python Files #
@@ -154,7 +156,7 @@ class dataHolder:
         self.ActorNum=0
         self.theScene=None
         messenger.send('SGE_Update Explorer',[render])
-        print 'Scene should be cleaned up!'
+        print('Scene should be cleaned up!')
 
     def removeObj(self, nodePath):
         #################################################################
@@ -169,7 +171,7 @@ class dataHolder:
         childrenList = nodePath.getChildren()
 
 
-        if self.ModelDic.has_key(name):
+        if name in self.ModelDic:
             del self.ModelDic[name]
             del self.ModelRefDic[name]
             if len(childrenList) != 0:
@@ -178,7 +180,7 @@ class dataHolder:
             nodePath.removeNode()
             self.ModelNum -= 1
             pass
-        elif self.ActorDic.has_key(name):
+        elif name in self.ActorDic:
             del self.ActorDic[name]
             del self.ActorRefDic[name]
             if len(childrenList) != 0:
@@ -187,14 +189,14 @@ class dataHolder:
             nodePath.removeNode()
             self.ActorNum -= 1
             pass
-        elif self.collisionDict.has_key(name):
+        elif name in self.collisionDict:
             del self.collisionDict[name]
             if len(childrenList) != 0:
                 for node in childrenList:
                     self.removeObj(node)
             nodePath.removeNode()
             pass
-        elif self.dummyDict.has_key(name):
+        elif name in self.dummyDict:
             del self.dummyDict[name]
             if len(childrenList) != 0:
                 for node in childrenList:
@@ -207,12 +209,12 @@ class dataHolder:
                     self.removeObj(node)
             list = self.lightManager.delete(name)
             return list
-        elif self.particleNodes.has_key(name):
+        elif name in self.particleNodes:
             self.particleNodes[name].removeNode()
             del self.particleNodes[name]
             del self.particleDict[name]
         else:
-            print 'You cannot remove this NodePath'
+            print('You cannot remove this NodePath')
             return
 
         messenger.send('SGE_Update Explorer',[render])
@@ -237,15 +239,15 @@ class dataHolder:
         cHpr = hpr
         cScale = scale
         parent = nodePath.getParent()
-        if self.ActorDic.has_key(name):
+        if name in self.ActorDic:
             holder = self.ActorDic
             holderRef = self.ActorRefDic
             isModel = False
-        elif self.ModelDic.has_key(name):
+        elif name in self.ModelDic:
             holder = self.ModelDic
             holderRef = self.ModelRefDic
         else:
-            print '---- DataHolder: Target Obj is not a legal object could be duplicate!'
+            print('---- DataHolder: Target Obj is not a legal object could be duplicate!')
             return
 
         FilePath = holderRef[name]
@@ -356,7 +358,7 @@ class dataHolder:
         # 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)
+        return name in self.ActorDic
 
     def getActor(self, name):
         ###########################################################################
@@ -366,7 +368,7 @@ class dataHolder:
         if self.isActor(name):
             return self.ActorDic[name]
         else:
-            print '----No Actor named: ', name
+            print('----No Actor named: ', name)
             return None
 
     def getModel(self, name):
@@ -377,7 +379,7 @@ class dataHolder:
         if self.isModel(name):
             return self.ModelDic[name]
         else:
-            print '----No Model named: ', name
+            print('----No Model named: ', name)
             return None
 
     def isModel(self, name):
@@ -386,7 +388,7 @@ class dataHolder:
         # 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)
+        return name in self.ModelDic
 
     def loadAnimation(self,name, Dic):
         ###########################################################################
@@ -406,7 +408,7 @@ class dataHolder:
             messenger.send('DataH_loadFinish'+name)
             return
         else:
-            print '------ Error when loading animation for Actor: ', name
+            print('------ Error when loading animation for Actor: ', name)
 
     def removeAnimation(self, name, anim):
         ###########################################################################
@@ -527,7 +529,7 @@ class dataHolder:
             self.ActorDic[nName]= self.ActorDic[oName]
             self.ActorRefDic[nName]= self.ActorRefDic[oName]
             self.ActorDic[nName].setName(nName)
-            if self.blendAnimDict.has_key(oName):
+            if oName in self.blendAnimDict:
                 self.blendAnimDict[nName] = self.blendAnimDict[oName]
                 del self.blendAnimDict[oName]
             del self.ActorDic[oName]
@@ -540,16 +542,16 @@ class dataHolder:
             del self.ModelRefDic[oName]
         elif self.lightManager.isLight(oName):
             list, lightNode = self.lightManager.rename(oName, nName)
-        elif self.dummyDict.has_key(oName):
+        elif oName in self.dummyDict:
             self.dummyDict[nName]= self.dummyDict[oName]
             self.dummyDict[nName].setName(nName)
             del self.dummyDict[oName]
-        elif self.collisionDict.has_key(oName):
+        elif oName in self.collisionDict:
             self.collisionDict[nName]= self.collisionDict[oName]
             self.collisionDict[nName].setName(nName)
             del self.collisionDict[oName]
 
-        elif self.particleNodes.has_key(oName):
+        elif oName in self.particleNodes:
             self.particleNodes[nName]= self.particleNodes[oName]
             self.particleDict[nName]= self.particleDict[oName]
             self.particleDict[nName].setName(nName)
@@ -557,9 +559,9 @@ class dataHolder:
             del self.particleNodes[oName]
             del self.particleDict[oName]
         else:
-            print '----Error: This Object is not allowed to this function!'
+            print('----Error: This Object is not allowed to this function!')
 
-        if self.curveDict.has_key(oName):
+        if oName in self.curveDict:
             self.curveDict[nName] = self.curveDict[oName]
             del self.curveDict[oName]
 
@@ -578,11 +580,11 @@ class dataHolder:
             return True
         elif self.lightManager.isLight(name):
             return True
-        elif self.dummyDict.has_key(name):
+        elif name in self.dummyDict:
             return True
-        elif self.collisionDict.has_key(name):
+        elif name in self.collisionDict:
             return True
-        elif self.particleNodes.has_key(name):
+        elif name in self.particleNodes:
             return True
         elif (name == 'render')or(name == 'SEditor')or(name == 'Lights')or(name == 'camera'):
             return True
@@ -596,7 +598,7 @@ class dataHolder:
         # 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):
+        if name in self.curveDict:
             self.curveDict[name].append(curveCollection)
             return
         else:
@@ -612,7 +614,7 @@ class dataHolder:
         # If the input node has not been bindedwith any curve, it will return None.
         ###########################################################################
         name = nodePath.getName()
-        if self.curveDict.has_key(name):
+        if name in self.curveDict:
             return self.curveDict[name]
         else:
             return None
@@ -626,7 +628,7 @@ class dataHolder:
         # This message will be caught by Property Window for this node.
         ###########################################################################
         name =nodePath.getName()
-        if self.curveDict.has_key(name):
+        if name in self.curveDict:
             index = None
             for curve in self.curveDict[name]:
                 if curve.getCurve(0).getName() == curveName:
@@ -677,12 +679,12 @@ class dataHolder:
         elif self.isLight(name):
             type = 'Light'
             info['lightNode'] = self.lightManager.getLightNode(name)
-        elif self.dummyDict.has_key(name):
+        elif name in self.dummyDict:
             type = 'dummy'
-        elif self.collisionDict.has_key(name):
+        elif name in self.collisionDict:
             type = 'collisionNode'
             info['collisionNode'] = self.collisionDict[name]
-        if self.curveDict.has_key(name):
+        if name in self.curveDict:
             info['curveList'] = self.getCurveList(nodePath)
 
         return type, info
@@ -794,7 +796,7 @@ class dataHolder:
         # The formate of thsi dictionary is
         # {"name of Blend Animation" : ["Animation A, Animation B, Effect(Float, 0~1)"]}
         ###########################################################################
-        if self.blendAnimDict.has_key(name):
+        if name in self.blendAnimDict:
             return self.blendAnimDict[name]
         else:
             return {}
@@ -808,8 +810,8 @@ class dataHolder:
         # 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):
+        if actorName in self.blendAnimDict:
+            if blendName in self.blendAnimDict[actorName]:
                 ### replace the original setting
                 self.blendAnimDict[actorName][blendName][0] = animNameA
                 self.blendAnimDict[actorName][blendName][1] = animNameB
@@ -832,7 +834,7 @@ class dataHolder:
         # it will also rewrite the data to the newest one.
         ###########################################################################
         self.removeBlendAnim(actorName,oName)
-        print self.blendAnimDict
+        print(self.blendAnimDict)
         return self.saveBlendAnim(actorName, nName, animNameA, animNameB, effect)
 
     def removeBlendAnim(self, actorName, blendName):
@@ -844,8 +846,8 @@ class dataHolder:
         # 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):
+        if actorName in self.blendAnimDict:
+            if blendName in self.blendAnimDict[actorName]:
                 ### replace the original setting
                 del self.blendAnimDict[actorName][blendName]
             if len(self.blendAnimDict[actorName])==0:
@@ -876,15 +878,15 @@ class dataHolder:
         ###########################################################################
         if name == 'camera':
             return camera
-        elif self.ModelDic.has_key(name):
+        elif name in self.ModelDic:
             return self.ModelDic[name]
-        elif self.ActorDic.has_key(name):
+        elif name in self.ActorDic:
             return self.ActorDic[name]
-        elif self.collisionDict.has_key(name):
+        elif name in self.collisionDict:
             return self.collisionDict[name]
-        elif self.dummyDict.has_key(name):
+        elif name in self.dummyDict:
             return self.dummyDict[name]
-        elif self.particleNodes.has_key(name):
+        elif name in self.particleNodes:
             return self.particleNodes[name]
         elif self.lightManager.isLight(name):
             return self.lightManager.getLightNode(name)
@@ -935,13 +937,13 @@ class dataHolder:
         ###########################################################################
 
         ### Ask for a filename
-        OpenFilename = tkFileDialog.askopenfilename(filetypes = [("PY","py")],title = "Load Scene")
+        OpenFilename = 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
+        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
@@ -976,7 +978,7 @@ class dataHolder:
             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))):
+            if(str(actor) in self.Scene.blendAnimDict):
                 self.blendAnimDict[actor]=self.Scene.blendAnimDict[actor]
             self.ActorNum=self.ActorNum+1
 
@@ -1006,7 +1008,7 @@ class dataHolder:
                 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'
+                print('Invalid light type')
 
         ############################################################################
         # Populate Dummy related Dictionaries

+ 2 - 2
contrib/src/sceneeditor/duplicateWindow.py

@@ -45,7 +45,7 @@ class duplicateWindow(AppShell):
         self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
 
     def appInit(self):
-        print '----SideWindow is Initialized!!'
+        print('----SideWindow is Initialized!!')
 
     def createInterface(self):
         # The interior of the toplevel panel
@@ -122,7 +122,7 @@ class duplicateWindow(AppShell):
         # This message will be caught by sceneEditor.
         #################################################################
         if not self.allEntryValid():
-            print '---- Duplication Window: Invalid value!!'
+            print('---- Duplication Window: Invalid value!!')
             return
         x = self.move_x.getvalue()
         y = self.move_y.getvalue()

+ 32 - 25
contrib/src/sceneeditor/lightingPanel.py

@@ -7,9 +7,16 @@ from direct.tkwidgets.AppShell import AppShell
 from seColorEntry import *
 from direct.tkwidgets.VectorWidgets import Vector3Entry
 from direct.tkwidgets.Slider import Slider
-from Tkinter import Frame, Button, Menubutton, Menu
-import string, math, types, Pmw, Tkinter
-from pandac.PandaModules import *
+import sys, math, types, Pmw
+from panda3d.core import *
+
+if sys.version_info >= (3, 0):
+    from tkinter import Frame, Button, Menubutton, Menu
+    import tkinter
+else:
+    from Tkinter import Frame, Button, Menubutton, Menu
+    import Tkinter as tkinter
+
 
 class lightingPanel(AppShell):
     #################################################################
@@ -51,25 +58,25 @@ class lightingPanel(AppShell):
         mainFrame = Frame(interior)
 
         self.listZone = Pmw.Group(mainFrame,tag_pyclass = None)
-        self.listZone.pack(expand=0, fill=Tkinter.X,padx=3,pady=3)
+        self.listZone.pack(expand=0, fill=tkinter.X,padx=3,pady=3)
         listFrame = self.listZone.interior()
 
         self.lightEntry = self.createcomponent(
             'Lights List', (), None,
             Pmw.ComboBox, (listFrame,),label_text='Light :',
-            labelpos = Tkinter.W, entry_width = 25, selectioncommand = self.selectLight,
+            labelpos = tkinter.W, entry_width = 25, selectioncommand = self.selectLight,
             scrolledlist_items = self.lightList)
-        self.lightEntry.pack(side=Tkinter.LEFT)
+        self.lightEntry.pack(side=tkinter.LEFT)
 
         self.renameButton = self.createcomponent(
             'Rename Light', (), None,
             Button, (listFrame,),
             text = ' Rename ',
             command = self.renameLight)
-        self.renameButton.pack(side=Tkinter.LEFT)
+        self.renameButton.pack(side=tkinter.LEFT)
 
         self.addLighZone = Pmw.Group(listFrame,tag_pyclass = None)
-        self.addLighZone.pack(side=Tkinter.LEFT)
+        self.addLighZone.pack(side=tkinter.LEFT)
         insideFrame = self.addLighZone.interior()
         self.lightsButton = Menubutton(insideFrame, text = 'Add light',borderwidth = 3,
                                        activebackground = '#909090')
@@ -91,13 +98,13 @@ class lightingPanel(AppShell):
             Button, (listFrame,),
             text = '  Delete  ',
             command = self.deleteLight)
-        self.deleteButton.pack(side=Tkinter.LEFT)
+        self.deleteButton.pack(side=tkinter.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=Tkinter.X,expand=0)
+        self.lightColor.pack(fill=tkinter.X,expand=0)
         self.bind(self.lightColor, 'Set light color')
 
         # Notebook pages for light specific controls
@@ -114,27 +121,27 @@ class lightingPanel(AppShell):
         self.dSpecularColor = seColorEntry(
             directionalPage, text = 'Specular Color')
         self.dSpecularColor['command'] = self.setSpecularColor
-        self.dSpecularColor.pack(fill = Tkinter.X, expand = 0)
+        self.dSpecularColor.pack(fill = tkinter.X, expand = 0)
         self.bind(self.dSpecularColor,
                   'Set directional light specular color')
         self.dPosition = Vector3Entry(
             directionalPage, text = 'Position')
         self.dPosition['command'] = self.setPosition
         self.dPosition['resetValue'] = [0,0,0,0]
-        self.dPosition.pack(fill = Tkinter.X, expand = 0)
+        self.dPosition.pack(fill = tkinter.X, expand = 0)
         self.bind(self.dPosition, 'Set directional light position')
         self.dOrientation = Vector3Entry(
             directionalPage, text = 'Orientation')
         self.dOrientation['command'] = self.setOrientation
         self.dOrientation['resetValue'] = [0,0,0,0]
-        self.dOrientation.pack(fill = Tkinter.X, expand = 0)
+        self.dOrientation.pack(fill = tkinter.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 = Tkinter.X, expand = 0)
+        self.pSpecularColor.pack(fill = tkinter.X, expand = 0)
         self.bind(self.pSpecularColor,
                   'Set point light specular color')
 
@@ -142,7 +149,7 @@ class lightingPanel(AppShell):
             pointPage, text = 'Position')
         self.pPosition['command'] = self.setPosition
         self.pPosition['resetValue'] = [0,0,0,0]
-        self.pPosition.pack(fill = Tkinter.X, expand = 0)
+        self.pPosition.pack(fill = tkinter.X, expand = 0)
         self.bind(self.pPosition, 'Set point light position')
 
         self.pConstantAttenuation = Slider(
@@ -152,7 +159,7 @@ class lightingPanel(AppShell):
             resolution = 0.01,
             value = 1.0)
         self.pConstantAttenuation['command'] = self.setConstantAttenuation
-        self.pConstantAttenuation.pack(fill = Tkinter.X, expand = 0)
+        self.pConstantAttenuation.pack(fill = tkinter.X, expand = 0)
         self.bind(self.pConstantAttenuation,
                   'Set point light constant attenuation')
 
@@ -163,7 +170,7 @@ class lightingPanel(AppShell):
             resolution = 0.01,
             value = 0.0)
         self.pLinearAttenuation['command'] = self.setLinearAttenuation
-        self.pLinearAttenuation.pack(fill = Tkinter.X, expand = 0)
+        self.pLinearAttenuation.pack(fill = tkinter.X, expand = 0)
         self.bind(self.pLinearAttenuation,
                   'Set point light linear attenuation')
 
@@ -174,7 +181,7 @@ class lightingPanel(AppShell):
             resolution = 0.01,
             value = 0.0)
         self.pQuadraticAttenuation['command'] = self.setQuadraticAttenuation
-        self.pQuadraticAttenuation.pack(fill = Tkinter.X, expand = 0)
+        self.pQuadraticAttenuation.pack(fill = tkinter.X, expand = 0)
         self.bind(self.pQuadraticAttenuation,
                   'Set point light quadratic attenuation')
 
@@ -182,7 +189,7 @@ class lightingPanel(AppShell):
         self.sSpecularColor = seColorEntry(
             spotPage, text = 'Specular Color')
         self.sSpecularColor['command'] = self.setSpecularColor
-        self.sSpecularColor.pack(fill = Tkinter.X, expand = 0)
+        self.sSpecularColor.pack(fill = tkinter.X, expand = 0)
         self.bind(self.sSpecularColor,
                   'Set spot light specular color')
 
@@ -193,7 +200,7 @@ class lightingPanel(AppShell):
             resolution = 0.01,
             value = 1.0)
         self.sConstantAttenuation['command'] = self.setConstantAttenuation
-        self.sConstantAttenuation.pack(fill = Tkinter.X, expand = 0)
+        self.sConstantAttenuation.pack(fill = tkinter.X, expand = 0)
         self.bind(self.sConstantAttenuation,
                   'Set spot light constant attenuation')
 
@@ -204,7 +211,7 @@ class lightingPanel(AppShell):
             resolution = 0.01,
             value = 0.0)
         self.sLinearAttenuation['command'] = self.setLinearAttenuation
-        self.sLinearAttenuation.pack(fill = Tkinter.X, expand = 0)
+        self.sLinearAttenuation.pack(fill = tkinter.X, expand = 0)
         self.bind(self.sLinearAttenuation,
                   'Set spot light linear attenuation')
 
@@ -215,7 +222,7 @@ class lightingPanel(AppShell):
             resolution = 0.01,
             value = 0.0)
         self.sQuadraticAttenuation['command'] = self.setQuadraticAttenuation
-        self.sQuadraticAttenuation.pack(fill = Tkinter.X, expand = 0)
+        self.sQuadraticAttenuation.pack(fill = tkinter.X, expand = 0)
         self.bind(self.sQuadraticAttenuation,
                   'Set spot light quadratic attenuation')
 
@@ -226,16 +233,16 @@ class lightingPanel(AppShell):
             resolution = 0.01,
             value = 0.0)
         self.sExponent['command'] = self.setExponent
-        self.sExponent.pack(fill = Tkinter.X, expand = 0)
+        self.sExponent.pack(fill = tkinter.X, expand = 0)
         self.bind(self.sExponent,
                   'Set spot light exponent')
 
         # MRM: Add frustum controls
 
         self.lightNotebook.setnaturalsize()
-        self.lightNotebook.pack(expand = 1, fill = Tkinter.BOTH)
+        self.lightNotebook.pack(expand = 1, fill = tkinter.BOTH)
 
-        mainFrame.pack(expand=1, fill = Tkinter.BOTH)
+        mainFrame.pack(expand=1, fill = tkinter.BOTH)
 
     def onDestroy(self, event):
         messenger.send('LP_close')

+ 4 - 5
contrib/src/sceneeditor/propertyWindow.py

@@ -11,8 +11,7 @@ from direct.tkwidgets import Floater
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Slider
 from direct.tkwidgets import VectorWidgets
-from pandac.PandaModules import *
-from Tkinter import *
+from panda3d.core import *
 import Pmw
 
 class propertyWindow(AppShell,Pmw.MegaWidget):
@@ -108,7 +107,7 @@ class propertyWindow(AppShell,Pmw.MegaWidget):
 
         self.curveFrame = None
         #### If nodePath has been binded with any curves
-        if self.info.has_key('curveList'):
+        if 'curveList' in self.info:
             self.createCurveFrame(self.contentFrame)
 
         ## Set all stuff done
@@ -271,7 +270,7 @@ class propertyWindow(AppShell,Pmw.MegaWidget):
         # And, it will set the call back function to setNodeColorVec()
         #################################################################
         color = self.nodePath.getColor()
-        print color
+        print(color)
         self.nodeColor = VectorWidgets.ColorEntry(
             contentFrame, text = 'Node Color', value=[color.getX()*255,
                                                       color.getY()*255,
@@ -725,7 +724,7 @@ class propertyWindow(AppShell,Pmw.MegaWidget):
         # But, not directly removed be this function.
         # This function will send out a message to notice dataHolder to remove this animation
         #################################################################
-        print anim
+        print(anim)
         widget = self.widgetsDict[anim]
         self.accept('animRemovedFromNode',self.redrawAnimProperty)
         messenger.send('PW_removeAnimFromNode',[self.name, anim])

+ 6 - 8
contrib/src/sceneeditor/quad.py

@@ -9,10 +9,8 @@
 from direct.showbase.ShowBaseGlobal import *
 from direct.interval.IntervalGlobal import *
 from direct.showbase.DirectObject import DirectObject
-from pandac.PandaModules import *
+from panda3d.core import *
 import math
-#Manakel 2/12/2005: replace from pandac import by from pandac.PandaModules import
-from pandac.PandaModules import MouseWatcher
 
 
 class ViewPort:
@@ -506,7 +504,7 @@ class QuadView(DirectObject):
             ansY=-1.0+y2
 
         self.xy=[ansX,ansY]
-        print "Sent X:%f Sent Y:%f"%(ansX,ansY)
+        print("Sent X:%f Sent Y:%f"%(ansX,ansY))
         #SEditor.iRay.pick(render,self.xy)
         SEditor.manipulationControl.manipulationStop(self.xy)
         #print "MouseX " + str(base.mouseWatcherNode.getMouseX()) + "MouseY " + str(base.mouseWatcherNode.getMouseY()) + "\n"
@@ -550,28 +548,28 @@ class QuadView(DirectObject):
             dr.setDimensions(0.5,1,0,0.5)
 
     def setLeft(self):
-        print "LEFT"
+        print("LEFT")
         self.CurrentQuad=3
         self.ChangeBaseDR()
         self.Left.setCam()
         #self.Left.setDR(self.mouseWatcherNode)
 
     def setTop(self):
-        print "TOP"
+        print("TOP")
         self.CurrentQuad=2
         self.ChangeBaseDR()
         self.Top.setCam()
         #self.Top.setDR(self.mouseWatcherNode)
 
     def setPerspective(self):
-        print "PERSPECTIVE"
+        print("PERSPECTIVE")
         self.CurrentQuad=4
         self.ChangeBaseDR()
         self.Perspective.setCam()
         #self.Perspective.setDR(self.mouseWatcherNode)
 
     def setFront(self):
-        print "FRONT"
+        print("FRONT")
         self.CurrentQuad=1
         self.ChangeBaseDR()
         self.Front.setCam()

+ 48 - 37
contrib/src/sceneeditor/sceneEditor.py

@@ -3,11 +3,19 @@ import sys
 try: import _tkinter
 except: sys.exit("Please install python module 'Tkinter'")
 
-import direct
-from direct.directbase.DirectStart import*
+from direct.showbase.ShowBase import ShowBase
+
+ShowBase()
+
 from direct.showbase.TkGlobal import spawnTkLoop
-from Tkinter import *
-from tkFileDialog import *
+
+if sys.version_info >= (3, 0):
+    from tkinter import *
+    from tkinter.filedialog import *
+else:
+    from Tkinter import *
+    from tkFileDialog import *
+
 from direct.directtools.DirectGlobals import *
 from direct.tkwidgets.AppShell import*
 
@@ -251,7 +259,10 @@ class myLevelEditor(AppShell):
         for event in self.actionEvents:
             self.accept(event[0], event[1], extraArgs = event[2:])
 
-        camera.toggleVis()
+        if camera.is_hidden():
+            camera.show()
+        else:
+            camera.hide()
         self.selectNode(base.camera) ## Initially, we select camera as the first node...
 
     def appInit(self):
@@ -386,31 +397,31 @@ class myLevelEditor(AppShell):
             self.showAbout()
             return
         elif buttonIndex==12:
-            print "You haven't defined the function for this Button, Number %d."%buttonIndex
+            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
+            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
+            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
+            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
+            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
+            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
+            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
+            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
+            print("You haven't defined the function for this Button, Number %d."%buttonIndex)
             return
 
         return
@@ -666,17 +677,17 @@ class myLevelEditor(AppShell):
         #################################################################
         type, info = AllScene.getInfoOfThisNode(nodePath)
         name = nodePath.getName()
-        if not self.propertyWindow.has_key(name):
+        if name not in self.propertyWindow:
             self.propertyWindow[name] = propertyWindow(nodePath, type,info )
         pass
 
     def closePropertyWindow(self, name):
-        if self.propertyWindow.has_key(name):
+        if name in self.propertyWindow:
             del self.propertyWindow[name]
         return
 
     def openMetadataPanel(self,nodePath=None):
-        print nodePath
+        print(nodePath)
         self.MetadataPanel=MetadataPanel(nodePath)
         pass
 
@@ -685,7 +696,7 @@ class myLevelEditor(AppShell):
         # duplicate(self, nodePath = None)
         # This function will be called when user try to open the duplication window
         #################################################################
-        print '----Duplication!!'
+        print('----Duplication!!')
         if nodePath != None:
             self.duplicateWindow = duplicateWindow(nodePath = nodePath)
         pass
@@ -791,8 +802,8 @@ class myLevelEditor(AppShell):
         #################################################################
         name = nodePath.getName()
         if AllScene.isActor(name):
-            if self.animPanel.has_key(name):
-                print '---- You already have an animation panel for this Actor!'
+            if name in self.animPanel:
+                print('---- You already have an animation panel for this Actor!')
                 return
             else:
                 Actor = AllScene.getActor(name)
@@ -842,9 +853,9 @@ class myLevelEditor(AppShell):
             # 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):
+            if currentModName in sys.modules:
                 del sys.modules[currentModName]
-                print sys.getrefcount(AllScene.theScene)
+                print(sys.getrefcount(AllScene.theScene))
                 del AllScene.theScene
         else:
             AllScene.resetAll()
@@ -872,9 +883,9 @@ class myLevelEditor(AppShell):
             # 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):
+            if currentModName in sys.modules:
                 del sys.modules[currentModName]
-                print sys.getrefcount(AllScene.theScene)
+                print(sys.getrefcount(AllScene.theScene))
                 del AllScene.theScene
         else:
             AllScene.resetAll()
@@ -886,7 +897,7 @@ class myLevelEditor(AppShell):
 
         thefile=Filename(self.CurrentFileName)
         thedir=thefile.getFullpathWoExtension()
-        print "SCENE EDITOR::" + thedir
+        print("SCENE EDITOR::" + thedir)
         self.CurrentDirName=thedir
         if self.CurrentFileName != None:
             self.parent.title('Scene Editor - '+ Filename.fromOsSpecific(self.CurrentFileName).getBasenameWoExtension())
@@ -934,7 +945,7 @@ class myLevelEditor(AppShell):
             theScene.writeBamFile(fileName)
         else:
             render.writeBamFile(fileName+".bad")
-        print " Scenegraph saved as :" +str(fileName)
+        print(" Scenegraph saved as :" +str(fileName))
 
     def loadFromBam(self):
         fileName = tkFileDialog.askopenfilename(filetypes = [("BAM",".bam")],title = "Load Scenegraph from Bam file")
@@ -959,7 +970,7 @@ class myLevelEditor(AppShell):
         ###############################################################################
         # !!!!! See if a module exists by this name... if it does you cannot use this filename !!!!!
         ###############################################################################
-        if(sys.modules.has_key(fCheck.getBasenameWoExtension())):
+        if(fCheck.getBasenameWoExtension() in sys.modules):
             tkMessageBox.showwarning(
             "Save file",
             "Cannot save with this name because there is a system module with the same name. Please resave as something else."
@@ -993,7 +1004,7 @@ class myLevelEditor(AppShell):
         if modelFilename:
             self.makeDirty()
             if not AllScene.loadModel(modelFilename, Filename.fromOsSpecific(modelFilename)):
-                print '----Error! No Such Model File!'
+                print('----Error! No Such Model File!')
         pass
 
     def loadActor(self):
@@ -1016,12 +1027,12 @@ class myLevelEditor(AppShell):
         if ActorFilename:
             self.makeDirty()
             if not AllScene.loadActor(ActorFilename, Filename.fromOsSpecific(ActorFilename)):
-                print '----Error! No Such Model File!'
+                print('----Error! No Such Model File!')
         pass
 
     def importScene(self):
         self.makeDirty()
-        print '----God bless you Please Import!'
+        print('----God bless you Please Import!')
         pass
 
 
@@ -1495,7 +1506,7 @@ class myLevelEditor(AppShell):
         return
 
     def animPanelClose(self, name):
-        if self.animPanel.has_key(name):
+        if name in self.animPanel:
             del self.animPanel[name]
         return
 
@@ -1508,8 +1519,8 @@ class myLevelEditor(AppShell):
         ################################################################
         name = nodePath.getName()
         if AllScene.isActor(name):
-            if self.animBlendPanel.has_key(name):
-                print '---- You already have an Blend Animation Panel for this Actor!'
+            if name in self.animBlendPanel:
+                print('---- You already have an Blend Animation Panel for this Actor!')
                 return
             else:
                 Actor = AllScene.getActor(name)
@@ -1554,7 +1565,7 @@ class myLevelEditor(AppShell):
         # 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):
+        if name in self.animBlendPanel:
             del self.animBlendPanel[name]
         return
 
@@ -1613,7 +1624,7 @@ class myLevelEditor(AppShell):
 
     def openAlignPanel(self, nodePath=None):
         name = nodePath.getName()
-        if not self.alignPanelDict.has_key(name):
+        if name not in self.alignPanelDict:
             list = AllScene.getAllObjNameAsList()
             if name in list:
                 list.remove(name)
@@ -1623,7 +1634,7 @@ class myLevelEditor(AppShell):
         return
 
     def closeAlignPanel(self, name=None):
-        if self.alignPanelDict.has_key(name):
+        if name in self.alignPanelDict:
             del self.alignPanelDict[name]
 
     def alignObject(self, nodePath, name, list):
@@ -1705,4 +1716,4 @@ class myLevelEditor(AppShell):
 
 editor = myLevelEditor(parent = base.tkRoot)
 
-run()
+base.run()

+ 9 - 5
contrib/src/sceneeditor/seAnimPanel.py

@@ -5,12 +5,16 @@
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import *
 from direct.showbase.TkGlobal import *
-from tkSimpleDialog import askfloat
 import string
 import math
 import types
 from direct.task import Task
 
+if sys.version_info >= (3, 0):
+    from tkinter.simpledialog import askfloat
+else:
+    from tkSimpleDialog import askfloat
+
 FRAMES = 0
 SECONDS = 1
 
@@ -112,7 +116,7 @@ class AnimPanel(AppShell):
         self.playRateEntry.selectitem('1.0')
 
         ### Loop checkbox
-        Label(actorFrame, text= "Loop:", font=('MSSansSerif', 12)).place(x=420,y=05,anchor=NW)
+        Label(actorFrame, text= "Loop:", font=('MSSansSerif', 12)).place(x=420,y=5,anchor=NW)
 
         self.loopVar = IntVar()
         self.loopVar.set(0)
@@ -250,7 +254,7 @@ class AnimPanel(AppShell):
         self['animList'] = self['actor'].getAnimNames()
         animL = self['actor'].getAnimNames()
         self.AnimEntry.setlist(animL)
-        print '-----',animL
+        print('-----',animL)
         return
 
     def loadAnimation(self):
@@ -278,7 +282,7 @@ class AnimPanel(AppShell):
             taskMgr.add(self.playTask, self.id + '_UpdateTask')
             self.stopButton.config(state=NORMAL)
         else:
-            print '----Illegal Animaion name!!', self.animName
+            print('----Illegal Animaion name!!', self.animName)
         return
 
     def playTask(self, task):
@@ -591,7 +595,7 @@ class LoadAnimPanel(AppShell):
         else:
             self.animList.append(name)
         self.AnimName_1.setlist(self.animList)
-        print self.animDic
+        print(self.animDic)
         return
 
     def ok_press(self):

+ 9 - 5
contrib/src/sceneeditor/seBlendAnimPanel.py

@@ -5,12 +5,16 @@
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import *
 from direct.showbase.TkGlobal import *
-from tkSimpleDialog import askfloat
 import string
 import math
 import types
 from direct.task import Task
 
+if sys.version_info >= (3, 0):
+    from tkinter.simpledialog import askfloat
+else:
+    from tkSimpleDialog import askfloat
+
 FRAMES = 0
 SECONDS = 1
 
@@ -304,7 +308,7 @@ class BlendAnimPanel(AppShell):
             taskMgr.add(self.playTask, self.id + '_UpdateTask')
             self.stopButton.config(state=NORMAL)
         else:
-            print '----Illegal Animaion name!!', self.animNameA +  ',  '+ self.animNameB
+            print('----Illegal Animaion name!!', self.animNameA +  ',  '+ self.animNameB)
         return
 
     def playTask(self, task):
@@ -348,7 +352,7 @@ class BlendAnimPanel(AppShell):
         # setAnimation(self, animation, AB = 'a')
         # see play(self)
         #################################################################
-        print 'OK!!!'
+        print('OK!!!')
         if AB == 'a':
             if self.animNameA != None:
                 self['actor'].setControlEffect(self.animNameA, 1.0, 'modelRoot','lodRoot')
@@ -519,7 +523,7 @@ class BlendAnimPanel(AppShell):
         # 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):
+        if name in self.blendDict:
             self.currentBlendName = name
             animA = self.blendDict[name][0]
             animB = self.blendDict[name][1]
@@ -544,7 +548,7 @@ class BlendAnimPanel(AppShell):
         self.blendDict.clear()
         del self.blendDict
         self.blendDict = dict.copy()
-        print self.blendDict
+        print(self.blendDict)
         if len(self.blendDict)>0:
             self.blendList = self.blendDict.keys()
         else:

+ 10 - 11
contrib/src/sceneeditor/seCameraControl.py

@@ -55,14 +55,14 @@ class DirectCameraControl(DirectObject):
             ['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],
+            ['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],
@@ -351,7 +351,7 @@ class DirectCameraControl(DirectObject):
             # 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`)
+            SEditor.message('COA Distance: ' + repr(dist))
             coa.set(0,dist,0)
         # Compute COA Dist
         coaDist = Vec3(coa - ZERO_POINT).length()
@@ -392,8 +392,7 @@ class DirectCameraControl(DirectObject):
             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')
+        self.coaMarker.colorInterval(3.0, VBase4(1, 0, 0, 0), name='fadeAway').start()
 
     def homeCam(self):
         # Record undo point

+ 8 - 2
contrib/src/sceneeditor/seColorEntry.py

@@ -12,9 +12,15 @@
 from direct.tkwidgets import Valuator
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Slider
-import string, Pmw, Tkinter, tkColorChooser
+import sys, Pmw
 from direct.tkwidgets.VectorWidgets import VectorEntry
 
+if sys.version_info >= (3, 0):
+    from tkinter.colorchooser import askcolor
+else:
+    from tkColorChooser import askcolor
+
+
 class seColorEntry(VectorEntry):
     def __init__(self, parent = None, **kw):
         # Initialize options for the class (overriding some superclass options)
@@ -41,7 +47,7 @@ class seColorEntry(VectorEntry):
 
     def popupColorPicker(self):
         # Can pass in current color with: color = (255, 0, 0)
-        color = tkColorChooser.askcolor(
+        color = askcolor(
             parent = self.interior(),
             # Initialize it to current color
             initialcolor = tuple(self.get()[:3]))[0]

+ 18 - 18
contrib/src/sceneeditor/seFileSaver.py

@@ -3,7 +3,7 @@
 # This code saves the scene out as python code... the scene is stored in the various dictionaries in "dataHolder.py" ...the class "AllScene"
 #
 ####################################################################################################################################################
-from pandac.PandaModules import *
+from panda3d.core import *
 
 from direct.showbase.ShowBaseGlobal import *
 import os
@@ -42,7 +42,7 @@ class FileSaver:
         i1="    " # indentation
         i2=i1+i1  # double indentation
         out_file = open(filename,"w")
-        print "dirname:" + dirname
+        print("dirname:" + dirname)
         if( not os.path.isdir(dirname)):
             os.mkdir(dirname)
         savepathname=Filename(filename)
@@ -176,7 +176,7 @@ class FileSaver:
                     newtexpathF=Filename(newtexpath)
                     newtexpathSpecific=newtexpathF.toOsSpecific()
 
-                    print "TEXTURE SAVER:: copying" + oldtexpath + " to " + newtexpathSpecific
+                    print("TEXTURE SAVER:: copying" + oldtexpath + " to " + newtexpathSpecific)
                     if(oldtexpath != newtexpathSpecific):
                         shutil.copyfile(oldtexpath,newtexpathSpecific)
 
@@ -187,7 +187,7 @@ class FileSaver:
 
                 # Copy the file over to the relative directory
                 oldModelpath=AllScene.ModelRefDic[model].toOsSpecific()
-                print "FILESAVER:: copying from " + AllScene.ModelRefDic[model].toOsSpecific() + "to" + newpathSpecific
+                print("FILESAVER:: copying from " + AllScene.ModelRefDic[model].toOsSpecific() + "to" + newpathSpecific)
                 if(oldModelpath!=newpathSpecific):
                     shutil.copyfile(oldModelpath,newpathSpecific)
 
@@ -197,7 +197,7 @@ class FileSaver:
                 etc=EggTextureCollection()
                 etc.extractTextures(e)
                 for index in range(len(fnamelist)):
-                    print fnamelist[index]
+                    print(fnamelist[index])
                     tex=etc.findFilename(Filename(fnamelist[index]))
                     fn=Filename(tex.getFilename())
                     fn.setDirname("")
@@ -305,14 +305,14 @@ class FileSaver:
                     newtexpath=dirname + "/" + texfilename.getBasename()
                     newtexpathF=Filename(newtexpath)
                     newtexpathSpecific=newtexpathF.toOsSpecific()
-                    print "TEXTURE SAVER:: copying" + oldtexpath + " to " + newtexpathSpecific
+                    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
+                print("FILESAVER:: copying from " + AllScene.ActorRefDic[actor].toOsSpecific() + "to" + newpathSpecific)
                 if(oldActorpath!=newpathSpecific):
                     shutil.copyfile(oldActorpath,newpathSpecific)
 
@@ -322,7 +322,7 @@ class FileSaver:
                 etc=EggTextureCollection()
                 etc.extractTextures(e)
                 for index in range(len(actorfnamelist)):
-                    print actorfnamelist[index]
+                    print(actorfnamelist[index])
                     tex=etc.findFilename(Filename(actorfnamelist[index]))
                     fn=Filename(tex.getFilename())
                     fn.setDirname("")
@@ -362,12 +362,12 @@ class FileSaver:
                         #out_file.write(i2+ "self."+ actorS + ".loadAnims(" + str(ActorAnimations) +")\n") # Old way with absolute paths
                         #Manakel 2/12/2004: solve the not empty but not defined animation case
                         if not animation is None:
-                            print "ACTOR ANIMATIONS:" + ActorAnimations[animation]
+                            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
+                            print("ACTOR ANIM SAVER:: Comparing" + oldAnim +"and" + newAnim)
                             if(oldAnim!=newAnim):
                                 shutil.copyfile(oldAnim,newAnim)
                             newAnimF=Filename.fromOsSpecific(newAnim)
@@ -379,16 +379,16 @@ class FileSaver:
                 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
+                print("LOAD ANIM STRING BEFORE" + theloadAnimString)
                 theloadAnimString=theloadAnimString.replace('\'self.executionpath +','self.executionpath + \'')
-                print "LOAD ANIM STRING AFTER" + theloadAnimString
+                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
+                if(actor in AllScene.blendAnimDict): # Check if a dictionary of blended animations exists
                     out_file.write(i2+ "self.blendAnimDict[\"" + actorS +"\"]=" + str(AllScene.blendAnimDict[actor]) + "\n")
 
 
@@ -458,7 +458,7 @@ class FileSaver:
 
                 pass
             else:
-                 print "Invalid Collision Node: " + nodetype
+                 print("Invalid Collision Node: " + nodetype)
             out_file.write("\n")
 
 
@@ -653,7 +653,7 @@ class FileSaver:
             if(parent=="render" or parent=="camera"):
                 out_file.write(i2+ "self."+ modelS + ".reparentTo(" + parent + ")\n")
             else:
-                if(AllScene.particleDict.has_key(parent)):
+                if(parent in AllScene.particleDict):
                     out_file.write(i2+ "self."+ modelS + ".reparentTo(self." + parent + ".getEffect())\n")
                 else:
                     out_file.write(i2+ "self."+ modelS + ".reparentTo(self." + parent + ")\n")
@@ -666,7 +666,7 @@ class FileSaver:
             if(parent=="render" or parent=="camera"):
                 out_file.write(i2+ "self."+ dummyS + ".reparentTo(" + parent + ")\n")
             else:
-                if(AllScene.particleDict.has_key(parent)):
+                if(parent in AllScene.particleDict):
                     out_file.write(i2+ "self."+ dummyS + ".reparentTo(self." + parent + ".getEffect())\n")
                 else:
                     out_file.write(i2+ "self."+ dummyS + ".reparentTo(self." + parent + ")\n")
@@ -680,7 +680,7 @@ class FileSaver:
             if(parent=="render" or parent=="camera"):
                 out_file.write(i2+ "self."+ actorS + ".reparentTo(" + parent + ")\n")
             else:
-                if(AllScene.particleDict.has_key(parent)):
+                if(parent in AllScene.particleDict):
                     out_file.write(i2+ "self."+ actorS + ".reparentTo(self." + parent + ".getEffect())\n")
                 else:
                     out_file.write(i2+ "self."+ actorS + ".reparentTo(self." + parent + ")\n")
@@ -698,7 +698,7 @@ class FileSaver:
                 out_file.write(i2+"self.collisionDict[\"" + collnodeS + "\"]="+ parentname + ".attachNewNode(self." + collnodeS + "_Node)\n")
             else:
                 #Manakel 2/12/2005: parent replaced by parent Name but why Parent name in partice and parent for other objects?
-                if(AllScene.particleDict.has_key(parentname)):
+                if(parentname in AllScene.particleDict):
                     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")

+ 1 - 3
contrib/src/sceneeditor/seForceGroup.py

@@ -1,8 +1,6 @@
-from pandac.PandaModules import *
+from panda3d.core import *
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.PhysicsManagerGlobal import *
-#Manakel 2/12/2005: replace from pandac import by from pandac.PandaModules import
-from pandac.PandaModules import ForceNode
 from direct.directnotify import DirectNotifyGlobal
 import sys
 

+ 8 - 8
contrib/src/sceneeditor/seGeometry.py

@@ -12,7 +12,7 @@
 #
 #################################################################
 
-from pandac.PandaModules import *
+from panda3d.core import *
 from direct.directtools.DirectGlobals import *
 from direct.directtools.DirectUtil import *
 import math
@@ -41,10 +41,10 @@ class LineNodePath(NodePath):
         ls.setColor(colorVec)
 
     def moveTo( self, *_args ):
-        apply( self.lineSegs.moveTo, _args )
+        self.lineSegs.moveTo(*_args)
 
     def drawTo( self, *_args ):
-        apply( self.lineSegs.drawTo, _args )
+        self.lineSegs.drawTo(*_args)
 
     def create( self, frameAccurate = 0 ):
         self.lineSegs.create( self.lineNode, frameAccurate )
@@ -60,13 +60,13 @@ class LineNodePath(NodePath):
         self.lineSegs.setThickness( thickness )
 
     def setColor( self, *_args ):
-        apply( self.lineSegs.setColor, _args )
+        self.lineSegs.setColor(*_args)
 
     def setVertex( self, *_args):
-        apply( self.lineSegs.setVertex, _args )
+        self.lineSegs.setVertex(*_args)
 
     def setVertexColor( self, vertex, *_args ):
-        apply( self.lineSegs.setVertexColor, (vertex,) + _args )
+        self.lineSegs.setVertexColor(*(vertex,) + _args)
 
     def getCurrentPosition( self ):
         return self.lineSegs.getCurrentPosition()
@@ -132,9 +132,9 @@ class LineNodePath(NodePath):
         Given a list of lists of points, draw a separate line for each list
         """
         for pointList in lineList:
-            apply(self.moveTo, pointList[0])
+            self.moveTo(*pointList[0])
             for point in pointList[1:]:
-                apply(self.drawTo, point)
+                self.drawTo(*point)
 
 ##
 ## Given a point in space, and a direction, find the point of intersection

+ 10 - 11
contrib/src/sceneeditor/seLights.py

@@ -3,9 +3,8 @@
 # Written by Yi-Hong Lin, [email protected], 2004
 #################################################################
 from direct.showbase.DirectObject import *
-from string import lower
 from direct.directtools import DirectUtil
-from pandac.PandaModules import *
+from panda3d.core import *
 import string
 
 
@@ -341,7 +340,7 @@ class seLightManager(NodePath):
         if type == 'ambient':
             self.ambientCount += 1
             if(name=='DEFAULT_NAME'):
-                light = AmbientLight('ambient_' + `self.ambientCount`)
+                light = AmbientLight('ambient_' + repr(self.ambientCount))
             else:
                 light = AmbientLight(name)
 
@@ -350,7 +349,7 @@ class seLightManager(NodePath):
         elif type == 'directional':
             self.directionalCount += 1
             if(name=='DEFAULT_NAME'):
-                light = DirectionalLight('directional_' + `self.directionalCount`)
+                light = DirectionalLight('directional_' + repr(self.directionalCount))
             else:
                 light = DirectionalLight(name)
 
@@ -360,7 +359,7 @@ class seLightManager(NodePath):
         elif type == 'point':
             self.pointCount += 1
             if(name=='DEFAULT_NAME'):
-                light = PointLight('point_' + `self.pointCount`)
+                light = PointLight('point_' + repr(self.pointCount))
             else:
                 light = PointLight(name)
 
@@ -371,7 +370,7 @@ class seLightManager(NodePath):
         elif type == 'spot':
             self.spotCount += 1
             if(name=='DEFAULT_NAME'):
-                light = Spotlight('spot_' + `self.spotCount`)
+                light = Spotlight('spot_' + repr(self.spotCount))
             else:
                 light = Spotlight(name)
 
@@ -382,7 +381,7 @@ class seLightManager(NodePath):
             light.setAttenuation(Vec3(constant, linear, quadratic))
             light.setExponent(exponent)
         else:
-            print 'Invalid light type'
+            print('Invalid light type')
             return None
 
         # Create the seLight objects and put the light object we just created into it.
@@ -411,7 +410,7 @@ class seLightManager(NodePath):
         # Attention!!
         # only Spotlight obj nneds to be specified a lens node first. i.e. setLens() first!
         #################################################################
-        type = lower(light.getType().getName())
+        type = light.getType().getName().lower()
 
         specularColor = VBase4(1)
         position = Point3(0,0,0)
@@ -451,7 +450,7 @@ class seLightManager(NodePath):
             quadratic = Attenuation.getZ()
             exponent = light.getExponent()
         else:
-            print 'Invalid light type'
+            print('Invalid light type')
             return None
 
         lightNode = seLight(light,self,type,
@@ -508,7 +507,7 @@ class seLightManager(NodePath):
         # isLight(self.name)
         # Use a string as a index to check if there existing a light named "name"
         #################################################################
-        return self.lightDict.has_key(name)
+        return name in self.lightDict
 
     def rename(self,oName,nName):
         #################################################################
@@ -523,7 +522,7 @@ class seLightManager(NodePath):
             del self.lightDict[oName]
             return self.lightDict.keys(),lightNode
         else:
-            print '----Light Mnager: No such Light!'
+            print('----Light Mnager: No such Light!')
 
     def getLightNodeList(self):
         #################################################################

+ 1 - 1
contrib/src/sceneeditor/seManipulation.py

@@ -520,7 +520,7 @@ class ObjectHandles(NodePath,DirectObject):
         # To avoid recreating a vec every frame
         self.hitPt = Vec3(0)
         # Get a handle on the components
-        self.xHandles = self.find('**/X')
+        self.xHandles = self.find('**/ohScalingNode')
         self.xPostGroup = self.xHandles.find('**/x-post-group')
         self.xPostCollision = self.xHandles.find('**/x-post')
         self.xRingGroup = self.xHandles.find('**/x-ring-group')

+ 153 - 146
contrib/src/sceneeditor/seMopathRecorder.py

@@ -25,10 +25,17 @@ from direct.tkwidgets.Slider import Slider
 from direct.tkwidgets.EntryScale import EntryScale
 from direct.tkwidgets.VectorWidgets import Vector2Entry, Vector3Entry
 from direct.tkwidgets.VectorWidgets import ColorEntry
-from Tkinter import Button, Frame, Radiobutton, Checkbutton, Label
-from Tkinter import StringVar, BooleanVar, Entry, Scale
-import os, string, Tkinter, Pmw
-import __builtin__
+import os, string, sys, Pmw
+
+if sys.version_info >= (3, 0):
+    from tkinter import Button, Frame, Radiobutton, Checkbutton, Label
+    from tkinter import StringVar, BooleanVar, Entry, Scale
+    import tkinter
+else:
+    from Tkinter import Button, Frame, Radiobutton, Checkbutton, Label
+    from Tkinter import StringVar, BooleanVar, Entry, Scale
+    import Tkinter as tkinter
+
 
 PRF_UTILITIES = [
     'lambda: camera.lookAt(render)',
@@ -123,7 +130,7 @@ class MopathRecorder(AppShell, DirectObject):
         self.postPoints = []
         self.pointSetDict = {}
         self.pointSetCount = 0
-        self.pointSetName = self.name + '-ps-' + `self.pointSetCount`
+        self.pointSetName = self.name + '-ps-' + repr(self.pointSetCount)
         # User callback to call before recording point
         self.samplingMode = 'Continuous'
         self.preRecordFunc = None
@@ -233,7 +240,7 @@ class MopathRecorder(AppShell, DirectObject):
             self.undoButton['state'] = 'normal'
         else:
             self.undoButton['state'] = 'disabled'
-        self.undoButton.pack(side = Tkinter.LEFT, expand = 0)
+        self.undoButton.pack(side = tkinter.LEFT, expand = 0)
         self.bind(self.undoButton, 'Undo last operation')
 
         self.redoButton = Button(self.menuFrame, text = 'Redo',
@@ -242,19 +249,19 @@ class MopathRecorder(AppShell, DirectObject):
             self.redoButton['state'] = 'normal'
         else:
             self.redoButton['state'] = 'disabled'
-        self.redoButton.pack(side = Tkinter.LEFT, expand = 0)
+        self.redoButton.pack(side = tkinter.LEFT, expand = 0)
         self.bind(self.redoButton, 'Redo last operation')
 
         # Record button
-        mainFrame = Frame(interior, relief = Tkinter.SUNKEN, borderwidth = 2)
+        mainFrame = Frame(interior, relief = tkinter.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 = Tkinter.LEFT, expand = 0)
-        widget['relief'] = Tkinter.FLAT
+                                   side = tkinter.LEFT, expand = 0)
+        widget['relief'] = tkinter.FLAT
         self.nodePathMenu = Pmw.ComboBox(
             frame, entry_width = 20,
             selectioncommand = self.selectNodePathNamed,
@@ -264,7 +271,7 @@ class MopathRecorder(AppShell, DirectObject):
             self.nodePathMenu.component('entryfield_entry'))
         self.nodePathMenuBG = (
             self.nodePathMenuEntry.configure('background')[3])
-        self.nodePathMenu.pack(side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
+        self.nodePathMenu.pack(side = tkinter.LEFT, fill = tkinter.X, expand = 1)
         self.bind(self.nodePathMenu,
                   'Select active node path used for recording and playback')
         # Recording type
@@ -285,81 +292,81 @@ class MopathRecorder(AppShell, DirectObject):
             'Recording', 'Extend',
             ('Next record session extends existing path'),
             self.recordingType, 'Extend', expand = 0)
-        frame.pack(fill = Tkinter.X, expand = 1)
+        frame.pack(fill = tkinter.X, expand = 1)
 
         frame = Frame(mainFrame)
         widget = self.createCheckbutton(
             frame, 'Recording', 'Record',
             'On: path is being recorded', self.toggleRecord, 0,
-            side = Tkinter.LEFT, fill = Tkinter.BOTH, expand = 1)
-        widget.configure(foreground = 'Red', relief = Tkinter.RAISED, borderwidth = 2,
-                         anchor = Tkinter.CENTER, width = 16)
+            side = tkinter.LEFT, fill = tkinter.BOTH, expand = 1)
+        widget.configure(foreground = 'Red', relief = tkinter.RAISED, borderwidth = 2,
+                         anchor = tkinter.CENTER, width = 16)
         widget = self.createButton(frame, 'Recording', 'Add Keyframe',
                                    'Add Keyframe To Current Path',
                                    self.addKeyframe,
-                                   side = Tkinter.LEFT, expand = 1)
+                                   side = tkinter.LEFT, expand = 1)
 
         widget = self.createButton(frame, 'Recording', 'Bind Path to Node',
                                    'Bind Motion Path to selected Object',
                                    self.bindMotionPathToNode,
-                                   side = Tkinter.LEFT, expand = 1)
+                                   side = tkinter.LEFT, expand = 1)
 
 
-        frame.pack(fill = Tkinter.X, expand = 1)
+        frame.pack(fill = tkinter.X, expand = 1)
 
-        mainFrame.pack(expand = 1, fill = Tkinter.X, pady = 3)
+        mainFrame.pack(expand = 1, fill = tkinter.X, pady = 3)
 
         # Playback controls
-        playbackFrame = Frame(interior, relief = Tkinter.SUNKEN,
+        playbackFrame = Frame(interior, relief = tkinter.SUNKEN,
                               borderwidth = 2)
         Label(playbackFrame, text = 'PLAYBACK CONTROLS',
-              font=('MSSansSerif', 12, 'bold')).pack(fill = Tkinter.X)
+              font=('MSSansSerif', 12, 'bold')).pack(fill = tkinter.X)
         # Main playback control slider
         widget = self.createEntryScale(
             playbackFrame, 'Playback', 'Time', 'Set current playback time',
-            resolution = 0.01, command = self.playbackGoTo, side = Tkinter.TOP)
-        widget.component('hull')['relief'] = Tkinter.RIDGE
+            resolution = 0.01, command = self.playbackGoTo, side = tkinter.TOP)
+        widget.component('hull')['relief'] = tkinter.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 = Tkinter.LEFT, expand = 0)
+                                side = tkinter.LEFT, expand = 0)
         # Start stop buttons
         frame = Frame(playbackFrame)
         widget = self.createButton(frame, 'Playback', '<<',
                                    'Jump to start of playback',
                                    self.jumpToStartOfPlayback,
-                                   side = Tkinter.LEFT, expand = 1)
+                                   side = tkinter.LEFT, expand = 1)
         widget['font'] = (('MSSansSerif', 12, 'bold'))
         widget = self.createCheckbutton(frame, 'Playback', 'Play',
                                         'Start/Stop playback',
                                         self.startStopPlayback, 0,
-                                        side = Tkinter.LEFT, fill = Tkinter.BOTH, expand = 1)
+                                        side = tkinter.LEFT, fill = tkinter.BOTH, expand = 1)
         widget.configure(anchor = 'center', justify = 'center',
-                         relief = Tkinter.RAISED, font = ('MSSansSerif', 12, 'bold'))
+                         relief = tkinter.RAISED, font = ('MSSansSerif', 12, 'bold'))
         widget = self.createButton(frame, 'Playback', '>>',
                                    'Jump to end of playback',
                                    self.jumpToEndOfPlayback,
-                                   side = Tkinter.LEFT, expand = 1)
+                                   side = tkinter.LEFT, expand = 1)
         widget['font'] = (('MSSansSerif', 12, 'bold'))
         self.createCheckbutton(frame, 'Playback', 'Loop',
                                'On: loop playback',
                                self.setLoopPlayback, self.loopPlayback,
-                               side = Tkinter.LEFT, fill = Tkinter.BOTH, expand = 0)
-        frame.pack(fill = Tkinter.X, expand = 1)
+                               side = tkinter.LEFT, fill = tkinter.BOTH, expand = 0)
+        frame.pack(fill = tkinter.X, expand = 1)
 
         # Speed control
         frame = Frame(playbackFrame)
-        widget = Button(frame, text = 'PB Speed Vernier', relief = Tkinter.FLAT,
+        widget = Button(frame, text = 'PB Speed Vernier', relief = tkinter.FLAT,
                         command = lambda s = self: s.setSpeedScale(1.0))
-        widget.pack(side = Tkinter.LEFT, expand = 0)
+        widget.pack(side = tkinter.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 = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
+        self.speedScale.pack(side = tkinter.LEFT, fill = tkinter.X, expand = 1)
         self.speedVar = StringVar()
         self.speedVar.set("0.00")
         self.speedEntry = Entry(frame, textvariable = self.speedVar,
@@ -368,14 +375,14 @@ class MopathRecorder(AppShell, DirectObject):
             '<Return>',
             lambda e = None, s = self: s.setSpeedScale(
             string.atof(s.speedVar.get())))
-        self.speedEntry.pack(side = Tkinter.LEFT, expand = 0)
-        frame.pack(fill = Tkinter.X, expand = 1)
+        self.speedEntry.pack(side = tkinter.LEFT, expand = 0)
+        frame.pack(fill = tkinter.X, expand = 1)
 
-        playbackFrame.pack(fill = Tkinter.X, pady = 2)
+        playbackFrame.pack(fill = tkinter.X, pady = 2)
 
         # Create notebook pages
         self.mainNotebook = Pmw.NoteBook(interior)
-        self.mainNotebook.pack(fill = Tkinter.BOTH, expand = 1)
+        self.mainNotebook.pack(fill = tkinter.BOTH, expand = 1)
         self.resamplePage = self.mainNotebook.add('Resample')
         self.refinePage = self.mainNotebook.add('Refine')
         self.extendPage = self.mainNotebook.add('Extend')
@@ -386,35 +393,35 @@ class MopathRecorder(AppShell, DirectObject):
         ## RESAMPLE PAGE
         label = Label(self.resamplePage, text = 'RESAMPLE CURVE',
                       font=('MSSansSerif', 12, 'bold'))
-        label.pack(fill = Tkinter.X)
+        label.pack(fill = tkinter.X)
 
         # Resample
         resampleFrame = Frame(
-            self.resamplePage, relief = Tkinter.SUNKEN, borderwidth = 2)
+            self.resamplePage, relief = tkinter.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'] = Tkinter.RIDGE
+        widget.component('hull')['relief'] = tkinter.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 = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
+            self.makeEven, side = tkinter.LEFT, fill = tkinter.X, expand = 1)
         self.createButton(
             frame, 'Resample', 'Face Forward',
             'Compute HPR so resulting hpr curve faces along xyz tangent',
-            self.faceForward, side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
-        frame.pack(fill = Tkinter.X, expand = 0)
-        resampleFrame.pack(fill = Tkinter.X, expand = 0, pady = 2)
+            self.faceForward, side = tkinter.LEFT, fill = tkinter.X, expand = 1)
+        frame.pack(fill = tkinter.X, expand = 0)
+        resampleFrame.pack(fill = tkinter.X, expand = 0, pady = 2)
 
         # Desample
         desampleFrame = Frame(
-            self.resamplePage, relief = Tkinter.SUNKEN, borderwidth = 2)
+            self.resamplePage, relief = tkinter.SUNKEN, borderwidth = 2)
         Label(desampleFrame, text = 'DESAMPLE CURVE',
               font=('MSSansSerif', 12, 'bold')).pack()
         widget = self.createSlider(
@@ -422,16 +429,16 @@ class MopathRecorder(AppShell, DirectObject):
             'Specify number of points to skip between samples',
             min = 1, max = 100, resolution = 1,
             command = self.setDesampleFrequency)
-        widget.component('hull')['relief'] = Tkinter.RIDGE
+        widget.component('hull')['relief'] = tkinter.RIDGE
         widget['postCallback'] = self.desampleCurve
-        desampleFrame.pack(fill = Tkinter.X, expand = 0, pady = 2)
+        desampleFrame.pack(fill = tkinter.X, expand = 0, pady = 2)
 
         ## REFINE PAGE ##
-        refineFrame = Frame(self.refinePage, relief = Tkinter.SUNKEN,
+        refineFrame = Frame(self.refinePage, relief = tkinter.SUNKEN,
                             borderwidth = 2)
         label = Label(refineFrame, text = 'REFINE CURVE',
                       font=('MSSansSerif', 12, 'bold'))
-        label.pack(fill = Tkinter.X)
+        label.pack(fill = tkinter.X)
 
         widget = self.createSlider(refineFrame,
                                        'Refine Page', 'Refine From',
@@ -460,14 +467,14 @@ class MopathRecorder(AppShell, DirectObject):
                                        command = self.setRefineStop)
         widget['preCallback'] = self.setRefineMode
         widget['postCallback'] = self.getPostPoints
-        refineFrame.pack(fill = Tkinter.X)
+        refineFrame.pack(fill = tkinter.X)
 
         ## EXTEND PAGE ##
-        extendFrame = Frame(self.extendPage, relief = Tkinter.SUNKEN,
+        extendFrame = Frame(self.extendPage, relief = tkinter.SUNKEN,
                             borderwidth = 2)
         label = Label(extendFrame, text = 'EXTEND CURVE',
                       font=('MSSansSerif', 12, 'bold'))
-        label.pack(fill = Tkinter.X)
+        label.pack(fill = tkinter.X)
 
         widget = self.createSlider(extendFrame,
                                        'Extend Page', 'Extend From',
@@ -483,14 +490,14 @@ class MopathRecorder(AppShell, DirectObject):
             resolution = 0.01,
             command = self.setControlStart)
         widget['preCallback'] = self.setExtendMode
-        extendFrame.pack(fill = Tkinter.X)
+        extendFrame.pack(fill = tkinter.X)
 
         ## CROP PAGE ##
-        cropFrame = Frame(self.cropPage, relief = Tkinter.SUNKEN,
+        cropFrame = Frame(self.cropPage, relief = tkinter.SUNKEN,
                             borderwidth = 2)
         label = Label(cropFrame, text = 'CROP CURVE',
                       font=('MSSansSerif', 12, 'bold'))
-        label.pack(fill = Tkinter.X)
+        label.pack(fill = tkinter.X)
 
         widget = self.createSlider(
             cropFrame,
@@ -508,11 +515,11 @@ class MopathRecorder(AppShell, DirectObject):
 
         self.createButton(cropFrame, 'Crop Page', 'Crop Curve',
                           'Crop curve to specified from to times',
-                          self.cropCurve, fill = Tkinter.NONE)
-        cropFrame.pack(fill = Tkinter.X)
+                          self.cropCurve, fill = tkinter.NONE)
+        cropFrame.pack(fill = tkinter.X)
 
         ## DRAW PAGE ##
-        drawFrame = Frame(self.drawPage, relief = Tkinter.SUNKEN,
+        drawFrame = Frame(self.drawPage, relief = tkinter.SUNKEN,
                            borderwidth = 2)
 
         self.sf = Pmw.ScrolledFrame(self.drawPage, horizflex = 'elastic')
@@ -521,57 +528,57 @@ class MopathRecorder(AppShell, DirectObject):
 
         label = Label(sfFrame, text = 'CURVE RENDERING STYLE',
                       font=('MSSansSerif', 12, 'bold'))
-        label.pack(fill = Tkinter.X)
+        label.pack(fill = tkinter.X)
 
         frame = Frame(sfFrame)
-        Label(frame, text = 'SHOW:').pack(side = Tkinter.LEFT, expand = 0)
+        Label(frame, text = 'SHOW:').pack(side = tkinter.LEFT, expand = 0)
         widget = self.createCheckbutton(
             frame, 'Style', 'Path',
             'On: path is visible', self.setPathVis, 1,
-            side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
+            side = tkinter.LEFT, fill = tkinter.X, expand = 1)
         widget = self.createCheckbutton(
             frame, 'Style', 'Knots',
             'On: path knots are visible', self.setKnotVis, 1,
-            side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
+            side = tkinter.LEFT, fill = tkinter.X, expand = 1)
         widget = self.createCheckbutton(
             frame, 'Style', 'CVs',
             'On: path CVs are visible', self.setCvVis, 0,
-            side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
+            side = tkinter.LEFT, fill = tkinter.X, expand = 1)
         widget = self.createCheckbutton(
             frame, 'Style', 'Hull',
             'On: path hull is visible', self.setHullVis, 0,
-            side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
+            side = tkinter.LEFT, fill = tkinter.X, expand = 1)
         widget = self.createCheckbutton(
             frame, 'Style', 'Trace',
             'On: record is visible', self.setTraceVis, 0,
-            side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
+            side = tkinter.LEFT, fill = tkinter.X, expand = 1)
         widget = self.createCheckbutton(
             frame, 'Style', 'Marker',
             'On: playback marker is visible', self.setMarkerVis, 0,
-            side = Tkinter.LEFT, fill = Tkinter.X, expand = 1)
-        frame.pack(fill = Tkinter.X, expand = 1)
+            side = tkinter.LEFT, fill = tkinter.X, expand = 1)
+        frame.pack(fill = tkinter.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 = Tkinter.TOP)
-        widget.component('hull')['relief'] = Tkinter.RIDGE
+            command = self.setNumSegs, side = tkinter.TOP)
+        widget.component('hull')['relief'] = tkinter.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 = Tkinter.TOP)
-        widget.component('hull')['relief'] = Tkinter.RIDGE
+            command = self.setNumTicks, side = tkinter.TOP)
+        widget.component('hull')['relief'] = tkinter.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 = Tkinter.TOP)
-        widget.component('hull')['relief'] = Tkinter.RIDGE
+            command = self.setTickScale, side = tkinter.TOP)
+        widget.component('hull')['relief'] = tkinter.RIDGE
         self.createColorEntry(
             sfFrame, 'Style', 'Path Color',
             'Color of curve',
@@ -598,14 +605,14 @@ class MopathRecorder(AppShell, DirectObject):
             command = self.setHullColor,
             value = [255.0,128.0,128.0,255.0])
 
-        #drawFrame.pack(fill = Tkinter.X)
+        #drawFrame.pack(fill = tkinter.X)
 
         ## OPTIONS PAGE ##
-        optionsFrame = Frame(self.optionsPage, relief = Tkinter.SUNKEN,
+        optionsFrame = Frame(self.optionsPage, relief = tkinter.SUNKEN,
                             borderwidth = 2)
         label = Label(optionsFrame, text = 'RECORDING OPTIONS',
                       font=('MSSansSerif', 12, 'bold'))
-        label.pack(fill = Tkinter.X)
+        label.pack(fill = tkinter.X)
         # Hooks
         frame = Frame(optionsFrame)
         widget = self.createLabeledEntry(
@@ -614,7 +621,7 @@ class MopathRecorder(AppShell, DirectObject):
             value = self.startStopHook,
             command = self.setStartStopHook)[0]
         label = self.getWidget('Recording', 'Record Hook-Label')
-        label.configure(width = 16, anchor = Tkinter.W)
+        label.configure(width = 16, anchor = tkinter.W)
         self.setStartStopHook()
         widget = self.createLabeledEntry(
             frame, 'Recording', 'Keyframe Hook',
@@ -622,9 +629,9 @@ class MopathRecorder(AppShell, DirectObject):
             value = self.keyframeHook,
             command = self.setKeyframeHook)[0]
         label = self.getWidget('Recording', 'Keyframe Hook-Label')
-        label.configure(width = 16, anchor = Tkinter.W)
+        label.configure(width = 16, anchor = tkinter.W)
         self.setKeyframeHook()
-        frame.pack(expand = 1, fill = Tkinter.X)
+        frame.pack(expand = 1, fill = tkinter.X)
         # PreRecordFunc
         frame = Frame(optionsFrame)
         widget = self.createComboBox(
@@ -632,17 +639,17 @@ class MopathRecorder(AppShell, DirectObject):
             'Function called before sampling each point',
             PRF_UTILITIES, self.setPreRecordFunc,
             history = 1, expand = 1)
-        widget.configure(label_width = 16, label_anchor = Tkinter.W)
+        widget.configure(label_width = 16, label_anchor = tkinter.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 = Tkinter.LEFT, fill = Tkinter.BOTH, expand = 0)
-        frame.pack(expand = 1, fill = Tkinter.X)
+                               side = tkinter.LEFT, fill = tkinter.BOTH, expand = 0)
+        frame.pack(expand = 1, fill = tkinter.X)
         # Pack record frame
-        optionsFrame.pack(fill = Tkinter.X, pady = 2)
+        optionsFrame.pack(fill = tkinter.X, pady = 2)
 
         self.mainNotebook.setnaturalsize()
 
@@ -682,16 +689,16 @@ class MopathRecorder(AppShell, DirectObject):
         marker if subnode selected
         """
         taskMgr.remove(self.name + '-curveEditTask')
-        print nodePath.id()
-        if nodePath.id() in self.playbackMarkerIds:
+        print(nodePath.get_key())
+        if nodePath.get_key() in self.playbackMarkerIds:
             SEditor.select(self.playbackMarker)
-        elif nodePath.id() in self.tangentMarkerIds:
+        elif nodePath.get_key() in self.tangentMarkerIds:
             SEditor.select(self.tangentMarker)
-        elif nodePath.id() == self.playbackMarker.id():
+        elif nodePath.get_key() == self.playbackMarker.get_key():
             self.tangentGroup.show()
             taskMgr.add(self.curveEditTask,
                                      self.name + '-curveEditTask')
-        elif nodePath.id() == self.tangentMarker.id():
+        elif nodePath.get_key() == self.tangentMarker.get_key():
             self.tangentGroup.show()
             taskMgr.add(self.curveEditTask,
                                      self.name + '-curveEditTask')
@@ -699,7 +706,7 @@ class MopathRecorder(AppShell, DirectObject):
             self.tangentGroup.hide()
 
     def getChildIds(self, nodePath):
-        ids = [nodePath.id()]
+        ids = [nodePath.get_key()]
         kids = nodePath.getChildren()
         for kid in kids:
             ids += self.getChildIds(kid)
@@ -710,14 +717,14 @@ class MopathRecorder(AppShell, DirectObject):
         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())):
+        if ((nodePath.get_key() == self.playbackMarker.get_key()) or
+            (nodePath.get_key() == self.tangentMarker.get_key())):
             self.tangentGroup.hide()
 
     def curveEditTask(self,state):
         if self.curveCollection != None:
             # Update curve position
-            if self.manipulandumId == self.playbackMarker.id():
+            if self.manipulandumId == self.playbackMarker.get_key():
                 # Show playback marker
                 self.playbackMarker.getChild(0).show()
                 pos = Point3(0)
@@ -731,7 +738,7 @@ class MopathRecorder(AppShell, DirectObject):
                 # Note: this calls recompute on the curves
                 self.nurbsCurveDrawer.draw()
             # Update tangent
-            if self.manipulandumId == self.tangentMarker.id():
+            if self.manipulandumId == self.tangentMarker.get_key():
                 # If manipulating marker, update tangent
                 # Hide playback marker
                 self.playbackMarker.getChild(0).hide()
@@ -766,10 +773,10 @@ class MopathRecorder(AppShell, DirectObject):
     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()
+            if SEditor.selected.last.get_key() == self.playbackMarker.get_key():
+                self.manipulandumId = self.playbackMarker.get_key()
+            elif SEditor.selected.last.get_key() == self.tangentMarker.get_key():
+                self.manipulandumId = self.tangentMarker.get_key()
 
     def manipulateObjectCleanupHook(self):
         # Clear flag
@@ -799,7 +806,7 @@ class MopathRecorder(AppShell, DirectObject):
 
     def createNewPointSet(self, curveName = None):
         if curveName == None:
-            self.pointSetName = self.name + '-ps-' + `self.pointSetCount`
+            self.pointSetName = self.name + '-ps-' + repr(self.pointSetCount)
         else:
             self.pointSetName = curveName
         # Update dictionary and record pointer to new point set
@@ -1137,7 +1144,7 @@ class MopathRecorder(AppShell, DirectObject):
     def computeCurves(self):
         # Check to make sure curve fitters have points
         if (self.curveFitter.getNumSamples() == 0):
-            print 'MopathRecorder.computeCurves: Must define curve first'
+            print('MopathRecorder.computeCurves: Must define curve first')
             return
         # Create curves
         # XYZ
@@ -1282,8 +1289,8 @@ class MopathRecorder(AppShell, DirectObject):
             dictName = name
         else:
             # Generate a unique name for the dict
-            dictName = name # + '-' + `nodePath.id()`
-        if not dict.has_key(dictName):
+            dictName = name # + '-' + repr(nodePath.get_key())
+        if dictName not in dict:
             # Update combo box to include new item
             names.append(dictName)
             listbox = menu.component('scrolledlist')
@@ -1386,7 +1393,7 @@ class MopathRecorder(AppShell, DirectObject):
 
     def desampleCurve(self):
         if (self.curveFitter.getNumSamples() == 0):
-            print 'MopathRecorder.desampleCurve: Must define curve first'
+            print('MopathRecorder.desampleCurve: Must define curve first')
             return
         # NOTE: This is destructive, points will be deleted from curve fitter
         self.curveFitter.desample(self.desampleFrequency)
@@ -1400,7 +1407,7 @@ class MopathRecorder(AppShell, DirectObject):
 
     def sampleCurve(self, fCompute = 1, curveName = None):
         if self.curveCollection == None:
-            print 'MopathRecorder.sampleCurve: Must define curve first'
+            print('MopathRecorder.sampleCurve: Must define curve first')
             return
         # Reset curve fitters
         self.curveFitter.reset()
@@ -1617,7 +1624,7 @@ class MopathRecorder(AppShell, DirectObject):
 
     def cropCurve(self):
         if self.pointSet == None:
-            print 'Empty Point Set'
+            print('Empty Point Set')
             return
         # Keep handle on old points
         oldPoints = self.pointSet
@@ -1653,15 +1660,15 @@ class MopathRecorder(AppShell, DirectObject):
         # Use first directory in model path
         mPath = getModelPath()
         if mPath.getNumDirectories() > 0:
-            if `mPath.getDirectory(0)` == '.':
+            if repr(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'
+            print('MopathRecorder Info: Empty Model Path!')
+            print('Using current directory')
             path = '.'
         mopathFilename = askopenfilename(
             defaultextension = '.egg',
@@ -1692,15 +1699,15 @@ class MopathRecorder(AppShell, DirectObject):
         # Use first directory in model path
         mPath = getModelPath()
         if mPath.getNumDirectories() > 0:
-            if `mPath.getDirectory(0)` == '.':
+            if repr(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'
+            print('MopathRecorder Info: Empty Model Path!')
+            print('Using current directory')
             path = '.'
         mopathFilename = asksaveasfilename(
             defaultextension = '.egg',
@@ -1734,28 +1741,28 @@ class MopathRecorder(AppShell, DirectObject):
 
     def createLabeledEntry(self, parent, category, text, balloonHelp,
                            value = '', command = None,
-                           relief = 'sunken', side = Tkinter.LEFT,
+                           relief = 'sunken', side = tkinter.LEFT,
                            expand = 1, width = 12):
         frame = Frame(parent)
         variable = StringVar()
         variable.set(value)
         label = Label(frame, text = text)
-        label.pack(side = Tkinter.LEFT, fill = Tkinter.X)
+        label.pack(side = tkinter.LEFT, fill = tkinter.X)
         self.bind(label, balloonHelp)
         self.widgetDict[category + '-' + text + '-Label'] = label
         entry = Entry(frame, width = width, relief = relief,
                       textvariable = variable)
-        entry.pack(side = Tkinter.LEFT, fill = Tkinter.X, expand = expand)
+        entry.pack(side = tkinter.LEFT, fill = tkinter.X, expand = expand)
         self.bind(entry, balloonHelp)
         self.widgetDict[category + '-' + text] = entry
         self.variableDict[category + '-' + text] = variable
         if command:
             entry.bind('<Return>', command)
-        frame.pack(side = side, fill = Tkinter.X, expand = expand)
+        frame.pack(side = side, fill = tkinter.X, expand = expand)
         return (frame, label, entry)
 
     def createButton(self, parent, category, text, balloonHelp, command,
-                     side = 'top', expand = 0, fill = Tkinter.X):
+                     side = 'top', expand = 0, fill = tkinter.X):
         widget = Button(parent, text = text)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
@@ -1766,10 +1773,10 @@ class MopathRecorder(AppShell, DirectObject):
 
     def createCheckbutton(self, parent, category, text,
                           balloonHelp, command, initialState,
-                          side = 'top', fill = Tkinter.X, expand = 0):
+                          side = 'top', fill = tkinter.X, expand = 0):
         bool = BooleanVar()
         bool.set(initialState)
-        widget = Checkbutton(parent, text = text, anchor = Tkinter.W,
+        widget = Checkbutton(parent, text = text, anchor = tkinter.W,
                          variable = bool)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
@@ -1781,8 +1788,8 @@ class MopathRecorder(AppShell, DirectObject):
 
     def createRadiobutton(self, parent, side, category, text,
                           balloonHelp, variable, value,
-                          command = None, fill = Tkinter.X, expand = 0):
-        widget = Radiobutton(parent, text = text, anchor = Tkinter.W,
+                          command = None, fill = tkinter.X, expand = 0):
+        widget = Radiobutton(parent, text = text, anchor = tkinter.W,
                              variable = variable, value = value)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
@@ -1798,10 +1805,10 @@ class MopathRecorder(AppShell, DirectObject):
         kw['min'] = min
         kw['maxVelocity'] = maxVelocity
         kw['resolution'] = resolution
-        widget = apply(Floater, (parent,), kw)
+        widget = Floater(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
-        widget.pack(fill = Tkinter.X)
+        widget.pack(fill = tkinter.X)
         self.bind(widget, balloonHelp)
         self.widgetDict[category + '-' + text] = widget
         return widget
@@ -1809,10 +1816,10 @@ class MopathRecorder(AppShell, DirectObject):
     def createAngleDial(self, parent, category, text, balloonHelp,
                         command = None, **kw):
         kw['text'] = text
-        widget = apply(AngleDial,(parent,), kw)
+        widget = AngleDial(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
-        widget.pack(fill = Tkinter.X)
+        widget.pack(fill = tkinter.X)
         self.bind(widget, balloonHelp)
         self.widgetDict[category + '-' + text] = widget
         return widget
@@ -1820,13 +1827,13 @@ class MopathRecorder(AppShell, DirectObject):
     def createSlider(self, parent, category, text, balloonHelp,
                          command = None, min = 0.0, max = 1.0,
                          resolution = None,
-                         side = Tkinter.TOP, fill = Tkinter.X, expand = 1, **kw):
+                         side = tkinter.TOP, fill = tkinter.X, expand = 1, **kw):
         kw['text'] = text
         kw['min'] = min
         kw['max'] = max
         kw['resolution'] = resolution
         #widget = apply(EntryScale, (parent,), kw)
-        widget = apply(Slider, (parent,), kw)
+        widget = 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)
@@ -1837,12 +1844,12 @@ class MopathRecorder(AppShell, DirectObject):
     def createEntryScale(self, parent, category, text, balloonHelp,
                          command = None, min = 0.0, max = 1.0,
                          resolution = None,
-                         side = Tkinter.TOP, fill = Tkinter.X, expand = 1, **kw):
+                         side = tkinter.TOP, fill = tkinter.X, expand = 1, **kw):
         kw['text'] = text
         kw['min'] = min
         kw['max'] = max
         kw['resolution'] = resolution
-        widget = apply(EntryScale, (parent,), kw)
+        widget = 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)
@@ -1854,10 +1861,10 @@ class MopathRecorder(AppShell, DirectObject):
                            command = None, **kw):
         # Set label's text
         kw['text'] = text
-        widget = apply(Vector2Entry, (parent,), kw)
+        widget = Vector2Entry(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
-        widget.pack(fill = Tkinter.X)
+        widget.pack(fill = tkinter.X)
         self.bind(widget, balloonHelp)
         self.widgetDict[category + '-' + text] = widget
         return widget
@@ -1866,10 +1873,10 @@ class MopathRecorder(AppShell, DirectObject):
                            command = None, **kw):
         # Set label's text
         kw['text'] = text
-        widget = apply(Vector3Entry, (parent,), kw)
+        widget = Vector3Entry(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
-        widget.pack(fill = Tkinter.X)
+        widget.pack(fill = tkinter.X)
         self.bind(widget, balloonHelp)
         self.widgetDict[category + '-' + text] = widget
         return widget
@@ -1878,10 +1885,10 @@ class MopathRecorder(AppShell, DirectObject):
                          command = None, **kw):
         # Set label's text
         kw['text'] = text
-        widget = apply(ColorEntry, (parent,) ,kw)
+        widget = ColorEntry(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
-        widget.pack(fill = Tkinter.X)
+        widget.pack(fill = tkinter.X)
         self.bind(widget, balloonHelp)
         self.widgetDict[category + '-' + text] = widget
         return widget
@@ -1891,13 +1898,13 @@ class MopathRecorder(AppShell, DirectObject):
         optionVar = StringVar()
         if len(items) > 0:
             optionVar.set(items[0])
-        widget = Pmw.OptionMenu(parent, labelpos = Tkinter.W, label_text = text,
+        widget = Pmw.OptionMenu(parent, labelpos = tkinter.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 = Tkinter.X)
+        widget.pack(fill = tkinter.X)
         self.bind(widget.component('menubutton'), balloonHelp)
         self.widgetDict[category + '-' + text] = widget
         self.variableDict[category + '-' + text] = optionVar
@@ -1905,9 +1912,9 @@ class MopathRecorder(AppShell, DirectObject):
 
     def createComboBox(self, parent, category, text, balloonHelp,
                        items, command, history = 0,
-                       side = Tkinter.LEFT, expand = 0, fill = Tkinter.X):
+                       side = tkinter.LEFT, expand = 0, fill = tkinter.X):
         widget = Pmw.ComboBox(parent,
-                              labelpos = Tkinter.W,
+                              labelpos = tkinter.W,
                               label_text = text,
                               label_anchor = 'e',
                               label_width = 12,
@@ -1959,14 +1966,14 @@ class MopathRecorder(AppShell, DirectObject):
 
     def bindMotionPathToNode(self):
         if self.curveCollection == None:
-            print '----Error: you need to select or create a curve first!'
+            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
+        print(test)
         self.ignore('MP_checkName')
         del self.askName
         self.curveCollection.getCurve(0).setName(name)
@@ -1993,7 +2000,7 @@ class MopathRecorder(AppShell, DirectObject):
         If the list is not None, it will put the vurve back into the curve list.
         else, do nothing.
         '''
-        print curveList
+        print(curveList)
         self.ignore('curveListFor'+self.name)
         if curveList != None:
             for collection in curveList:
@@ -2037,8 +2044,8 @@ class namePathPanel(AppShell):
 
         dataFrame = Frame(mainFrame)
         label = Label(dataFrame, text='This name will be used as a reference for this Path.',font=('MSSansSerif', 10))
-        label.pack(side = Tkinter.TOP, expand = 0, fill = Tkinter.X)
-        dataFrame.pack(side = Tkinter.TOP, expand = 0, fill = Tkinter.X, padx=5, pady=10)
+        label.pack(side = tkinter.TOP, expand = 0, fill = tkinter.X)
+        dataFrame.pack(side = tkinter.TOP, expand = 0, fill = tkinter.X, padx=5, pady=10)
 
         dataFrame = Frame(mainFrame)
         self.inputZone = Pmw.EntryField(dataFrame, labelpos='w', label_text = 'Name Selected Path: ',
@@ -2046,14 +2053,14 @@ class namePathPanel(AppShell):
                                         label_font=('MSSansSerif', 10),
                                         validate = None,
                                         entry_width = 20)
-        self.inputZone.pack(side = Tkinter.LEFT, fill=Tkinter.X,expand=0)
+        self.inputZone.pack(side = tkinter.LEFT, fill=tkinter.X,expand=0)
 
         self.button_ok = Button(dataFrame, text="OK", command=self.ok_press,width=10)
-        self.button_ok.pack(fill=Tkinter.X,expand=0,side=Tkinter.LEFT, padx = 3)
+        self.button_ok.pack(fill=tkinter.X,expand=0,side=tkinter.LEFT, padx = 3)
 
-        dataFrame.pack(side = Tkinter.TOP, expand = 0, fill = Tkinter.X, padx=10, pady=10)
+        dataFrame.pack(side = tkinter.TOP, expand = 0, fill = tkinter.X, padx=10, pady=10)
 
-        mainFrame.pack(expand = 1, fill = Tkinter.BOTH)
+        mainFrame.pack(expand = 1, fill = tkinter.BOTH)
 
 
 

+ 4 - 4
contrib/src/sceneeditor/seParticleEffect.py

@@ -1,4 +1,4 @@
-from pandac.PandaModules import *
+from panda3d.core import *
 import seParticles
 import seForceGroup
 from direct.directnotify import DirectNotifyGlobal
@@ -209,9 +209,9 @@ class ParticleEffect(NodePath):
         """loadConfig(filename)"""
         #try:
         #    if vfs:
-        print vfs.readFile(filename)
-        exec vfs.readFile(filename)
-        print "Particle Effect Reading using VFS"
+        print(vfs.readFile(filename))
+        exec(vfs.readFile(filename))
+        print("Particle Effect Reading using VFS")
         #    else:
         #       execfile(filename.toOsSpecific())
         #       print "Shouldnt be wrong"

+ 29 - 22
contrib/src/sceneeditor/seParticlePanel.py

@@ -2,9 +2,7 @@
 
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import AppShell
-from tkFileDialog import *
-from tkSimpleDialog import askstring
-import os, Pmw, Tkinter
+import os, Pmw, sys
 from direct.tkwidgets.Dial import AngleDial
 from direct.tkwidgets.Floater import Floater
 from direct.tkwidgets.Slider import Slider
@@ -15,6 +13,15 @@ import seForceGroup
 import seParticles
 import seParticleEffect
 
+
+if sys.version_info >= (3, 0):
+    from tkinter.filedialog import *
+    from tkinter.simpledialog import askstring
+else:
+    from tkFileDialog import *
+    from tkSimpleDialog import askstring
+
+
 class ParticlePanel(AppShell):
     # Override class variables
     appname = 'Particle Panel'
@@ -774,7 +781,7 @@ class ParticlePanel(AppShell):
         kw['min'] = min
         kw['resolution'] = resolution
         kw['numDigits'] = numDigits
-        widget = apply(Floater, (parent,), kw)
+        widget = Floater(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
         widget.pack(fill = X)
@@ -786,7 +793,7 @@ class ParticlePanel(AppShell):
                         command = None, **kw):
         kw['text'] = text
         kw['style'] = 'mini'
-        widget = apply(AngleDial,(parent,), kw)
+        widget = AngleDial(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
         widget.pack(fill = X)
@@ -801,7 +808,7 @@ class ParticlePanel(AppShell):
         kw['min'] = min
         kw['max'] = max
         kw['resolution'] = resolution
-        widget = apply(Slider, (parent,), kw)
+        widget = Slider(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
         widget.pack(fill = X)
@@ -813,7 +820,7 @@ class ParticlePanel(AppShell):
                            command = None, **kw):
         # Set label's text
         kw['text'] = text
-        widget = apply(Vector2Entry, (parent,), kw)
+        widget = Vector2Entry(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
         widget.pack(fill = X)
@@ -825,7 +832,7 @@ class ParticlePanel(AppShell):
                            command = None, **kw):
         # Set label's text
         kw['text'] = text
-        widget = apply(Vector3Entry, (parent,), kw)
+        widget = Vector3Entry(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
         widget.pack(fill = X)
@@ -837,7 +844,7 @@ class ParticlePanel(AppShell):
                          command = None, **kw):
         # Set label's text
         kw['text'] = text
-        widget = apply(ColorEntry, (parent,) ,kw)
+        widget = ColorEntry(parent, **kw)
         # Do this after the widget so command isn't called on creation
         widget['command'] = command
         widget.pack(fill = X)
@@ -992,7 +999,7 @@ class ParticlePanel(AppShell):
             self.mainNotebook.selectpage('System')
             self.updateInfo('System')
         else:
-            print 'ParticlePanel: No effect named ' + name
+            print('ParticlePanel: No effect named ' + name)
 
     def toggleEffect(self, effect, var):
         if var.get():
@@ -1041,15 +1048,15 @@ class ParticlePanel(AppShell):
         # Find path to particle directory
         pPath = getParticlePath()
         if pPath.getNumDirectories() > 0:
-            if `pPath.getDirectory(0)` == '.':
+            if repr(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'
+            print('ParticlePanel Warning: Invalid default DNA directory!')
+            print('Using current directory')
             path = '.'
         particleFilename = askopenfilename(
             defaultextension = '.ptf',
@@ -1070,15 +1077,15 @@ class ParticlePanel(AppShell):
         # Find path to particle directory
         pPath = getParticlePath()
         if pPath.getNumDirectories() > 0:
-            if `pPath.getDirectory(0)` == '.':
+            if repr(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'
+            print('ParticlePanel Warning: Invalid default DNA directory!')
+            print('Using current directory')
             path = '.'
         particleFilename = asksaveasfilename(
             defaultextension = '.ptf',
@@ -1654,7 +1661,7 @@ class ParticlePanel(AppShell):
     def setRendererSpriteNonAnimatedTheta(self, theta):
         self.particles.renderer.setNonanimatedTheta(theta)
     def setRendererSpriteBlendMethod(self, blendMethod):
-        print blendMethod
+        print(blendMethod)
         if blendMethod == 'PP_NO_BLEND':
             bMethod = BaseParticleRenderer.PPNOBLEND
         elif blendMethod == 'PP_BLEND_LINEAR':
@@ -1863,7 +1870,7 @@ class ParticlePanel(AppShell):
                                       count, force):
         def setVec(vec, f = force):
             f.setVector(vec[0], vec[1], vec[2])
-        forceName = 'Vector Force-' + `count`
+        forceName = 'Vector Force-' + repr(count)
         frame = self.createForceFrame(forcePage, forceName, force)
         self.createLinearForceWidgets(frame, pageName, forceName, force)
         vec = force.getLocalVector()
@@ -1875,7 +1882,7 @@ class ParticlePanel(AppShell):
 
     def createLinearRandomForceWidget(self, forcePage, pageName, count,
                                 force, type):
-        forceName = type + ' Force-' + `count`
+        forceName = type + ' Force-' + repr(count)
         frame = self.createForceFrame(forcePage, forceName, force)
         self.createLinearForceWidgets(frame, pageName, forceName, force)
         self.createForceActiveWidget(frame, pageName, forceName, force)
@@ -1884,7 +1891,7 @@ class ParticlePanel(AppShell):
                                         count, force):
         def setCoef(coef, f = force):
             f.setCoef(coef)
-        forceName = 'Friction Force-' + `count`
+        forceName = 'Friction Force-' + repr(count)
         frame = self.createForceFrame(forcePage, forceName, force)
         self.createLinearForceWidgets(frame, pageName, forceName, force)
         self.createFloater(frame, pageName, forceName + ' Coef',
@@ -1895,7 +1902,7 @@ class ParticlePanel(AppShell):
 
     def createLinearCylinderVortexForceWidget(self, forcePage, pageName,
                                               count, force):
-        forceName = 'Vortex Force-' + `count`
+        forceName = 'Vortex Force-' + repr(count)
         def setCoef(coef, f = force):
             f.setCoef(coef)
         def setLength(length, f = force):
@@ -1934,7 +1941,7 @@ class ParticlePanel(AppShell):
             f.setForceCenter(Point3(vec[0], vec[1], vec[2]))
         def setRadius(radius, f = force):
             f.setRadius(radius)
-        forceName = type + ' Force-' + `count`
+        forceName = type + ' Force-' + repr(count)
         frame = self.createForceFrame(forcePage, forceName, force)
         self.createLinearForceWidgets(frame, pageName, forceName, force)
         var = self.createOptionMenu(

+ 5 - 25
contrib/src/sceneeditor/seParticles.py

@@ -1,28 +1,8 @@
-from pandac.PandaModules import *
+from panda3d.core import *
+from panda3d.physics import *
 from direct.particles.ParticleManagerGlobal import *
 from direct.showbase.PhysicsManagerGlobal import *
-#Manakel 2/12/2005: replace from pandac import by from pandac.PandaModules import
-from pandac.PandaModules import ParticleSystem
-from pandac.PandaModules import BaseParticleFactory
-from pandac.PandaModules import PointParticleFactory
-from pandac.PandaModules import ZSpinParticleFactory
 #import OrientedParticleFactory
-from pandac.PandaModules import BaseParticleRenderer
-from pandac.PandaModules import PointParticleRenderer
-from pandac.PandaModules import LineParticleRenderer
-from pandac.PandaModules import GeomParticleRenderer
-from pandac.PandaModules import SparkleParticleRenderer
-from pandac.PandaModules import SpriteParticleRenderer
-from pandac.PandaModules import BaseParticleEmitter
-from pandac.PandaModules import BoxEmitter
-from pandac.PandaModules import DiscEmitter
-from pandac.PandaModules import LineEmitter
-from pandac.PandaModules import PointEmitter
-from pandac.PandaModules import RectangleEmitter
-from pandac.PandaModules import RingEmitter
-from pandac.PandaModules import SphereSurfaceEmitter
-from pandac.PandaModules import SphereVolumeEmitter
-from pandac.PandaModules import TangentRingEmitter
 import string
 import os
 from direct.directnotify import DirectNotifyGlobal
@@ -113,7 +93,7 @@ class Particles(ParticleSystem):
         elif (type == "OrientedParticleFactory"):
             self.factory = OrientedParticleFactory.OrientedParticleFactory()
         else:
-            print "unknown factory type: %s" % type
+            print("unknown factory type: %s" % type)
             return None
         self.factory.setLifespanBase(0.5)
         ParticleSystem.ParticleSystem.setFactory(self, self.factory)
@@ -152,7 +132,7 @@ class Particles(ParticleSystem):
                 # See sourceFileName and sourceNodeName in SpriteParticleRenderer-extensions.py
                 self.renderer.setTextureFromNode()
         else:
-            print "unknown renderer type: %s" % type
+            print("unknown renderer type: %s" % type)
             return None
         ParticleSystem.ParticleSystem.setRenderer(self, self.renderer)
 
@@ -183,7 +163,7 @@ class Particles(ParticleSystem):
         elif (type == "TangentRingEmitter"):
             self.emitter = TangentRingEmitter.TangentRingEmitter()
         else:
-            print "unknown emitter type: %s" % type
+            print("unknown emitter type: %s" % type)
             return None
         ParticleSystem.ParticleSystem.setEmitter(self, self.emitter)
 

+ 34 - 27
contrib/src/sceneeditor/sePlacer.py

@@ -5,9 +5,16 @@ from direct.directtools.DirectGlobals import *
 from direct.tkwidgets.AppShell import AppShell
 from direct.tkwidgets.Dial import AngleDial
 from direct.tkwidgets.Floater import Floater
-from Tkinter import Button, Menubutton, Menu, StringVar
-from pandac.PandaModules import *
-import Tkinter, Pmw
+from panda3d.core import *
+import sys, Pmw
+
+if sys.version_info >= (3, 0):
+    from tkinter import Button, Menubutton, Menu, StringVar
+    import tkinter
+else:
+    from Tkinter import Button, Menubutton, Menu, StringVar
+    import Tkinter as tkinter
+
 """
 TODO:
 Task to monitor pose
@@ -84,7 +91,7 @@ class Placer(AppShell):
     def createInterface(self):
         # The interior of the toplevel panel
         interior = self.interior()
-        interior['relief'] = Tkinter.FLAT
+        interior['relief'] = tkinter.FLAT
         # Add placer commands to menubar
         self.menuBar.addmenu('Placer', 'Placer Panel Operations')
         self.menuBar.addmenuitem('Placer', 'command',
@@ -113,7 +120,7 @@ class Placer(AppShell):
         # Get a handle to the menu frame
         menuFrame = self.menuFrame
         self.nodePathMenu = Pmw.ComboBox(
-            menuFrame, labelpos = Tkinter.W, label_text = 'Node Path:',
+            menuFrame, labelpos = tkinter.W, label_text = 'Node Path:',
             entry_width = 20,
             selectioncommand = self.selectNodePathNamed,
             scrolledlist_items = self.nodePathNames)
@@ -168,7 +175,7 @@ class Placer(AppShell):
                              tag_text = 'Position',
                              tag_font=('MSSansSerif', 14),
                              tag_activebackground = '#909090',
-                             ring_relief = Tkinter.RIDGE)
+                             ring_relief = tkinter.RIDGE)
         posMenubutton = posGroup.component('tag')
         self.bind(posMenubutton, 'Position menu operations')
         posMenu = Menu(posMenubutton, tearoff = 0)
@@ -182,7 +189,7 @@ class Placer(AppShell):
         # Create the dials
         self.posX = self.createcomponent('posX', (), None,
                                          Floater, (posInterior,),
-                                         text = 'X', relief = Tkinter.FLAT,
+                                         text = 'X', relief = tkinter.FLAT,
                                          value = 0.0,
                                          label_foreground = 'Red')
         self.posX['commandData'] = ['x']
@@ -193,7 +200,7 @@ class Placer(AppShell):
 
         self.posY = self.createcomponent('posY', (), None,
                                          Floater, (posInterior,),
-                                         text = 'Y', relief = Tkinter.FLAT,
+                                         text = 'Y', relief = tkinter.FLAT,
                                          value = 0.0,
                                          label_foreground = '#00A000')
         self.posY['commandData'] = ['y']
@@ -204,7 +211,7 @@ class Placer(AppShell):
 
         self.posZ = self.createcomponent('posZ', (), None,
                                          Floater, (posInterior,),
-                                         text = 'Z', relief = Tkinter.FLAT,
+                                         text = 'Z', relief = tkinter.FLAT,
                                          value = 0.0,
                                          label_foreground = 'Blue')
         self.posZ['commandData'] = ['z']
@@ -219,7 +226,7 @@ class Placer(AppShell):
                              tag_text = 'Orientation',
                              tag_font=('MSSansSerif', 14),
                              tag_activebackground = '#909090',
-                             ring_relief = Tkinter.RIDGE)
+                             ring_relief = tkinter.RIDGE)
         hprMenubutton = hprGroup.component('tag')
         self.bind(hprMenubutton, 'Orientation menu operations')
         hprMenu = Menu(hprMenubutton, tearoff = 0)
@@ -234,7 +241,7 @@ class Placer(AppShell):
                                          AngleDial, (hprInterior,),
                                          style = 'mini',
                                          text = 'H', value = 0.0,
-                                         relief = Tkinter.FLAT,
+                                         relief = tkinter.FLAT,
                                          label_foreground = 'blue')
         self.hprH['commandData'] = ['h']
         self.hprH['preCallback'] = self.xformStart
@@ -246,7 +253,7 @@ class Placer(AppShell):
                                          AngleDial, (hprInterior,),
                                          style = 'mini',
                                          text = 'P', value = 0.0,
-                                         relief = Tkinter.FLAT,
+                                         relief = tkinter.FLAT,
                                          label_foreground = 'red')
         self.hprP['commandData'] = ['p']
         self.hprP['preCallback'] = self.xformStart
@@ -258,7 +265,7 @@ class Placer(AppShell):
                                          AngleDial, (hprInterior,),
                                          style = 'mini',
                                          text = 'R', value = 0.0,
-                                         relief = Tkinter.FLAT,
+                                         relief = tkinter.FLAT,
                                          label_foreground = '#00A000')
         self.hprR['commandData'] = ['r']
         self.hprR['preCallback'] = self.xformStart
@@ -276,7 +283,7 @@ class Placer(AppShell):
                                tag_pyclass = Menubutton,
                                tag_font=('MSSansSerif', 14),
                                tag_activebackground = '#909090',
-                               ring_relief = Tkinter.RIDGE)
+                               ring_relief = tkinter.RIDGE)
         self.scaleMenubutton = scaleGroup.component('tag')
         self.bind(self.scaleMenubutton, 'Scale menu operations')
         self.scaleMenubutton['textvariable'] = self.scalingMode
@@ -302,7 +309,7 @@ class Placer(AppShell):
         self.scaleX = self.createcomponent('scaleX', (), None,
                                            Floater, (scaleInterior,),
                                            text = 'X Scale',
-                                           relief = Tkinter.FLAT,
+                                           relief = tkinter.FLAT,
                                            min = 0.0001, value = 1.0,
                                            resetValue = 1.0,
                                            label_foreground = 'Red')
@@ -315,7 +322,7 @@ class Placer(AppShell):
         self.scaleY = self.createcomponent('scaleY', (), None,
                                            Floater, (scaleInterior,),
                                            text = 'Y Scale',
-                                           relief = Tkinter.FLAT,
+                                           relief = tkinter.FLAT,
                                            min = 0.0001, value = 1.0,
                                            resetValue = 1.0,
                                            label_foreground = '#00A000')
@@ -328,7 +335,7 @@ class Placer(AppShell):
         self.scaleZ = self.createcomponent('scaleZ', (), None,
                                            Floater, (scaleInterior,),
                                            text = 'Z Scale',
-                                           relief = Tkinter.FLAT,
+                                           relief = tkinter.FLAT,
                                            min = 0.0001, value = 1.0,
                                            resetValue = 1.0,
                                            label_foreground = 'Blue')
@@ -428,7 +435,7 @@ class Placer(AppShell):
                 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())):
+                (self.refCS.get_key() == self['nodePath'].get_key())):
                 # Yes they are, use temp CS as ref
                 # This calls updatePlacer
                 self.setReferenceNodePath(self.tempCS)
@@ -473,7 +480,7 @@ class Placer(AppShell):
                     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()):
+        if (nodePath != None) and (nodePath.get_key() == self['nodePath'].get_key()):
             # Yes they are, use temp CS and update listbox accordingly
             nodePath = self.tempCS
             self.refNodePathMenu.selectitem('parent')
@@ -508,8 +515,8 @@ class Placer(AppShell):
             dictName = name
         else:
             # Generate a unique name for the dict
-            dictName = name + '-' + `nodePath.id()`
-        if not dict.has_key(dictName):
+            dictName = name + '-' + repr(nodePath.get_key())
+        if dictName not in dict:
             # Update combo box to include new item
             names.append(dictName)
             listbox = menu.component('scrolledlist')
@@ -769,12 +776,12 @@ class Placer(AppShell):
             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))
+            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

+ 16 - 9
contrib/src/sceneeditor/seSceneGraphExplorer.py

@@ -9,9 +9,16 @@
 #
 #################################################################
 from direct.showbase.DirectObject import DirectObject
-from Tkinter import IntVar, Frame, Label
 from seTree import TreeNode, TreeItem
-import Pmw, Tkinter
+
+import Pmw, sys
+
+if sys.version_info >= (3, 0):
+    from tkinter import IntVar, Frame, Label
+    import tkinter
+else:
+    from Tkinter import IntVar, Frame, Label
+    import Tkinter as tkinter
 
 # 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
@@ -57,7 +64,7 @@ class seSceneGraphExplorer(Pmw.MegaWidget, DirectObject):
 
         # Setup up container
         interior = self.interior()
-        interior.configure(relief = Tkinter.GROOVE, borderwidth = 2)
+        interior.configure(relief = tkinter.GROOVE, borderwidth = 2)
 
         # Create a label and an entry
         self._scrolledCanvas = self.createcomponent(
@@ -69,7 +76,7 @@ class seSceneGraphExplorer(Pmw.MegaWidget, DirectObject):
         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 = Tkinter.BOTH)
+        self._scrolledCanvas.pack(padx = 3, pady = 3, expand=1, fill = tkinter.BOTH)
 
         self._canvas.bind('<ButtonPress-2>', self.mouse2Down)
         self._canvas.bind('<B2-Motion>', self.mouse2Motion)
@@ -91,8 +98,8 @@ class seSceneGraphExplorer(Pmw.MegaWidget, DirectObject):
             (), None,
             Label, (interior,),
             text = 'Active Reparent Target: ',
-            anchor = Tkinter.W, justify = Tkinter.LEFT)
-        self._label.pack(fill = Tkinter.X)
+            anchor = tkinter.W, justify = tkinter.LEFT)
+        self._label.pack(fill = tkinter.X)
 
         # Add update parent label
         def updateLabel(nodePath = None, s = self):
@@ -141,11 +148,11 @@ class seSceneGraphExplorer(Pmw.MegaWidget, DirectObject):
         self._node.deselecttree()
 
     def selectNodePath(self,nodePath, callBack=True):
-        item = self._node.find(nodePath.id())
+        item = self._node.find(nodePath.get_key())
         if item!= None:
             item.select(callBack)
         else:
-            print '----SGE: Error Selection'
+            print('----SGE: Error Selection')
 
 class SceneGraphExplorerItem(TreeItem):
 
@@ -164,7 +171,7 @@ class SceneGraphExplorerItem(TreeItem):
         return name
 
     def GetKey(self):
-        return self.nodePath.id()
+        return self.nodePath.get_key()
 
     def IsEditable(self):
         # All nodes' names can be edited nowadays.

+ 20 - 11
contrib/src/sceneeditor/seSelection.py

@@ -11,7 +11,7 @@
 # (If we do change original directools, it will force user has to install the latest version of OUR Panda)
 #
 #################################################################
-from pandac.PandaModules import GeomNode
+from panda3d.core import GeomNode
 from direct.directtools.DirectGlobals import *
 from direct.directtools.DirectUtil import *
 from seGeometry import *
@@ -70,7 +70,7 @@ class SelectedNodePaths(DirectObject):
         """ Select the specified node path.  Multiselect as required """
         # Do nothing if nothing selected
         if not nodePath:
-            print 'Nothing selected!!'
+            print('Nothing selected!!')
             return None
 
         # Reset selected objects and highlight if multiSelect is false
@@ -78,7 +78,7 @@ class SelectedNodePaths(DirectObject):
             self.deselectAll()
 
         # Get this pointer
-        id = nodePath.id()
+        id = nodePath.get_key()
         # First see if its already in the selected dictionary
         dnp = self.getSelectedDict(id)
         # If so, we're done
@@ -96,7 +96,7 @@ class SelectedNodePaths(DirectObject):
                 # Show its bounding box
                 dnp.highlight()
             # Add it to the selected dictionary
-            self.selectedDict[dnp.id()] = dnp
+            self.selectedDict[dnp.get_key()] = dnp
         # And update last
         __builtins__["last"] = self.last = dnp
         return dnp
@@ -104,7 +104,7 @@ class SelectedNodePaths(DirectObject):
     def deselect(self, nodePath):
         """ Deselect the specified node path """
         # Get this pointer
-        id = nodePath.id()
+        id = nodePath.get_key()
         # See if it is in the selected dictionary
         dnp = self.getSelectedDict(id)
         if dnp:
@@ -124,7 +124,7 @@ class SelectedNodePaths(DirectObject):
         Return a list of all selected node paths.  No verification of
         connectivity is performed on the members of the list
         """
-        return self.selectedDict.values()[:]
+        return list(self.selectedDict.values())
 
     def __getitem__(self,index):
         return self.getSelectedAsList()[index]
@@ -141,7 +141,7 @@ class SelectedNodePaths(DirectObject):
             return None
 
     def getDeselectedAsList(self):
-        return self.deselectedDict.values()[:]
+        return list(self.deselectedDict.values())
 
     def getDeselectedDict(self, id):
         """
@@ -204,15 +204,24 @@ class SelectedNodePaths(DirectObject):
         # Remove all selected nodePaths from the Scene Graph
         self.forEachSelectedNodePathDo(NodePath.remove)
 
+    def toggleVis(self, nodePath):
+        if nodePath.is_hidden():
+            nodePath.show()
+        else:
+            nodePath.hide()
+
     def toggleVisSelected(self):
         selected = self.last
         # Toggle visibility of selected node paths
         if selected:
-            selected.toggleVis()
+            if selected.is_hidden():
+                selected.show()
+            else:
+                selected.hide()
 
     def toggleVisAll(self):
         # Toggle viz for all selected node paths
-        self.forEachSelectedNodePathDo(NodePath.toggleVis)
+        self.forEachSelectedNodePathDo(self.toggleVis)
 
     def isolateSelected(self):
         selected = self.last
@@ -221,7 +230,7 @@ class SelectedNodePaths(DirectObject):
 
     def getDirectNodePath(self, nodePath):
         # Get this pointer
-        id = nodePath.id()
+        id = nodePath.get_key()
         # First check selected dict
         dnp = self.getSelectedDict(id)
         if dnp:
@@ -376,7 +385,7 @@ class DirectBoundingBox:
         return '%.2f %.2f %.2f' % (vec[0], vec[1], vec[2])
 
     def __repr__(self):
-        return (`self.__class__` +
+        return (repr(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) +

+ 9 - 6
contrib/src/sceneeditor/seSession.py

@@ -388,7 +388,7 @@ class SeSession(DirectObject):  ### Customized DirectSession
             messenger.send('DIRECT_preSelectNodePath', [dnp])
             if fResetAncestry:
                 # Update ancestry
-                self.ancestry = dnp.getAncestors()
+                self.ancestry = list(dnp.getAncestors())
                 self.ancestry.reverse()
                 self.ancestryIndex = 0
             # Update the selectedNPReadout
@@ -479,8 +479,8 @@ class SeSession(DirectObject):  ### Customized DirectSession
 
 
     def isNotCycle(self, nodePath, parent):
-        if nodePath.id() == parent.id():
-            print 'DIRECT.reparent: Invalid parent'
+        if nodePath.get_key() == parent.get_key():
+            print('DIRECT.reparent: Invalid parent')
             return 0
         elif parent.hasParent():
             return self.isNotCycle(nodePath, parent.getParent())
@@ -520,7 +520,10 @@ class SeSession(DirectObject):  ### Customized DirectSession
             nodePath = self.selected.last
         if nodePath:
             # Now toggle node path's visibility state
-            nodePath.toggleVis()
+            if nodePath.is_hidden():
+                nodePath.show()
+            else:
+                nodePath.hide()
 
     def removeNodePath(self, nodePath = 'None Given'):
         if nodePath == 'None Given':
@@ -732,8 +735,8 @@ class SeSession(DirectObject):  ### Customized DirectSession
         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
+        print(posB, hprB)
+        print(posE, hprE)
         posInterval1 = base.camera.posInterval(time, posE, bakeInStart = 1)
         posInterval2 = base.camera.posInterval(time, posB, bakeInStart = 1)
 

+ 15 - 9
contrib/src/sceneeditor/seTree.py

@@ -12,15 +12,21 @@
 #
 #################################################################
 
-import os, sys, string, Pmw, Tkinter
+import os, sys, string, Pmw
 from direct.showbase.DirectObject import DirectObject
-from Tkinter import IntVar, Menu, PhotoImage, Label, Frame, Entry
-from pandac.PandaModules import *
+from panda3d.core import *
+
+if sys.version_info >= (3, 0):
+    import tkinter
+    from tkinter import IntVar, Menu, PhotoImage, Label, Frame, Entry
+else:
+    import Tkinter as tkinter
+    from Tkinter import IntVar, Menu, PhotoImage, Label, Frame, Entry
 
 # Initialize icon directory
 ICONDIR = getModelPath().findFile(Filename('icons')).toOsSpecific()
 if not os.path.isdir(ICONDIR):
-    raise RuntimeError, "can't find DIRECT icon directory (%s)" % `ICONDIR`
+    raise RuntimeError("can't find DIRECT icon directory (%r)" % ICONDIR)
 
 class TreeNode:
 
@@ -187,9 +193,9 @@ class TreeNode:
             oldcursor = self.canvas['cursor']
             self.canvas['cursor'] = "watch"
             self.canvas.update()
-            self.canvas.delete(Tkinter.ALL)     # XXX could be more subtle
+            self.canvas.delete(tkinter.ALL)     # XXX could be more subtle
             self.draw(7, 2)
-            x0, y0, x1, y1 = self.canvas.bbox(Tkinter.ALL)
+            x0, y0, x1, y1 = self.canvas.bbox(tkinter.ALL)
             self.canvas.configure(scrollregion=(0, 0, x1, y1))
             self.canvas['cursor'] = oldcursor
 
@@ -208,7 +214,7 @@ class TreeNode:
         self.kidKeys = []
         for item in sublist:
             key = item.GetKey()
-            if self.children.has_key(key):
+            if key in self.children:
                 child = self.children[key]
             else:
                 child = TreeNode(self.canvas, self, item, self.menuList)
@@ -309,7 +315,7 @@ class TreeNode:
     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, Tkinter.END)
+        self.entry.selection_range(0, tkinter.END)
         self.entry.pack(ipadx=5)
         self.entry.focus_set()
         self.entry.bind("<Return>", self.edit_finish)
@@ -344,7 +350,7 @@ class TreeNode:
         for item in sublist:
             key = item.GetKey()
             # Use existing child or create new TreeNode if none exists
-            if self.children.has_key(key):
+            if key in self.children:
                 child = self.children[key]
             else:
                 child = TreeNode(self.canvas, self, item, self.menuList)

+ 17 - 1
direct/src/interval/MetaInterval.py

@@ -573,7 +573,23 @@ class MetaInterval(CMetaInterval):
             out = ostream
         CMetaInterval.timeline(self, out)
 
-
+    add_sequence = addSequence
+    add_parallel = addParallel
+    add_parallel_end_together = addParallelEndTogether
+    add_track = addTrack
+    add_interval = addInterval
+    set_manager = setManager
+    get_manager = getManager
+    set_t = setT
+    resume_until = resumeUntil
+    clear_to_initial = clearToInitial
+    clear_intervals = clearIntervals
+    set_play_rate = setPlayRate
+    priv_do_event = privDoEvent
+    priv_post_event = privPostEvent
+    set_interval_start_time = setIntervalStartTime
+    get_interval_start_time = getIntervalStartTime
+    get_duration = getDuration
 
 
 class Sequence(MetaInterval):

+ 2 - 2
direct/src/interval/cMetaInterval.cxx

@@ -679,7 +679,7 @@ write(std::ostream &out, int indent_level) const {
   int total_digits = num_decimals + 4;
   static const int max_digits = 32;  // totally arbitrary
   nassertv(total_digits <= max_digits);
-  char format_str[12];
+  char format_str[16];
   sprintf(format_str, "%%%d.%df", total_digits, num_decimals);
 
   indent(out, indent_level) << get_name() << ":\n";
@@ -708,7 +708,7 @@ timeline(std::ostream &out) const {
   int total_digits = num_decimals + 4;
   static const int max_digits = 32;  // totally arbitrary
   nassertv(total_digits <= max_digits);
-  char format_str[12];
+  char format_str[16];
   sprintf(format_str, "%%%d.%df", total_digits, num_decimals);
 
   int extra_indent_level = 0;

+ 20 - 12
direct/src/showbase/Transitions.py

@@ -89,7 +89,7 @@ class Transitions:
             self.fade.setBin('unsorted', 0)
             self.fade.setColor(0,0,0,0)
 
-    def getFadeInIval(self, t=0.5, finishIval=None):
+    def getFadeInIval(self, t=0.5, finishIval=None, blendType='noBlend'):
         """
         Returns an interval without starting it.  This is particularly useful in
         cutscenes, so when the cutsceneIval is escaped out of we can finish the fade immediately
@@ -103,6 +103,7 @@ class Transitions:
                                   self.lerpFunc(self.fade, t,
                                                 self.alphaOff,
                                                 # self.alphaOn,
+                                                blendType=blendType
                                                 ),
                                   Func(self.fade.detachNode),
                                   name = self.fadeTaskName,
@@ -111,7 +112,7 @@ class Transitions:
             transitionIval.append(finishIval)
         return transitionIval
 
-    def getFadeOutIval(self, t=0.5, finishIval=None):
+    def getFadeOutIval(self, t=0.5, finishIval=None, blendType='noBlend'):
         """
         Create a sequence that lerps the color out, then
         parents the fade to hidden
@@ -125,6 +126,7 @@ class Transitions:
                                   self.lerpFunc(self.fade, t,
                                                 self.alphaOn,
                                                 # self.alphaOff,
+                                                blendType=blendType
                                                 ),
                                   name = self.fadeTaskName,
                                   )
@@ -132,7 +134,7 @@ class Transitions:
             transitionIval.append(finishIval)
         return transitionIval
 
-    def fadeIn(self, t=0.5, finishIval=None):
+    def fadeIn(self, t=0.5, finishIval=None, blendType='noBlend'):
         """
         Play a fade in transition over t seconds.
         Places a polygon on the aspect2d plane then lerps the color
@@ -159,13 +161,13 @@ class Transitions:
         else:
             # Create a sequence that lerps the color out, then
             # parents the fade to hidden
-            self.transitionIval = self.getFadeInIval(t, finishIval)
+            self.transitionIval = self.getFadeInIval(t, finishIval, blendType)
             self.transitionIval.append(Func(self.__finishTransition))
             self.__transitionFuture = AsyncFuture()
             self.transitionIval.start()
             return self.__transitionFuture
 
-    def fadeOut(self, t=0.5, finishIval=None):
+    def fadeOut(self, t=0.5, finishIval=None, blendType='noBlend'):
         """
         Play a fade out transition over t seconds.
         Places a polygon on the aspect2d plane then lerps the color
@@ -189,7 +191,7 @@ class Transitions:
         else:
             # Create a sequence that lerps the color out, then
             # parents the fade to hidden
-            self.transitionIval = self.getFadeOutIval(t, finishIval)
+            self.transitionIval = self.getFadeOutIval(t, finishIval, blendType)
             self.transitionIval.append(Func(self.__finishTransition))
             self.__transitionFuture = AsyncFuture()
             self.transitionIval.start()
@@ -264,7 +266,7 @@ class Transitions:
             self.iris = loader.loadModel(self.IrisModelName)
             self.iris.setPos(0, 0, 0)
 
-    def irisIn(self, t=0.5, finishIval=None):
+    def irisIn(self, t=0.5, finishIval=None, blendType = 'noBlend'):
         """
         Play an iris in transition over t seconds.
         Places a polygon on the aspect2d plane then lerps the scale
@@ -284,7 +286,8 @@ class Transitions:
             scale = 0.18 * max(base.a2dRight, base.a2dTop)
             self.transitionIval = Sequence(LerpScaleInterval(self.iris, t,
                                                    scale = scale,
-                                                   startScale = 0.01),
+                                                   startScale = 0.01,
+                                                   blendType=blendType),
                                  Func(self.iris.detachNode),
                                  Func(self.__finishTransition),
                                  name = self.irisTaskName,
@@ -295,7 +298,7 @@ class Transitions:
             self.transitionIval.start()
             return self.__transitionFuture
 
-    def irisOut(self, t=0.5, finishIval=None):
+    def irisOut(self, t=0.5, finishIval=None, blendType='noBlend'):
         """
         Play an iris out transition over t seconds.
         Places a polygon on the aspect2d plane then lerps the scale
@@ -318,7 +321,8 @@ class Transitions:
             scale = 0.18 * max(base.a2dRight, base.a2dTop)
             self.transitionIval = Sequence(LerpScaleInterval(self.iris, t,
                                                    scale = 0.01,
-                                                   startScale = scale),
+                                                   startScale = scale,
+                                                   blendType=blendType),
                                  Func(self.iris.detachNode),
                                  # Use the fade to cover up the hole that the iris would leave
                                  Func(self.fadeOut, 0),
@@ -441,7 +445,7 @@ class Transitions:
             self.__letterboxFuture.setResult(None)
             self.__letterboxFuture = None
 
-    def letterboxOn(self, t=0.25, finishIval=None):
+    def letterboxOn(self, t=0.25, finishIval=None, blendType='noBlend'):
         """
         Move black bars in over t seconds.
         """
@@ -461,11 +465,13 @@ class Transitions:
                                 t,
                                 pos = Vec3(0, 0, -1),
                                 #startPos = Vec3(0, 0, -1.2),
+                                blendType=blendType
                                 ),
                 LerpPosInterval(self.letterboxTop,
                                 t,
                                 pos = Vec3(0, 0, 0.8),
                                 # startPos = Vec3(0, 0, 1),
+                                blendType=blendType
                                 ),
                 ),
                                           Func(self.__finishLetterbox),
@@ -476,7 +482,7 @@ class Transitions:
             self.letterboxIval.start()
             return self.__letterboxFuture
 
-    def letterboxOff(self, t=0.25, finishIval=None):
+    def letterboxOff(self, t=0.25, finishIval=None, blendType='noBlend'):
         """
         Move black bars away over t seconds.
         """
@@ -495,11 +501,13 @@ class Transitions:
                                 t,
                                 pos = Vec3(0, 0, -1.2),
                                 # startPos = Vec3(0, 0, -1),
+                                blendType=blendType
                                 ),
                 LerpPosInterval(self.letterboxTop,
                                 t,
                                 pos = Vec3(0, 0, 1),
                                 # startPos = Vec3(0, 0, 0.8),
+                                blendType=blendType
                                 ),
                 ),
                                           Func(self.letterbox.stash),

+ 2 - 2
direct/src/stdpy/threading.py

@@ -312,7 +312,7 @@ class Event:
     object. """
 
     def __init__(self):
-        self.__lock = core.Lock("Python Event")
+        self.__lock = core.Mutex("Python Event")
         self.__cvar = core.ConditionVarFull(self.__lock)
         self.__flag = False
 
@@ -325,7 +325,7 @@ class Event:
         self.__lock.acquire()
         try:
             self.__flag = True
-            self.__cvar.signalAll()
+            self.__cvar.notifyAll()
 
         finally:
             self.__lock.release()

+ 2 - 1
direct/src/tkpanels/ParticlePanel.py

@@ -263,7 +263,8 @@ class ParticlePanel(AppShell):
             'Factory', 'Factory Type',
             'Select type of particle factory',
             ('PointParticleFactory', 'ZSpinParticleFactory',
-             'OrientedParticleFactory'),
+             #'OrientedParticleFactory'
+             ),
             self.selectFactoryType)
         factoryWidgets = (
             ('Factory', 'Life Span',

File diff suppressed because it is too large
+ 224 - 222
dtool/src/cppparser/cppBison.cxx.prebuilt


+ 2 - 2
dtool/src/cppparser/cppBison.h.prebuilt

@@ -1,8 +1,8 @@
-/* A Bison parser, made by GNU Bison 3.0.4.  */
+/* A Bison parser, made by GNU Bison 3.0.5.  */
 
 /* Bison interface for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by

+ 27 - 5
dtool/src/cppparser/cppBison.yxx

@@ -1185,14 +1185,27 @@ constructor_prototype:
 /* Functions with implicit return types, and constructors */
         IDENTIFIER '('
 {
-  push_scope($1->get_scope(current_scope, global_scope));
+  // Create a scope for this function.
+  CPPScope *scope = new CPPScope($1->get_scope(current_scope, global_scope),
+                                 $1->_names.back(), V_private);
+
+  // It still needs to be able to pick up any template arguments, if this is
+  // a definition for a method template.  Add a fake "using" declaration to
+  // accomplish this.
+  scope->_using.insert(current_scope);
+
+  push_scope(scope);
 }
         function_parameter_list ')' function_post
 {
+  CPPScope *scope = $1->get_scope(current_scope, global_scope);
   CPPType *type;
-  if ($1->get_simple_name() == current_scope->get_simple_name() ||
-      $1->get_simple_name() == string("~") + current_scope->get_simple_name()) {
-    // This is a constructor, and has no return.
+  std::string simple_name = $1->get_simple_name();
+  if (!simple_name.empty() && simple_name[0] == '~') {
+    // A destructor has no return type.
+    type = new CPPSimpleType(CPPSimpleType::T_void);
+  } else if (scope != nullptr && simple_name == scope->get_simple_name()) {
+    // Neither does a constructor.
     type = new CPPSimpleType(CPPSimpleType::T_void);
   } else {
     // This isn't a constructor, so it has an implicit return type of
@@ -1209,7 +1222,16 @@ constructor_prototype:
 }
         | TYPENAME_IDENTIFIER '('
 {
-  push_scope($1->get_scope(current_scope, global_scope));
+  // Create a scope for this function.
+  CPPScope *scope = new CPPScope($1->get_scope(current_scope, global_scope),
+                                 $1->_names.back(), V_private);
+
+  // It still needs to be able to pick up any template arguments, if this is
+  // a definition for a method template.  Add a fake "using" declaration to
+  // accomplish this.
+  scope->_using.insert(current_scope);
+
+  push_scope(scope);
 }
         function_parameter_list ')' function_post
 {

+ 4 - 0
dtool/src/dtoolbase/dtoolbase_cc.h

@@ -97,10 +97,12 @@ typedef std::ios::seekdir ios_seekdir;
 // in some important missing functions.
 #if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719
 #include <tr1/tuple>
+#include <tr1/cmath>
 
 namespace std {
   using std::tr1::tuple;
   using std::tr1::tie;
+  using std::tr1::copysign;
 
   typedef decltype(nullptr) nullptr_t;
 
@@ -111,6 +113,8 @@ namespace std {
   template<class T> typename remove_reference<T>::type &&move(T &&t) {
     return static_cast<typename remove_reference<T>::type&&>(t);
   }
+
+  template<class T> struct owner_less;
 };
 #endif
 

+ 7 - 0
dtool/src/parser-inc/time.h

@@ -1 +1,8 @@
+#pragma once
+
 #include <stdtypedefs.h>
+
+struct timespec {
+  time_t tv_sec;
+  long tv_nsec;
+};

+ 1 - 1
dtool/src/parser-inc/ws2tcpip.h

@@ -1 +1 @@
-typedef DWORD socklen_t;
+typedef int socklen_t;

+ 18 - 19
dtool/src/pystub/pystub.cxx

@@ -36,7 +36,6 @@ extern "C" {
   EXPCL_PYSTUB int PyDict_SetItem(...);
   EXPCL_PYSTUB int PyDict_SetItemString(...);
   EXPCL_PYSTUB int PyDict_Size(...);
-  EXPCL_PYSTUB int PyDict_Type(...);
   EXPCL_PYSTUB int PyErr_Clear(...);
   EXPCL_PYSTUB int PyErr_ExceptionMatches(...);
   EXPCL_PYSTUB int PyErr_Fetch(...);
@@ -54,9 +53,7 @@ extern "C" {
   EXPCL_PYSTUB int PyEval_SaveThread(...);
   EXPCL_PYSTUB int PyFloat_AsDouble(...);
   EXPCL_PYSTUB int PyFloat_FromDouble(...);
-  EXPCL_PYSTUB int PyFloat_Type(...);
   EXPCL_PYSTUB int PyGen_Check(...);
-  EXPCL_PYSTUB int PyGen_Type(...);
   EXPCL_PYSTUB int PyGILState_Ensure(...);
   EXPCL_PYSTUB int PyGILState_Release(...);
   EXPCL_PYSTUB int PyImport_GetModuleDict(...);
@@ -65,14 +62,12 @@ extern "C" {
   EXPCL_PYSTUB int PyInt_AsSsize_t(...);
   EXPCL_PYSTUB int PyInt_FromLong(...);
   EXPCL_PYSTUB int PyInt_FromSize_t(...);
-  EXPCL_PYSTUB int PyInt_Type(...);
   EXPCL_PYSTUB int PyIter_Next(...);
   EXPCL_PYSTUB int PyList_Append(...);
   EXPCL_PYSTUB int PyList_AsTuple(...);
   EXPCL_PYSTUB int PyList_GetItem(...);
   EXPCL_PYSTUB int PyList_New(...);
   EXPCL_PYSTUB int PyList_SetItem(...);
-  EXPCL_PYSTUB int PyList_Type(...);
   EXPCL_PYSTUB int PyLong_AsLong(...);
   EXPCL_PYSTUB int PyLong_AsLongLong(...);
   EXPCL_PYSTUB int PyLong_AsSsize_t(...);
@@ -83,7 +78,6 @@ extern "C" {
   EXPCL_PYSTUB int PyLong_FromSize_t(...);
   EXPCL_PYSTUB int PyLong_FromUnsignedLong(...);
   EXPCL_PYSTUB int PyLong_FromUnsignedLongLong(...);
-  EXPCL_PYSTUB int PyLong_Type(...);
   EXPCL_PYSTUB int PyMapping_GetItemString(...);
   EXPCL_PYSTUB int PyMem_Free(...);
   EXPCL_PYSTUB int PyMemoryView_FromObject(...);
@@ -124,7 +118,6 @@ extern "C" {
   EXPCL_PYSTUB int PyObject_SetAttr(...);
   EXPCL_PYSTUB int PyObject_SetAttrString(...);
   EXPCL_PYSTUB int PyObject_Str(...);
-  EXPCL_PYSTUB int PyObject_Type(...);
   EXPCL_PYSTUB int PySeqIter_New(...);
   EXPCL_PYSTUB int PySequence_Check(...);
   EXPCL_PYSTUB int PySequence_Fast(...);
@@ -139,7 +132,6 @@ extern "C" {
   EXPCL_PYSTUB int PyString_InternFromString(...);
   EXPCL_PYSTUB int PyString_InternInPlace(...);
   EXPCL_PYSTUB int PyString_Size(...);
-  EXPCL_PYSTUB int PyString_Type(...);
   EXPCL_PYSTUB int PySys_GetObject(...);
   EXPCL_PYSTUB int PyThreadState_Clear(...);
   EXPCL_PYSTUB int PyThreadState_Delete(...);
@@ -181,7 +173,6 @@ extern "C" {
   EXPCL_PYSTUB int PyUnicode_GetSize(...);
   EXPCL_PYSTUB int PyUnicode_InternFromString(...);
   EXPCL_PYSTUB int PyUnicode_InternInPlace(...);
-  EXPCL_PYSTUB int PyUnicode_Type(...);
   EXPCL_PYSTUB int Py_BuildValue(...);
   EXPCL_PYSTUB int Py_GetVersion(...);
   EXPCL_PYSTUB int Py_InitModule4(...);
@@ -233,8 +224,17 @@ extern "C" {
   EXPCL_PYSTUB extern void *PyExc_SystemExit;
   EXPCL_PYSTUB extern void *PyExc_TypeError;
   EXPCL_PYSTUB extern void *PyExc_ValueError;
+  EXPCL_PYSTUB extern void *PyDict_Type;
+  EXPCL_PYSTUB extern void *PyFloat_Type;
+  EXPCL_PYSTUB extern void *PyGen_Type;
+  EXPCL_PYSTUB extern void *PyInt_Type;
+  EXPCL_PYSTUB extern void *PyList_Type;
+  EXPCL_PYSTUB extern void *PyLong_Type;
+  EXPCL_PYSTUB extern void *PyObject_Type;
+  EXPCL_PYSTUB extern void *PyString_Type;
   EXPCL_PYSTUB extern void *PyTuple_Type;
   EXPCL_PYSTUB extern void *PyType_Type;
+  EXPCL_PYSTUB extern void *PyUnicode_Type;
   EXPCL_PYSTUB extern void *_PyThreadState_Current;
   EXPCL_PYSTUB extern void *_Py_FalseStruct;
   EXPCL_PYSTUB extern void *_Py_NoneStruct;
@@ -266,7 +266,6 @@ int PyDict_Next(...) { return 0; };
 int PyDict_SetItem(...) { return 0; };
 int PyDict_SetItemString(...) { return 0; };
 int PyDict_Size(...){ return 0; }
-int PyDict_Type(...) { return 0; };
 int PyErr_Clear(...) { return 0; };
 int PyErr_ExceptionMatches(...) { return 0; };
 int PyErr_Fetch(...) { return 0; }
@@ -285,9 +284,7 @@ int PyEval_RestoreThread(...) { return 0; }
 int PyEval_SaveThread(...) { return 0; }
 int PyFloat_AsDouble(...) { return 0; }
 int PyFloat_FromDouble(...) { return 0; }
-int PyFloat_Type(...) { return 0; }
 int PyGen_Check(...) { return 0; }
-int PyGen_Type(...) { return 0; }
 int PyGILState_Ensure(...) { return 0; }
 int PyGILState_Release(...) { return 0; }
 int PyImport_GetModuleDict(...) { return 0; }
@@ -296,14 +293,12 @@ int PyInt_AsLong(...) { return 0; }
 int PyInt_AsSsize_t(...) { return 0; }
 int PyInt_FromLong(...) { return 0; }
 int PyInt_FromSize_t(...) { return 0; }
-int PyInt_Type(...) { return 0; }
 int PyIter_Next(...) { return 0; }
 int PyList_Append(...) { return 0; }
 int PyList_AsTuple(...) { return 0; }
 int PyList_GetItem(...) { return 0; }
 int PyList_New(...) { return 0; }
 int PyList_SetItem(...) { return 0; }
-int PyList_Type(...) { return 0; }
 int PyLong_AsLong(...) { return 0; }
 int PyLong_AsLongLong(...) { return 0; }
 int PyLong_AsSsize_t(...) { return 0; }
@@ -314,7 +309,6 @@ int PyLong_FromLongLong(...) { return 0; }
 int PyLong_FromSize_t(...) { return 0; }
 int PyLong_FromUnsignedLong(...) { return 0; }
 int PyLong_FromUnsignedLongLong(...) { return 0; }
-int PyLong_Type(...) { return 0; }
 int PyMapping_GetItemString(...) { return 0; }
 int PyMem_Free(...) { return 0; }
 int PyMemoryView_FromObject(...) { return 0; }
@@ -355,7 +349,6 @@ int PyObject_SelfIter(...) { return 0; }
 int PyObject_SetAttr(...) { return 0; }
 int PyObject_SetAttrString(...) { return 0; }
 int PyObject_Str(...) { return 0; }
-int PyObject_Type(...) { return 0; }
 int PySeqIter_New(...) { return 0; }
 int PySequence_Check(...) { return 0; }
 int PySequence_Fast(...) { return 0; }
@@ -369,8 +362,6 @@ int PyString_FromString(...) { return 0; }
 int PyString_FromStringAndSize(...) { return 0; }
 int PyString_InternFromString(...) { return 0; }
 int PyString_InternInPlace(...) { return 0; }
-int PyString_Size(...) { return 0; }
-int PyString_Type(...) { return 0; }
 int PySys_GetObject(...) { return 0; }
 int PyThreadState_Clear(...) { return 0; }
 int PyThreadState_Delete(...) { return 0; }
@@ -412,7 +403,6 @@ int PyUnicode_FromWideChar(...) { return 0; }
 int PyUnicode_GetSize(...) { return 0; }
 int PyUnicode_InternFromString(...) { return 0; }
 int PyUnicode_InternInPlace(...) { return 0; }
-int PyUnicode_Type(...) { return 0; }
 int Py_GetVersion(...) { return 0; }
 int Py_BuildValue(...) { return 0; }
 int Py_InitModule4(...) { return 0; }
@@ -470,8 +460,17 @@ void *PyExc_StopIteration = nullptr;
 void *PyExc_SystemExit = nullptr;
 void *PyExc_TypeError = nullptr;
 void *PyExc_ValueError = nullptr;
+void *PyDict_Type = nullptr;
+void *PyFloat_Type = nullptr;
+void *PyGen_Type = nullptr;
+void *PyInt_Type = nullptr;
+void *PyList_Type = nullptr;
+void *PyLong_Type = nullptr;
+void *PyObject_Type = nullptr;
+void *PyString_Type = nullptr;
 void *PyTuple_Type = nullptr;
 void *PyType_Type = nullptr;
+void *PyUnicode_Type = nullptr;
 void *_PyThreadState_Current = nullptr;
 void *_Py_FalseStruct = nullptr;
 void *_Py_NoneStruct = nullptr;

+ 3 - 0
makepanda/makepanda.py

@@ -1415,6 +1415,9 @@ def CompileCxx(obj,src,opts):
             # Fast math is nice, but we'd like to see NaN in dev builds.
             cmd += " -fno-finite-math-only"
 
+        # Make sure this is off to avoid GCC/Eigen bug (see GitHub #228)
+        cmd += " -fno-unsafe-math-optimizations"
+
         if (optlevel==1): cmd += " -ggdb -D_DEBUG"
         if (optlevel==2): cmd += " -O1 -D_DEBUG"
         if (optlevel==3): cmd += " -O2"

+ 0 - 4
makepanda/makepanda.vcproj

@@ -760,7 +760,6 @@
 				<File RelativePath="..\panda\src\gobj\bufferContext.cxx"></File>
 				<File RelativePath="..\panda\src\gobj\textureContext.I"></File>
 				<File RelativePath="..\panda\src\gobj\internalName.h"></File>
-				<File RelativePath="..\panda\src\gobj\test_gobj.cxx"></File>
 				<File RelativePath="..\panda\src\gobj\geomTristrips.h"></File>
 				<File RelativePath="..\panda\src\gobj\textureContext.h"></File>
 				<File RelativePath="..\panda\src\gobj\config_gobj.cxx"></File>
@@ -1114,7 +1113,6 @@
 				<File RelativePath="..\panda\src\collide\collisionHandlerGravity.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionLine.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionHandlerPhysical.I"></File>
-				<File RelativePath="..\panda\src\collide\test_collide.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionFloorMesh.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionPolygon.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionGeom.cxx"></File>
@@ -1379,7 +1377,6 @@
 				<File RelativePath="..\panda\src\display\windowHandle.h"></File>
 				<File RelativePath="..\panda\src\display\displayRegionCullCallbackData.cxx"></File>
 				<File RelativePath="..\panda\src\display\graphicsOutput.cxx"></File>
-				<File RelativePath="..\panda\src\display\test_display.cxx"></File>
 				<File RelativePath="..\panda\src\display\graphicsBuffer.I"></File>
 				<File RelativePath="..\panda\src\display\stencilRenderStates.cxx"></File>
 				<File RelativePath="..\panda\src\display\stereoDisplayRegion.I"></File>
@@ -3706,7 +3703,6 @@
 				<File RelativePath="..\panda\src\testbed\test_map.cxx"></File>
 				<File RelativePath="..\panda\src\testbed\pgrid.cxx"></File>
 				<File RelativePath="..\panda\src\testbed\pview.cxx"></File>
-				<File RelativePath="..\panda\src\testbed\text_test.cxx"></File>
 			</Filter>
 			<Filter Name="cull">
 				<File RelativePath="..\panda\src\cull\config_cull.cxx"></File>

+ 2 - 1
makepanda/makepandacore.py

@@ -102,7 +102,8 @@ MAYAVERSIONINFO = [("MAYA6",   "6.0"),
                    ("MAYA2015","2015"),
                    ("MAYA2016","2016"),
                    ("MAYA20165","2016.5"),
-                   ("MAYA2017","2017")
+                   ("MAYA2017","2017"),
+                   ("MAYA2018","2018"),
 ]
 
 MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"),

+ 4 - 1
makepanda/makewheel.py

@@ -514,7 +514,10 @@ def makewheel(version, output_dir, platform=None):
 
     # Write the panda3d tree.  We use a custom empty __init__ since the
     # default one adds the bin directory to the PATH, which we don't have.
-    whl.write_file_data('panda3d/__init__.py', '')
+    whl.write_file_data('panda3d/__init__.py', """"Python bindings for the Panda3D libraries"
+
+__version__ = '{0}'
+""".format(version))
 
     ext_suffix = '.pyd' if sys.platform in ('win32', 'cygwin') else '.so'
 

+ 1 - 1
panda/src/chan/partBundle.h

@@ -172,7 +172,7 @@ private:
   typedef pvector<PartBundleNode *> Nodes;
   Nodes _nodes;
 
-  typedef pmap<WCPT(TransformState), WPT(PartBundle) > AppliedTransforms;
+  typedef pmap<WCPT(TransformState), WPT(PartBundle), std::owner_less<WCPT(TransformState)> > AppliedTransforms;
   AppliedTransforms _applied_transforms;
 
   double _update_delay;

+ 5 - 1
panda/src/char/characterJointEffect.I

@@ -35,5 +35,9 @@ get_character() const {
  */
 INLINE bool CharacterJointEffect::
 matches_character(Character *character) const {
-  return _character == character;
+  // This works because while the Character is destructing, the ref count will
+  // be 0 but was_deleted() will still return false.  We cannot construct a
+  // PointerTo to the character (via lock() or otherwise) when the reference
+  // count is 0 since that will cause double deletion.
+  return _character.get_orig() == character && !_character.was_deleted();
 }

+ 274 - 100
panda/src/collide/collisionBox.cxx

@@ -398,6 +398,49 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   return new_entry;
 }
 
+/**
+ *
+ */
+PT(CollisionEntry) CollisionBox::
+test_intersection_from_line(const CollisionEntry &entry) const {
+  const CollisionLine *line;
+  DCAST_INTO_R(line, entry.get_from(), nullptr);
+
+  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
+
+  LPoint3 from_origin = line->get_origin() * wrt_mat;
+  LVector3 from_direction = line->get_direction() * wrt_mat;
+
+  double t1, t2;
+  if (!intersects_line(t1, t2, from_origin, from_direction)) {
+    // No intersection.
+    return nullptr;
+  }
+
+  if (collide_cat.is_debug()) {
+    collide_cat.debug()
+      << "intersection detected from " << entry.get_from_node_path()
+      << " into " << entry.get_into_node_path() << "\n";
+  }
+  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
+
+  LPoint3 point = from_origin + t1 * from_direction;
+  new_entry->set_surface_point(point);
+
+  if (has_effective_normal() && line->get_respect_effective_normal()) {
+    new_entry->set_surface_normal(get_effective_normal());
+  } else {
+    LVector3 normal(
+      IS_NEARLY_EQUAL(point[0], _max[0]) - IS_NEARLY_EQUAL(point[0], _min[0]),
+      IS_NEARLY_EQUAL(point[1], _max[1]) - IS_NEARLY_EQUAL(point[1], _min[1]),
+      IS_NEARLY_EQUAL(point[2], _max[2]) - IS_NEARLY_EQUAL(point[2], _min[2])
+    );
+    normal.normalize();
+    new_entry->set_surface_normal(normal);
+  }
+
+  return new_entry;
+}
 
 /**
  * Double dispatch point for ray as a FROM object
@@ -411,51 +454,9 @@ test_intersection_from_ray(const CollisionEntry &entry) const {
   LPoint3 from_origin = ray->get_origin() * wrt_mat;
   LVector3 from_direction = ray->get_direction() * wrt_mat;
 
-  int i, j;
-  PN_stdfloat t;
-  PN_stdfloat near_t = 0.0;
-  bool intersect;
-  LPlane plane;
-  LPlane near_plane;
-
-  // Returns the details about the first plane of the box that the ray
-  // intersects.
-  for (i = 0, intersect = false, t = 0, j = 0; i < 6 && j < 2; i++) {
-    plane = get_plane(i);
-
-    if (!plane.intersects_line(t, from_origin, from_direction)) {
-      // No intersection.  The ray is parallel to the plane.
-      continue;
-    }
-
-    if (t < 0.0f) {
-      // The intersection point is before the start of the ray, and so the ray
-      // is entirely in front of the plane.
-      continue;
-    }
-    LPoint3 plane_point = from_origin + t * from_direction;
-    LPoint2 p = to_2d(plane_point, i);
-
-    if (!point_is_inside(p, _points[i])){
-      continue;
-    }
-    intersect = true;
-    if (j) {
-      if(t < near_t) {
-        near_plane = plane;
-        near_t = t;
-      }
-    }
-    else {
-      near_plane = plane;
-      near_t = t;
-    }
-    ++j;
-  }
-
-
-  if(!intersect) {
-    // No intersection with ANY of the box's planes has been detected
+  double t1, t2;
+  if (!intersects_line(t1, t2, from_origin, from_direction) || (t1 < 0.0 && t2 < 0.0)) {
+    // No intersection.
     return nullptr;
   }
 
@@ -464,22 +465,32 @@ test_intersection_from_ray(const CollisionEntry &entry) const {
       << "intersection detected from " << entry.get_from_node_path()
       << " into " << entry.get_into_node_path() << "\n";
   }
-
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
 
-  LPoint3 into_intersection_point = from_origin + near_t * from_direction;
+  if (t1 < 0.0) {
+    // The origin is inside the box, so we take the exit as our surface point.
+    new_entry->set_interior_point(from_origin);
+    t1 = t2;
+  }
 
-  LVector3 normal =
-    (has_effective_normal() && ray->get_respect_effective_normal())
-    ? get_effective_normal() : near_plane.get_normal();
+  LPoint3 point = from_origin + t1 * from_direction;
+  new_entry->set_surface_point(point);
 
-  new_entry->set_surface_normal(normal);
-  new_entry->set_surface_point(into_intersection_point);
+  if (has_effective_normal() && ray->get_respect_effective_normal()) {
+    new_entry->set_surface_normal(get_effective_normal());
+  } else {
+    LVector3 normal(
+      IS_NEARLY_EQUAL(point[0], _max[0]) - IS_NEARLY_EQUAL(point[0], _min[0]),
+      IS_NEARLY_EQUAL(point[1], _max[1]) - IS_NEARLY_EQUAL(point[1], _min[1]),
+      IS_NEARLY_EQUAL(point[2], _max[2]) - IS_NEARLY_EQUAL(point[2], _min[2])
+    );
+    normal.normalize();
+    new_entry->set_surface_normal(normal);
+  }
 
   return new_entry;
 }
 
-
 /**
  * Double dispatch point for segment as a FROM object
  */
@@ -493,70 +504,188 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
   LPoint3 from_extent = seg->get_point_b() * wrt_mat;
   LVector3 from_direction = from_extent - from_origin;
 
-  int i, j;
-  PN_stdfloat t;
-  PN_stdfloat near_t = 0.0;
-  bool intersect;
-  LPlane plane;
-  LPlane near_plane;
+  double t1, t2;
+  if (!intersects_line(t1, t2, from_origin, from_direction) ||
+      (t1 < 0.0 && t2 < 0.0) || (t1 > 1.0 && t2 > 1.0)) {
+    // No intersection.
+    return nullptr;
+  }
 
-  // Returns the details about the first plane of the box that the segment
-  // intersects.
-  for(i = 0, intersect = false, t = 0, j = 0; i < 6 && j < 2; i++) {
-    plane = get_plane(i);
+  if (collide_cat.is_debug()) {
+    collide_cat.debug()
+      << "intersection detected from " << entry.get_from_node_path()
+      << " into " << entry.get_into_node_path() << "\n";
+  }
+  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
 
-    if (!plane.intersects_line(t, from_origin, from_direction)) {
-      // No intersection.  The segment is parallel to the plane.
-      continue;
-    }
+  // In case the segment is entirely inside the cube, we consider the point
+  // closest to the surface as our entry point.
+  if (t1 < (1.0 - t2)) {
+    std::swap(t1, t2);
+  }
 
-    if (t < 0.0f || t > 1.0f) {
-      // The intersection point is before the start of the segment, or after
-      // the end of the segment, so the segment is either entirely in front of
-      // or behind the plane.
-      continue;
-    }
-    LPoint3 plane_point = from_origin + t * from_direction;
-    LPoint2 p = to_2d(plane_point, i);
+  // Our interior point is the closest point to t2 that is inside the segment.
+  new_entry->set_interior_point(from_origin + std::min(std::max(t2, 0.0), 1.0) * from_direction);
 
-    if (!point_is_inside(p, _points[i])){
-      continue;
-    }
-    intersect = true;
-    if(j) {
-      if(t < near_t) {
-        near_plane = plane;
-        near_t = t;
-      }
-    }
-    else {
-      near_plane = plane;
-      near_t = t;
-    }
-    ++j;
+  LPoint3 point = from_origin + t1 * from_direction;
+  new_entry->set_surface_point(point);
+
+  if (has_effective_normal() && seg->get_respect_effective_normal()) {
+    new_entry->set_surface_normal(get_effective_normal());
+  } else {
+    LVector3 normal(
+      IS_NEARLY_EQUAL(point[0], _max[0]) - IS_NEARLY_EQUAL(point[0], _min[0]),
+      IS_NEARLY_EQUAL(point[1], _max[1]) - IS_NEARLY_EQUAL(point[1], _min[1]),
+      IS_NEARLY_EQUAL(point[2], _max[2]) - IS_NEARLY_EQUAL(point[2], _min[2])
+    );
+    normal.normalize();
+    new_entry->set_surface_normal(normal);
   }
 
-  if(!intersect) {
-    // No intersection with ANY of the box's planes has been detected
+  return new_entry;
+}
+
+/**
+ * Double dispatch point for tube as a FROM object
+ */
+PT(CollisionEntry) CollisionBox::
+test_intersection_from_tube(const CollisionEntry &entry) const {
+  const CollisionTube *tube;
+  DCAST_INTO_R(tube, entry.get_from(), nullptr);
+
+  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
+
+  LPoint3 from_a = tube->get_point_a() * wrt_mat;
+  LPoint3 from_b = tube->get_point_b() * wrt_mat;
+  LVector3 from_direction = from_b - from_a;
+  PN_stdfloat radius_sq = wrt_mat.xform_vec(LVector3(0, 0, tube->get_radius())).length_squared();
+  PN_stdfloat radius = csqrt(radius_sq);
+
+  LPoint3 box_min = get_min();
+  LPoint3 box_max = get_max();
+  LVector3 dimensions = box_max - box_min;
+
+  // The method below is inspired by Christer Ericson's book Real-Time
+  // Collision Detection.  Instead of testing a capsule against a box, we test
+  // a segment against an box that is oversized by the capsule radius.
+
+  // First, we test if the line segment intersects a box with its faces
+  // expanded outwards by the capsule radius.  If not, there is no collision.
+  double t1, t2;
+  if (!intersects_line(t1, t2, from_a, from_direction, radius)) {
+    return nullptr;
+  }
+
+  if (t2 < 0.0 || t1 > 1.0) {
     return nullptr;
   }
 
+  t1 = std::min(1.0, std::max(0.0, (t1 + t2) * 0.5));
+  LPoint3 point = from_a + from_direction * t1;
+
+  // We now have a point of intersection between the line segment and the
+  // oversized box.  Check on how many axes it lies outside the box.  If it is
+  // less than two, we know that it does not lie in one of the rounded regions
+  // of the oversized rounded box, and it is a guaranteed hit.  Otherwise, we
+  // will need to test against the edge regions.
+  if ((point[0] < box_min[0] || point[0] > box_max[0]) +
+      (point[1] < box_min[1] || point[1] > box_max[1]) +
+      (point[2] < box_min[2] || point[2] > box_max[2]) > 1) {
+    // Test the capsule against each edge of the box.
+    static const struct {
+      LPoint3 point;
+      int axis;
+    } edges[] = {
+      {{0, 0, 0}, 0},
+      {{0, 1, 0}, 0},
+      {{0, 0, 1}, 0},
+      {{0, 1, 1}, 0},
+      {{0, 0, 0}, 1},
+      {{0, 0, 1}, 1},
+      {{1, 0, 0}, 1},
+      {{1, 0, 1}, 1},
+      {{0, 0, 0}, 2},
+      {{0, 1, 0}, 2},
+      {{1, 0, 0}, 2},
+      {{1, 1, 0}, 2},
+    };
+
+    PN_stdfloat best_dist_sq = FLT_MAX;
+
+    for (int i = 0; i < 12; ++i) {
+      LPoint3 vertex = edges[i].point;
+      vertex.componentwise_mult(dimensions);
+      vertex += box_min;
+      LVector3 delta(0);
+      delta[edges[i].axis] = dimensions[edges[i].axis];
+      double u1, u2;
+      CollisionTube::calc_closest_segment_points(u1, u2, from_a, from_direction, vertex, delta);
+      PN_stdfloat dist_sq = ((from_a + from_direction * u1) - (vertex + delta * u2)).length_squared();
+      if (dist_sq < best_dist_sq) {
+        best_dist_sq = dist_sq;
+      }
+    }
+
+    if (best_dist_sq > radius_sq) {
+      // It is not actually touching any edge.
+      return nullptr;
+    }
+  }
+
   if (collide_cat.is_debug()) {
     collide_cat.debug()
       << "intersection detected from " << entry.get_from_node_path()
       << " into " << entry.get_into_node_path() << "\n";
   }
-
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
 
-  LPoint3 into_intersection_point = from_origin + near_t * from_direction;
-
-  LVector3 normal =
-    (has_effective_normal() && seg->get_respect_effective_normal())
-    ? get_effective_normal() : near_plane.get_normal();
+  // Which is the longest axis?
+  LVector3 diff = point - _center;
+  diff[0] /= dimensions[0];
+  diff[1] /= dimensions[1];
+  diff[2] /= dimensions[2];
+  int axis = 0;
+  if (cabs(diff[0]) > cabs(diff[1])) {
+    if (cabs(diff[0]) > cabs(diff[2])) {
+      axis = 0;
+    } else {
+      axis = 2;
+    }
+  } else {
+    if (cabs(diff[1]) > cabs(diff[2])) {
+      axis = 1;
+    } else {
+      axis = 2;
+    }
+  }
+  LVector3 normal(0);
+  normal[axis] = std::copysign(1, diff[axis]);
+
+  LPoint3 clamped = point.fmax(box_min).fmin(box_max);
+  LPoint3 surface_point = clamped;
+  surface_point[axis] = (diff[axis] >= 0.0f) ? box_max[axis] : box_min[axis];
+
+  // Is the point inside the box?
+  LVector3 interior_vec;
+  if (clamped != point) {
+    // No, it is outside.  The interior point is in the direction of the
+    // surface point.
+    interior_vec = point - surface_point;
+    if (!interior_vec.normalize()) {
+      interior_vec = normal;
+    }
+  } else {
+    // It is inside.  I think any point will work for this.
+    interior_vec = normal;
+  }
+  new_entry->set_interior_point(point - interior_vec * radius);
+  new_entry->set_surface_point(surface_point);
 
-  new_entry->set_surface_normal(normal);
-  new_entry->set_surface_point(into_intersection_point);
+  if (has_effective_normal() && tube->get_respect_effective_normal()) {
+    new_entry->set_surface_normal(get_effective_normal());
+  } else {
+    new_entry->set_surface_normal(normal);
+  }
 
   return new_entry;
 }
@@ -823,6 +952,51 @@ fill_viz_geom() {
   _bounds_viz_geom->add_geom(geom, get_solid_bounds_viz_state());
 }
 
+/**
+ * Determine the point(s) of intersection of a parametric line with the box.
+ * The line is infinite in both directions, and passes through "from" and
+ * from+delta.  If the line does not intersect the box, the function returns
+ * false, and t1 and t2 are undefined.  If it does intersect the box, it
+ * returns true, and t1 and t2 are set to the points along the equation
+ * from+t*delta that correspond to the two points of intersection.
+ */
+bool CollisionBox::
+intersects_line(double &t1, double &t2,
+                const LPoint3 &from, const LVector3 &delta,
+                PN_stdfloat inflate_size) const {
+
+  LPoint3 bmin = _min - LVector3(inflate_size);
+  LPoint3 bmax = _max + LVector3(inflate_size);
+
+  double tmin = -DBL_MAX;
+  double tmax = DBL_MAX;
+
+  for (int i = 0; i < 3; ++i) {
+    PN_stdfloat d = delta[i];
+    if (!IS_NEARLY_ZERO(d)) {
+      double tmin2 = (bmin[i] - from[i]) / d;
+      double tmax2 = (bmax[i] - from[i]) / d;
+      if (tmin2 > tmax2) {
+        std::swap(tmin2, tmax2);
+      }
+      tmin = std::max(tmin, tmin2);
+      tmax = std::min(tmax, tmax2);
+
+      if (tmin > tmax) {
+        return false;
+      }
+
+    } else if (from[i] < bmin[i] || from[i] > bmax[i]) {
+      // The line is entirely parallel in this dimension.
+      return false;
+    }
+  }
+
+  t1 = tmin;
+  t2 = tmax;
+  return true;
+}
+
 /**
  * Clips the polygon by all of the clip planes named in the clip plane
  * attribute and fills new_points up with the resulting points.

+ 9 - 0
panda/src/collide/collisionBox.h

@@ -75,15 +75,24 @@ protected:
   virtual PT(BoundingVolume) compute_internal_bounds() const;
   virtual PT(CollisionEntry)
     test_intersection_from_sphere(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+    test_intersection_from_line(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
     test_intersection_from_ray(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
     test_intersection_from_segment(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+    test_intersection_from_tube(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
     test_intersection_from_box(const CollisionEntry &entry) const;
 
   virtual void fill_viz_geom();
 
+protected:
+  bool intersects_line(double &t1, double &t2,
+                       const LPoint3 &from, const LVector3 &delta,
+                       PN_stdfloat inflate_size=0) const;
+
 private:
   LPoint3 _center;
   LPoint3 _min;

+ 144 - 8
panda/src/collide/collisionTube.cxx

@@ -391,6 +391,70 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
   return new_entry;
 }
 
+/**
+ *
+ */
+PT(CollisionEntry) CollisionTube::
+test_intersection_from_tube(const CollisionEntry &entry) const {
+  const CollisionTube *tube;
+  DCAST_INTO_R(tube, entry.get_from(), nullptr);
+
+  LPoint3 into_a = _a;
+  LVector3 into_direction = _b - into_a;
+
+  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
+
+  LPoint3 from_a = tube->get_point_a() * wrt_mat;
+  LPoint3 from_b = tube->get_point_b() * wrt_mat;
+  LVector3 from_direction = from_b - from_a;
+
+  LVector3 from_radius_v =
+    LVector3(tube->get_radius(), 0.0f, 0.0f) * wrt_mat;
+  PN_stdfloat from_radius = length(from_radius_v);
+
+  // Determine the points on each segment with the smallest distance between.
+  double into_t, from_t;
+  calc_closest_segment_points(into_t, from_t,
+                              into_a, into_direction,
+                              from_a, from_direction);
+  LPoint3 into_closest = into_a + into_direction * into_t;
+  LPoint3 from_closest = from_a + from_direction * from_t;
+
+  // If the distance is greater than the sum of tube radii, the test fails.
+  LVector3 closest_vec = from_closest - into_closest;
+  PN_stdfloat distance = closest_vec.length();
+  if (distance > _radius + from_radius) {
+    return nullptr;
+  }
+
+  if (collide_cat.is_debug()) {
+    collide_cat.debug()
+      << "intersection detected from " << entry.get_from_node_path()
+      << " into " << entry.get_into_node_path() << "\n";
+  }
+  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
+
+  if (distance != 0) {
+    // This is the most common case, where the line segments don't touch
+    // exactly.  We point the normal along the vector of the closest distance.
+    LVector3 surface_normal = closest_vec * (1.0 / distance);
+
+    new_entry->set_surface_point(into_closest + surface_normal * _radius);
+    new_entry->set_interior_point(from_closest - surface_normal * from_radius);
+
+    if (has_effective_normal() && tube->get_respect_effective_normal()) {
+      new_entry->set_surface_normal(get_effective_normal());
+    } else if (distance != 0) {
+      new_entry->set_surface_normal(surface_normal);
+    }
+  } else {
+    // The rare case of the line segments touching exactly.
+    set_intersection_point(new_entry, into_closest, 0);
+  }
+
+  return new_entry;
+}
+
 /**
  *
  */
@@ -578,6 +642,80 @@ calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices,
   return LVertex(x, y, z);
 }
 
+/**
+ * Given line segments s1 and s2 defined by two points each, computes the
+ * point on each segment with the closest distance between them.
+ */
+void CollisionTube::
+calc_closest_segment_points(double &t1, double &t2,
+                            const LPoint3 &from1, const LVector3 &delta1,
+                            const LPoint3 &from2, const LVector3 &delta2) {
+  // Copyright 2001 softSurfer, 2012 Dan Sunday
+  // This code may be freely used, distributed and modified for any purpose
+  // providing that this copyright notice is included with it.
+  // SoftSurfer makes no warranty for this code, and cannot be held
+  // liable for any real or imagined damage resulting from its use.
+  // Users of this code must verify correctness for their application.
+  LVector3 w = from1 - from2;
+  PN_stdfloat a = delta1.dot(delta1); // always >= 0
+  PN_stdfloat b = delta1.dot(delta2);
+  PN_stdfloat c = delta2.dot(delta2); // always >= 0
+  PN_stdfloat d = delta1.dot(w);
+  PN_stdfloat e = delta2.dot(w);
+  PN_stdfloat D = a * c - b * b; // always >= 0
+  PN_stdfloat sN, sD = D;
+  PN_stdfloat tN, tD = D;
+
+  // compute the line parameters of the two closest points
+  if (IS_NEARLY_ZERO(D)) { // the lines are almost parallel
+    sN = 0.0; // force using point P0 on segment S1
+    sD = 1.0; // to prevent possible division by 0.0 later
+    tN = e;
+    tD = c;
+  } else {
+    // get the closest points on the infinite lines
+    sN = (b*e - c*d);
+    tN = (a*e - b*d);
+    if (sN < 0.0) { // sc < 0 => the s=0 edge is visible
+      sN = 0.0;
+      tN = e;
+      tD = c;
+    } else if (sN > sD) { // sc > 1  => the s=1 edge is visible
+      sN = sD;
+      tN = e + b;
+      tD = c;
+    }
+  }
+
+  if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
+    tN = 0.0;
+    // recompute sc for this edge
+    if (-d < 0.0) {
+      sN = 0.0;
+    } else if (-d > a) {
+      sN = sD;
+    } else {
+      sN = -d;
+      sD = a;
+    }
+  } else if (tN > tD) { // tc > 1  => the t=1 edge is visible
+    tN = tD;
+    // recompute sc for this edge
+    if ((-d + b) < 0.0) {
+      sN = 0;
+    } else if ((-d + b) > a) {
+      sN = sD;
+    } else {
+      sN = (-d +  b);
+      sD = a;
+    }
+  }
+
+  // finally do the division to get sc and tc
+  t1 = (IS_NEARLY_ZERO(sN) ? 0.0 : sN / sD);
+  t2 = (IS_NEARLY_ZERO(tN) ? 0.0 : tN / tD);
+}
+
 /**
  * Determine the point(s) of intersection of a parametric line with the tube.
  * The line is infinite in both directions, and passes through "from" and
@@ -692,7 +830,7 @@ intersects_line(double &t1, double &t2,
     // The starting point is off the bottom of the tube.  Test the line
     // against the first endcap.
     double t1a, t2a;
-    if (!sphere_intersects_line(t1a, t2a, 0.0f, from, delta, inflate_radius)) {
+    if (!sphere_intersects_line(t1a, t2a, 0.0f, from, delta, radius)) {
       // If there's no intersection with the endcap, there can't be an
       // intersection with the cylinder.
       return false;
@@ -703,7 +841,7 @@ intersects_line(double &t1, double &t2,
     // The starting point is off the top of the tube.  Test the line against
     // the second endcap.
     double t1b, t2b;
-    if (!sphere_intersects_line(t1b, t2b, _length, from, delta, inflate_radius)) {
+    if (!sphere_intersects_line(t1b, t2b, _length, from, delta, radius)) {
       // If there's no intersection with the endcap, there can't be an
       // intersection with the cylinder.
       return false;
@@ -715,7 +853,7 @@ intersects_line(double &t1, double &t2,
     // The ending point is off the bottom of the tube.  Test the line against
     // the first endcap.
     double t1a, t2a;
-    if (!sphere_intersects_line(t1a, t2a, 0.0f, from, delta, inflate_radius)) {
+    if (!sphere_intersects_line(t1a, t2a, 0.0f, from, delta, radius)) {
       // If there's no intersection with the endcap, there can't be an
       // intersection with the cylinder.
       return false;
@@ -726,7 +864,7 @@ intersects_line(double &t1, double &t2,
     // The ending point is off the top of the tube.  Test the line against the
     // second endcap.
     double t1b, t2b;
-    if (!sphere_intersects_line(t1b, t2b, _length, from, delta, inflate_radius)) {
+    if (!sphere_intersects_line(t1b, t2b, _length, from, delta, radius)) {
       // If there's no intersection with the endcap, there can't be an
       // intersection with the cylinder.
       return false;
@@ -740,16 +878,14 @@ intersects_line(double &t1, double &t2,
 /**
  * After confirming that the line intersects an infinite cylinder, test
  * whether it intersects one or the other endcaps.  The y parameter specifies
- * the center of the sphere (and hence the particular endcap.
+ * the center of the sphere (and hence the particular endcap).
  */
 bool CollisionTube::
 sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y,
                        const LPoint3 &from, const LVector3 &delta,
-                       PN_stdfloat inflate_radius) const {
+                       PN_stdfloat radius) {
   // See CollisionSphere::intersects_line() for a derivation of the formula
   // here.
-  PN_stdfloat radius = _radius + inflate_radius;
-
   double A = dot(delta, delta);
 
   nassertr(A != 0.0, false);

+ 10 - 3
panda/src/collide/collisionTube.h

@@ -82,6 +82,8 @@ protected:
   virtual PT(CollisionEntry)
   test_intersection_from_segment(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
+  test_intersection_from_tube(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
   test_intersection_from_parabola(const CollisionEntry &entry) const;
 
   virtual void fill_viz_geom();
@@ -93,12 +95,15 @@ private:
   LVertex calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices,
                               PN_stdfloat length);
 
+  static void calc_closest_segment_points(double &t1, double &t2,
+                                          const LPoint3 &from1, const LVector3 &delta1,
+                                          const LPoint3 &from2, const LVector3 &delta2);
   bool intersects_line(double &t1, double &t2,
                        const LPoint3 &from, const LVector3 &delta,
                        PN_stdfloat inflate_radius) const;
-  bool sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y,
-                              const LPoint3 &from, const LVector3 &delta,
-                              PN_stdfloat inflate_radius) const;
+  static bool sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y,
+                                     const LPoint3 &from, const LVector3 &delta,
+                                     PN_stdfloat radius);
   bool intersects_parabola(double &t, const LParabola &parabola,
                            double t1, double t2,
                            const LPoint3 &p1, const LPoint3 &p2) const;
@@ -146,6 +151,8 @@ public:
 
 private:
   static TypeHandle _type_handle;
+
+  friend class CollisionBox;
 };
 
 #include "collisionTube.I"

+ 0 - 70
panda/src/collide/test_collide.cxx

@@ -1,70 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file test_collide.cxx
- * @author drose
- * @date 2000-04-24
- */
-
-#include "collisionTraverser.h"
-#include "collisionNode.h"
-#include "collisionSphere.h"
-#include "collisionPlane.h"
-#include "collisionHandlerPusher.h"
-
-#include "namedNode.h"
-#include "pt_NamedNode.h"
-#include "pointerTo.h"
-#include "transformTransition.h"
-#include "luse.h"
-#include "get_rel_pos.h"
-#include "renderRelation.h"
-
-int
-main(int argc, char *argv[]) {
-  PT_NamedNode r = new NamedNode("r");
-
-  PT_NamedNode a = new NamedNode("a");
-  PT_NamedNode b = new NamedNode("b");
-
-  PT(CollisionNode) aa = new CollisionNode("aa");
-  PT(CollisionNode) ab = new CollisionNode("ab");
-  PT(CollisionNode) ba = new CollisionNode("ba");
-
-  RenderRelation *r_a = new RenderRelation(r, a);
-  RenderRelation *r_b = new RenderRelation(r, b);
-
-  RenderRelation *a_aa = new RenderRelation(a, aa);
-  RenderRelation *a_ab = new RenderRelation(a, ab);
-  RenderRelation *b_ba = new RenderRelation(b, ba);
-
-
-  CollisionSphere *aa1 = new CollisionSphere(LPoint3f(0, 0, 0), 1);
-  aa->add_solid(aa1);
-  a_aa->set_transition(new TransformTransition(LMatrix4f::translate_mat(0, -5, 0)));
-
-  CollisionSphere *ab1 = new CollisionSphere(LPoint3f(0, 2, 0), 1.5);
-  ab->add_solid(ab1);
-
-  Planef plane(LVector3f(0, 1, 0), LPoint3f(0, 0, 0));
-  CollisionPlane *ba1 = new CollisionPlane(plane);
-  ba->add_solid(ba1);
-
-  CollisionTraverser ct;
-  PT(CollisionHandlerPusher) chp = new CollisionHandlerPusher;
-  chp->add_collider(aa, a_aa);
-  ct.add_collider(aa, chp);
-
-  ct.traverse(r);
-
-  nout << "\nFrame 2:\n\n";
-
-  ct.traverse(r);
-
-  return (0);
-}

+ 3 - 5
panda/src/cull/cullBinBackToFront.cxx

@@ -85,9 +85,6 @@ void CullBinBackToFront::
 draw(bool force, Thread *current_thread) {
   PStatTimer timer(_draw_this_pcollector, current_thread);
 
-  GeomPipelineReader geom_reader(current_thread);
-  GeomVertexDataPipelineReader data_reader(current_thread);
-
   Objects::const_iterator oi;
   for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
     CullableObject *object = (*oi)._object;
@@ -96,9 +93,10 @@ draw(bool force, Thread *current_thread) {
       nassertd(object->_geom != nullptr) continue;
 
       _gsg->set_state_and_transform(object->_state, object->_internal_transform);
-      data_reader.set_object(object->_munged_data);
+
+      GeomPipelineReader geom_reader(object->_geom, current_thread);
+      GeomVertexDataPipelineReader data_reader(object->_munged_data, current_thread);
       data_reader.check_array_readers();
-      geom_reader.set_object(object->_geom);
       geom_reader.draw(_gsg, &data_reader, force);
     } else {
       // It has a callback associated.

+ 3 - 5
panda/src/cull/cullBinFixed.cxx

@@ -71,9 +71,6 @@ void CullBinFixed::
 draw(bool force, Thread *current_thread) {
   PStatTimer timer(_draw_this_pcollector, current_thread);
 
-  GeomPipelineReader geom_reader(current_thread);
-  GeomVertexDataPipelineReader data_reader(current_thread);
-
   Objects::const_iterator oi;
   for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
     CullableObject *object = (*oi)._object;
@@ -82,9 +79,10 @@ draw(bool force, Thread *current_thread) {
       nassertd(object->_geom != nullptr) continue;
 
       _gsg->set_state_and_transform(object->_state, object->_internal_transform);
-      data_reader.set_object(object->_munged_data);
+
+      GeomPipelineReader geom_reader(object->_geom, current_thread);
+      GeomVertexDataPipelineReader data_reader(object->_munged_data, current_thread);
       data_reader.check_array_readers();
-      geom_reader.set_object(object->_geom);
       geom_reader.draw(_gsg, &data_reader, force);
     } else {
       // It has a callback associated.

+ 3 - 5
panda/src/cull/cullBinFrontToBack.cxx

@@ -85,9 +85,6 @@ void CullBinFrontToBack::
 draw(bool force, Thread *current_thread) {
   PStatTimer timer(_draw_this_pcollector, current_thread);
 
-  GeomPipelineReader geom_reader(current_thread);
-  GeomVertexDataPipelineReader data_reader(current_thread);
-
   Objects::const_iterator oi;
   for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
     CullableObject *object = (*oi)._object;
@@ -96,9 +93,10 @@ draw(bool force, Thread *current_thread) {
       nassertd(object->_geom != nullptr) continue;
 
       _gsg->set_state_and_transform(object->_state, object->_internal_transform);
-      data_reader.set_object(object->_munged_data);
+
+      GeomPipelineReader geom_reader(object->_geom, current_thread);
+      GeomVertexDataPipelineReader data_reader(object->_munged_data, current_thread);
       data_reader.check_array_readers();
-      geom_reader.set_object(object->_geom);
       geom_reader.draw(_gsg, &data_reader, force);
     } else {
       // It has a callback associated.

+ 3 - 5
panda/src/cull/cullBinStateSorted.cxx

@@ -70,9 +70,6 @@ void CullBinStateSorted::
 draw(bool force, Thread *current_thread) {
   PStatTimer timer(_draw_this_pcollector, current_thread);
 
-  GeomPipelineReader geom_reader(current_thread);
-  GeomVertexDataPipelineReader data_reader(current_thread);
-
   Objects::const_iterator oi;
   for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
     CullableObject *object = (*oi)._object;
@@ -81,9 +78,10 @@ draw(bool force, Thread *current_thread) {
       nassertd(object->_geom != nullptr) continue;
 
       _gsg->set_state_and_transform(object->_state, object->_internal_transform);
-      data_reader.set_object(object->_munged_data);
+
+      GeomPipelineReader geom_reader(object->_geom, current_thread);
+      GeomVertexDataPipelineReader data_reader(object->_munged_data, current_thread);
       data_reader.check_array_readers();
-      geom_reader.set_object(object->_geom);
       geom_reader.draw(_gsg, &data_reader, force);
     } else {
       // It has a callback associated.

+ 3 - 5
panda/src/cull/cullBinUnsorted.cxx

@@ -55,9 +55,6 @@ void CullBinUnsorted::
 draw(bool force, Thread *current_thread) {
   PStatTimer timer(_draw_this_pcollector, current_thread);
 
-  GeomPipelineReader geom_reader(current_thread);
-  GeomVertexDataPipelineReader data_reader(current_thread);
-
   Objects::iterator oi;
   for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
     CullableObject *object = (*oi);
@@ -66,9 +63,10 @@ draw(bool force, Thread *current_thread) {
       nassertd(object->_geom != nullptr) continue;
 
       _gsg->set_state_and_transform(object->_state, object->_internal_transform);
-      data_reader.set_object(object->_munged_data);
+
+      GeomPipelineReader geom_reader(object->_geom, current_thread);
+      GeomVertexDataPipelineReader data_reader(object->_munged_data, current_thread);
       data_reader.check_array_readers();
-      geom_reader.set_object(object->_geom);
       geom_reader.draw(_gsg, &data_reader, force);
     } else {
       // It has a callback associated.

+ 0 - 18
panda/src/display/test_display.cxx

@@ -1,18 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file test_display.cxx
- * @author shochet
- * @date 2000-02-02
- */
-
-#include "graphicsWindow.h"
-
-int main() {
-  return 0;
-}

+ 10 - 4
panda/src/dxgsg9/dxGeomMunger9.cxx

@@ -300,8 +300,11 @@ compare_to_impl(const GeomMunger *other) const {
   if (_filtered_texture != om->_filtered_texture) {
     return _filtered_texture < om->_filtered_texture ? -1 : 1;
   }
-  if (_tex_gen != om->_tex_gen) {
-    return _tex_gen < om->_tex_gen ? -1 : 1;
+  if (_tex_gen.owner_before(om->_tex_gen)) {
+    return -1;
+  }
+  if (om->_tex_gen.owner_before(_tex_gen)) {
+    return 1;
   }
 
   return StandardMunger::compare_to_impl(other);
@@ -321,8 +324,11 @@ geom_compare_to_impl(const GeomMunger *other) const {
   if (_filtered_texture != om->_filtered_texture) {
     return _filtered_texture < om->_filtered_texture ? -1 : 1;
   }
-  if (_tex_gen != om->_tex_gen) {
-    return _tex_gen < om->_tex_gen ? -1 : 1;
+  if (_tex_gen.owner_before(om->_tex_gen)) {
+    return -1;
+  }
+  if (om->_tex_gen.owner_before(_tex_gen)) {
+    return 1;
   }
 
   return StandardMunger::geom_compare_to_impl(other);

+ 6 - 0
panda/src/egg/eggData.h

@@ -68,6 +68,12 @@ PUBLISHED:
   INLINE void set_egg_timestamp(time_t egg_timestamp);
   INLINE time_t get_egg_timestamp() const;
 
+  MAKE_PROPERTY(auto_resolve_externals, get_auto_resolve_externals,
+                                        set_auto_resolve_externals);
+  MAKE_PROPERTY(coordinate_system, get_coordinate_system, set_coordinate_system);
+  MAKE_PROPERTY(egg_filename, get_egg_filename, set_egg_filename);
+  MAKE_PROPERTY(egg_timestamp, get_egg_timestamp, set_egg_timestamp);
+
   INLINE void recompute_vertex_normals(double threshold);
   INLINE void recompute_polygon_normals();
   INLINE void strip_normals();

+ 1 - 0
panda/src/egg/eggGroupNode.h

@@ -109,6 +109,7 @@ PUBLISHED:
   EggNode *get_next_child();
 
   EXTENSION(PyObject *get_children() const);
+  MAKE_PROPERTY(children, get_children);
 
   EggNode *add_child(EggNode *node);
   PT(EggNode) remove_child(EggNode *node);

+ 9 - 6
panda/src/egg/eggNode.h

@@ -44,6 +44,9 @@ PUBLISHED:
   INLINE bool is_under_transform() const;
   INLINE bool is_local_coord() const;
 
+  MAKE_PROPERTY(parent, get_parent);
+  MAKE_PROPERTY(depth, get_depth);
+
   INLINE const LMatrix4d &get_vertex_frame() const;
   INLINE const LMatrix4d &get_node_frame() const;
   INLINE const LMatrix4d &get_vertex_frame_inv() const;
@@ -51,12 +54,12 @@ PUBLISHED:
   INLINE const LMatrix4d &get_vertex_to_node() const;
   INLINE const LMatrix4d &get_node_to_vertex() const;
 
-  INLINE const LMatrix4d *get_vertex_frame_ptr()const;
-  INLINE const LMatrix4d *get_node_frame_ptr()const;
-  INLINE const LMatrix4d *get_vertex_frame_inv_ptr()const;
-  INLINE const LMatrix4d *get_node_frame_inv_ptr()const;
-  INLINE const LMatrix4d *get_vertex_to_node_ptr()const;
-  INLINE const LMatrix4d *get_node_to_vertex_ptr()const;
+  INLINE const LMatrix4d *get_vertex_frame_ptr() const;
+  INLINE const LMatrix4d *get_node_frame_ptr() const;
+  INLINE const LMatrix4d *get_vertex_frame_inv_ptr() const;
+  INLINE const LMatrix4d *get_node_frame_inv_ptr() const;
+  INLINE const LMatrix4d *get_vertex_to_node_ptr() const;
+  INLINE const LMatrix4d *get_node_to_vertex_ptr() const;
 
   INLINE void transform(const LMatrix4d &mat);
   INLINE void transform_vertices_only(const LMatrix4d &mat);

+ 20 - 0
panda/src/express/pointerTo.h

@@ -215,6 +215,26 @@ void swap(ConstPointerTo<T> &one, ConstPointerTo<T> &two) noexcept {
 }
 
 
+// Define owner_less specializations, for completeness' sake.
+namespace std {
+  template<class T>
+  struct owner_less<PointerTo<T> > {
+    bool operator () (const PointerTo<T> &lhs,
+                      const PointerTo<T> &rhs) const noexcept {
+      return lhs < rhs;
+    }
+  };
+
+  template<class T>
+  struct owner_less<ConstPointerTo<T> > {
+    bool operator () (const ConstPointerTo<T> &lhs,
+                      const ConstPointerTo<T> &rhs) const noexcept {
+      return lhs < rhs;
+    }
+  };
+}
+
+
 // Finally, we'll define a couple of handy abbreviations to save on all that
 // wasted typing time.
 

+ 1 - 0
panda/src/express/pointerToBase.h

@@ -53,6 +53,7 @@ protected:
 
   // This is needed to be able to access the privates of other instantiations.
   template<typename Y> friend class PointerToBase;
+  template<typename Y> friend class WeakPointerToBase;
 
 PUBLISHED:
   ALWAYS_INLINE void clear();

+ 0 - 7
panda/src/express/pointerToVoid.I

@@ -11,13 +11,6 @@
  * @date 2004-09-27
  */
 
-/**
- *
- */
-constexpr PointerToVoid::
-PointerToVoid() noexcept : _void_ptr(nullptr) {
-}
-
 /**
  *
  */

+ 2 - 2
panda/src/express/pointerToVoid.h

@@ -32,7 +32,7 @@
  */
 class EXPCL_PANDA_EXPRESS PointerToVoid : public MemoryBase {
 protected:
-  constexpr PointerToVoid() noexcept;
+  constexpr PointerToVoid() noexcept = default;
   //INLINE ~PointerToVoid();
 
 private:
@@ -63,7 +63,7 @@ protected:
   // a PointerTo any class that inherits virtually from ReferenceCount.  (You
   // can't downcast past a virtual inheritance level, but you can always
   // cross-cast from a void pointer.)
-  AtomicAdjust::Pointer _void_ptr;
+  AtomicAdjust::Pointer _void_ptr = nullptr;
 };
 
 #include "pointerToVoid.I"

+ 264 - 34
panda/src/express/weakPointerTo.I

@@ -39,6 +39,49 @@ WeakPointerTo(const WeakPointerTo<T> &copy) :
 {
 }
 
+/**
+ *
+ */
+template<class T>
+INLINE WeakPointerTo<T>::
+WeakPointerTo(WeakPointerTo<T> &&from) noexcept :
+  WeakPointerToBase<T>(std::move(from))
+{
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakPointerTo<T>::
+WeakPointerTo(const WeakPointerTo<Y> &r) noexcept :
+  WeakPointerToBase<T>(r)
+{
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakPointerTo<T>::
+WeakPointerTo(const PointerTo<Y> &r) noexcept :
+  WeakPointerToBase<T>(r)
+{
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakPointerTo<T>::
+WeakPointerTo(WeakPointerTo<Y> &&r) noexcept :
+  WeakPointerToBase<T>(std::move(r))
+{
+}
+
 /**
  *
  */
@@ -82,23 +125,9 @@ operator T * () const {
 template<class T>
 INLINE PointerTo<T> WeakPointerTo<T>::
 lock() const {
-  WeakReferenceList *weak_ref = this->_weak_ref;
-  if (weak_ref != nullptr) {
-    PointerTo<T> ptr;
-    weak_ref->_lock.lock();
-    if (!weak_ref->was_deleted()) {
-      // We also need to check that the reference count is not zero (which can
-      // happen if the object is currently being destructed), since that could
-      // cause double deletion.
-      To *plain_ptr = (To *)WeakPointerToBase<T>::_void_ptr;
-      if (plain_ptr != nullptr && plain_ptr->ref_if_nonzero()) {
-        ptr.cheat() = plain_ptr;
-      }
-    }
-    weak_ref->_lock.unlock();
-    return ptr;
-  }
-  return nullptr;
+  PointerTo<T> ptr;
+  this->lock_into(ptr);
+  return ptr;
 }
 
 /**
@@ -152,6 +181,49 @@ operator = (const WeakPointerTo<T> &copy) {
   return *this;
 }
 
+/**
+ *
+ */
+template<class T>
+INLINE WeakPointerTo<T> &WeakPointerTo<T>::
+operator = (WeakPointerTo<T> &&from) noexcept {
+  this->reassign(std::move(from));
+  return *this;
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakPointerTo<T> &WeakPointerTo<T>::
+operator = (const WeakPointerTo<Y> &r) noexcept {
+  this->reassign(r);
+  return *this;
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakPointerTo<T> &WeakPointerTo<T>::
+operator = (const PointerTo<Y> &r) noexcept {
+  this->reassign(r);
+  return *this;
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakPointerTo<T> &WeakPointerTo<T>::
+operator = (WeakPointerTo<Y> &&r) noexcept {
+  this->reassign(std::move(r));
+  return *this;
+}
+
 /**
  *
  */
@@ -202,6 +274,92 @@ WeakConstPointerTo(const WeakConstPointerTo<T> &copy) :
 {
 }
 
+/**
+ *
+ */
+template<class T>
+INLINE WeakConstPointerTo<T>::
+WeakConstPointerTo(WeakPointerTo<T> &&from) noexcept :
+  WeakPointerToBase<T>(std::move(from))
+{
+}
+
+/**
+ *
+ */
+template<class T>
+INLINE WeakConstPointerTo<T>::
+WeakConstPointerTo(WeakConstPointerTo<T> &&from) noexcept :
+  WeakPointerToBase<T>(std::move(from))
+{
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T>::
+WeakConstPointerTo(const WeakPointerTo<Y> &r) noexcept :
+  WeakPointerToBase<T>(r)
+{
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T>::
+WeakConstPointerTo(const WeakConstPointerTo<Y> &r) noexcept :
+  WeakPointerToBase<T>(r)
+{
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T>::
+WeakConstPointerTo(const PointerTo<Y> &r) noexcept :
+  WeakPointerToBase<T>(r)
+{
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T>::
+WeakConstPointerTo(const ConstPointerTo<Y> &r) noexcept :
+  WeakPointerToBase<T>(r)
+{
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T>::
+WeakConstPointerTo(WeakPointerTo<Y> &&r) noexcept :
+  WeakPointerToBase<T>(std::move(r))
+{
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T>::
+WeakConstPointerTo(WeakConstPointerTo<Y> &&r) noexcept :
+  WeakPointerToBase<T>(std::move(r))
+{
+}
+
 /**
  *
  */
@@ -243,23 +401,9 @@ operator const T * () const {
 template<class T>
 INLINE ConstPointerTo<T> WeakConstPointerTo<T>::
 lock() const {
-  WeakReferenceList *weak_ref = this->_weak_ref;
-  if (weak_ref != nullptr) {
-    ConstPointerTo<T> ptr;
-    weak_ref->_lock.lock();
-    if (!weak_ref->was_deleted()) {
-      // We also need to check that the reference count is not zero (which can
-      // happen if the object is currently being destructed), since that could
-      // cause double deletion.
-      const To *plain_ptr = (const To *)WeakPointerToBase<T>::_void_ptr;
-      if (plain_ptr != nullptr && plain_ptr->ref_if_nonzero()) {
-        ptr.cheat() = plain_ptr;
-      }
-    }
-    weak_ref->_lock.unlock();
-    return ptr;
-  }
-  return nullptr;
+  ConstPointerTo<T> ptr;
+  this->lock_into(ptr);
+  return ptr;
 }
 
 /**
@@ -332,3 +476,89 @@ operator = (const WeakConstPointerTo<T> &copy) {
   ((WeakConstPointerTo<T> *)this)->reassign(*(const PointerToBase<T> *)&copy);
   return *this;
 }
+
+/**
+ *
+ */
+template<class T>
+INLINE WeakConstPointerTo<T> &WeakConstPointerTo<T>::
+operator = (WeakPointerTo<T> &&from) noexcept {
+  this->reassign(std::move(from));
+  return *this;
+}
+
+/**
+ *
+ */
+template<class T>
+INLINE WeakConstPointerTo<T> &WeakConstPointerTo<T>::
+operator = (WeakConstPointerTo<T> &&from) noexcept {
+  this->reassign(std::move(from));
+  return *this;
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T> &WeakConstPointerTo<T>::
+operator = (const WeakPointerTo<Y> &r) noexcept {
+  this->reassign(r);
+  return *this;
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T> &WeakConstPointerTo<T>::
+operator = (const WeakConstPointerTo<Y> &r) noexcept {
+  this->reassign(r);
+  return *this;
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T> &WeakConstPointerTo<T>::
+operator = (const PointerTo<Y> &r) noexcept {
+  this->reassign(r);
+  return *this;
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T> &WeakConstPointerTo<T>::
+operator = (const ConstPointerTo<Y> &r) noexcept {
+  this->reassign(r);
+  return *this;
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T> &WeakConstPointerTo<T>::
+operator = (WeakPointerTo<Y> &&r) noexcept {
+  this->reassign(std::move(r));
+  return *this;
+}
+
+/**
+ *
+ */
+template<class T>
+template<class Y>
+ALWAYS_INLINE WeakConstPointerTo<T> &WeakConstPointerTo<T>::
+operator = (WeakConstPointerTo<Y> &&r) noexcept {
+  this->reassign(std::move(r));
+  return *this;
+}

+ 77 - 2
panda/src/express/weakPointerTo.h

@@ -30,11 +30,21 @@ class WeakPointerTo : public WeakPointerToBase<T> {
 public:
   typedef typename WeakPointerToBase<T>::To To;
 PUBLISHED:
-  INLINE WeakPointerTo(To *ptr = nullptr);
+  constexpr WeakPointerTo() noexcept = default;
+  INLINE WeakPointerTo(To *ptr);
   INLINE WeakPointerTo(const PointerTo<T> &copy);
   INLINE WeakPointerTo(const WeakPointerTo<T> &copy);
 
 public:
+  INLINE WeakPointerTo(WeakPointerTo<T> &&from) noexcept;
+
+  template<class Y>
+  ALWAYS_INLINE WeakPointerTo(const WeakPointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakPointerTo(const PointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakPointerTo(WeakPointerTo<Y> &&r) noexcept;
+
   INLINE To &operator *() const;
   INLINE To *operator -> () const;
   // MSVC.NET 2005 insists that we use T *, and not To *, here.
@@ -49,6 +59,17 @@ PUBLISHED:
   INLINE WeakPointerTo<T> &operator = (const PointerTo<T> &copy);
   INLINE WeakPointerTo<T> &operator = (const WeakPointerTo<T> &copy);
 
+public:
+  INLINE WeakPointerTo<T> &operator = (WeakPointerTo<T> &&from) noexcept;
+
+  template<class Y>
+  ALWAYS_INLINE WeakPointerTo<T> &operator = (const WeakPointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakPointerTo<T> &operator = (const PointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakPointerTo<T> &operator = (WeakPointerTo<Y> &&r) noexcept;
+
+PUBLISHED:
   // This function normally wouldn't need to be redefined here, but we do so
   // anyway just to help out interrogate (which doesn't seem to want to
   // automatically export the WeakPointerToBase class).  When this works again
@@ -66,13 +87,30 @@ class WeakConstPointerTo : public WeakPointerToBase<T> {
 public:
   typedef typename WeakPointerToBase<T>::To To;
 PUBLISHED:
-  INLINE WeakConstPointerTo(const To *ptr = nullptr);
+  constexpr WeakConstPointerTo() noexcept = default;
+  INLINE WeakConstPointerTo(const To *ptr);
   INLINE WeakConstPointerTo(const PointerTo<T> &copy);
   INLINE WeakConstPointerTo(const ConstPointerTo<T> &copy);
   INLINE WeakConstPointerTo(const WeakPointerTo<T> &copy);
   INLINE WeakConstPointerTo(const WeakConstPointerTo<T> &copy);
 
 public:
+  INLINE WeakConstPointerTo(WeakPointerTo<T> &&from) noexcept;
+  INLINE WeakConstPointerTo(WeakConstPointerTo<T> &&from) noexcept;
+
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo(const WeakPointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo(const WeakConstPointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo(const PointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo(const ConstPointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo(WeakPointerTo<Y> &&r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo(WeakConstPointerTo<Y> &&r) noexcept;
+
   INLINE const To &operator *() const;
   INLINE const To *operator -> () const;
   INLINE explicit operator const T *() const;
@@ -88,6 +126,24 @@ PUBLISHED:
   INLINE WeakConstPointerTo<T> &operator = (const WeakPointerTo<T> &copy);
   INLINE WeakConstPointerTo<T> &operator = (const WeakConstPointerTo<T> &copy);
 
+public:
+  INLINE WeakConstPointerTo<T> &operator = (WeakPointerTo<T> &&from) noexcept;
+  INLINE WeakConstPointerTo<T> &operator = (WeakConstPointerTo<T> &&from) noexcept;
+
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo<T> &operator = (const WeakPointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo<T> &operator = (const WeakConstPointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo<T> &operator = (const PointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo<T> &operator = (const ConstPointerTo<Y> &r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo<T> &operator = (WeakPointerTo<Y> &&r) noexcept;
+  template<class Y>
+  ALWAYS_INLINE WeakConstPointerTo<T> &operator = (WeakConstPointerTo<Y> &&r) noexcept;
+
+PUBLISHED:
   // These functions normally wouldn't need to be redefined here, but we do so
   // anyway just to help out interrogate (which doesn't seem to want to
   // automatically export the WeakPointerToBase class).  When this works again
@@ -96,6 +152,25 @@ PUBLISHED:
   INLINE void clear() { WeakPointerToBase<T>::clear(); }
 };
 
+// Provide specializations of std::owner_less, for using a WPT as a map key.
+namespace std {
+  template<class T>
+  struct owner_less<WeakPointerTo<T> > {
+    bool operator () (const WeakPointerTo<T> &lhs,
+                      const WeakPointerTo<T> &rhs) const noexcept {
+      return lhs.owner_before(rhs);
+    }
+  };
+
+  template<class T>
+  struct owner_less<WeakConstPointerTo<T> > {
+    bool operator () (const WeakConstPointerTo<T> &lhs,
+                      const WeakConstPointerTo<T> &rhs) const noexcept {
+      return lhs.owner_before(rhs);
+    }
+  };
+}
+
 #define WPT(type) WeakPointerTo< type >
 #define WCPT(type) WeakConstPointerTo< type >
 

+ 191 - 33
panda/src/express/weakPointerToBase.I

@@ -12,7 +12,8 @@
  */
 
 /**
- *
+ * Constructs a weak pointer from a plain pointer (or nullptr).  It is the
+ * caller's responsibility to ensure that it points to a valid object.
  */
 template<class T>
 INLINE WeakPointerToBase<T>::
@@ -27,7 +28,7 @@ WeakPointerToBase(To *ptr) {
 }
 
 /**
- *
+ * Constructs a weak pointer from a reference-counting pointer.
  */
 template<class T>
 INLINE WeakPointerToBase<T>::
@@ -42,44 +43,72 @@ WeakPointerToBase(const PointerToBase<T> &copy) {
 }
 
 /**
- *
+ * Copies a weak pointer.  This is always safe, even for expired pointers.
  */
 template<class T>
 INLINE WeakPointerToBase<T>::
 WeakPointerToBase(const WeakPointerToBase<T> &copy) {
   _void_ptr = copy._void_ptr;
 
-  // Don't bother increasing the weak reference count if the object was
-  // already deleted.
+  // While it is tempting to stop maintaining the control block pointer after
+  // the object has been deleted, we still need it in order to define a
+  // consistent ordering in owner_before.
   WeakReferenceList *weak_ref = copy._weak_ref;
-  if (weak_ref != nullptr && !weak_ref->was_deleted()) {
+  if (weak_ref != nullptr/* && !weak_ref->was_deleted()*/) {
     _weak_ref = copy._weak_ref;
     _weak_ref->ref();
   }
 }
 
 /**
- *
+ * Moves a weak pointer.  This is always safe, even for expired pointers.
  */
 template<class T>
 INLINE WeakPointerToBase<T>::
 WeakPointerToBase(WeakPointerToBase<T> &&from) noexcept {
-  // Protect against self-move-assignment.
-  if (from._void_ptr != this->_void_ptr) {
-    WeakReferenceList *old_ref = (To *)this->_weak_ref;
+  this->_void_ptr = from._void_ptr;
+  this->_weak_ref = from._weak_ref;
+  from._void_ptr = nullptr;
+  from._weak_ref = nullptr;
+}
 
-    this->_void_ptr = from._void_ptr;
-    this->_weak_ref = from._weak_ref;
-    from._void_ptr = nullptr;
-    from._weak_ref = nullptr;
+/**
+ * Copies a weak pointer from a cast-convertible weak pointer type.
+ */
+template<class T>
+template<class Y>
+INLINE WeakPointerToBase<T>::
+WeakPointerToBase(const WeakPointerToBase<Y> &r) {
+  // If this next line gives an error, you are trying to convert a WeakPointerTo
+  // from an incompatible type of another WeakPointerTo.
+  To *ptr = (Y *)r._void_ptr;
 
-    // Now delete the old pointer.
-    if (old_ref != nullptr && !old_ref->unref()) {
-      delete old_ref;
-    }
+  this->_void_ptr = ptr;
+
+  WeakReferenceList *weak_ref = r._weak_ref;
+  if (weak_ref != nullptr) {
+    _weak_ref = weak_ref;
+    weak_ref->ref();
   }
 }
 
+/**
+ * Moves a weak pointer from a cast-convertible weak pointer type.
+ */
+template<class T>
+template<class Y>
+INLINE WeakPointerToBase<T>::
+WeakPointerToBase(WeakPointerToBase<Y> &&r) noexcept {
+  // If this next line gives an error, you are trying to convert a WeakPointerTo
+  // from an incompatible type of another WeakPointerTo.
+  To *ptr = (Y *)r._void_ptr;
+
+  this->_void_ptr = ptr;
+  this->_weak_ref = r._weak_ref;
+  r._void_ptr = nullptr;
+  r._weak_ref = nullptr;
+}
+
 /**
  *
  */
@@ -141,10 +170,11 @@ reassign(const WeakPointerToBase<To> &copy) {
     WeakReferenceList *old_ref = (WeakReferenceList *)_weak_ref;
     _void_ptr = new_ptr;
 
-    // Don't bother increasing the weak reference count if the object was
-    // already deleted.
+    // While it is tempting to stop maintaining the control block pointer
+    // after the object has been deleted, we still need it in order to define
+    // a consistent ordering in owner_before.
     WeakReferenceList *weak_ref = copy._weak_ref;
-    if (weak_ref != nullptr && !weak_ref->was_deleted()) {
+    if (weak_ref != nullptr/* && !weak_ref->was_deleted()*/) {
       weak_ref->ref();
       _weak_ref = weak_ref;
     } else {
@@ -180,6 +210,61 @@ reassign(WeakPointerToBase<To> &&from) noexcept {
   }
 }
 
+/**
+ * Like above, but casts from a compatible pointer type.
+ */
+template<class T>
+template<class Y>
+INLINE void WeakPointerToBase<T>::
+reassign(const WeakPointerToBase<Y> &copy) {
+  // If there is a compile error on this line, it means you tried to assign
+  // an incompatible type.
+  To *new_ptr = (Y *)copy._void_ptr;
+
+  if (new_ptr != (To *)_void_ptr) {
+    WeakReferenceList *old_ref = (WeakReferenceList *)_weak_ref;
+    WeakReferenceList *new_ref = copy._weak_ref;
+    _void_ptr = new_ptr;
+    _weak_ref = new_ref;
+
+    if (new_ref != nullptr) {
+      new_ref->ref();
+    }
+
+    // Now remove the old reference.
+    if (old_ref != nullptr && !old_ref->unref()) {
+      delete old_ref;
+    }
+  }
+}
+
+/**
+ * Like above, but casts from a compatible pointer type.
+ */
+template<class T>
+template<class Y>
+INLINE void WeakPointerToBase<T>::
+reassign(WeakPointerToBase<Y> &&from) noexcept {
+  // Protect against self-move-assignment.
+  if (from._void_ptr != this->_void_ptr) {
+    WeakReferenceList *old_ref = (WeakReferenceList *)this->_weak_ref;
+
+    // If there is a compile error on this line, it means you tried to assign
+    // an incompatible type.
+    To *new_ptr = (Y *)from._void_ptr;
+
+    this->_void_ptr = new_ptr;
+    this->_weak_ref = from._weak_ref;
+    from._void_ptr = nullptr;
+    from._weak_ref = nullptr;
+
+    // Now delete the old pointer.
+    if (old_ref != nullptr && !old_ref->unref()) {
+      delete old_ref;
+    }
+  }
+}
+
 /**
  * Ensures that the MemoryUsage record for the pointer has the right type of
  * object, if we know the type ourselves.
@@ -201,6 +286,34 @@ update_type(To *ptr) {
 #endif  // DO_MEMORY_USAGE
 }
 
+/**
+ * A thread-safe way to access the underlying pointer; will only write to the
+ * given pointer if the underlying pointer has not yet been deleted and is not
+ * null.  Note that it may leave the pointer unassigned even if was_deleted()
+ * still returns true, which can occur if the object has reached reference
+ * count 0 and is about to be destroyed.
+ */
+template<class T>
+INLINE void WeakPointerToBase<T>::
+lock_into(PointerToBase<To> &locked) const {
+  WeakReferenceList *weak_ref = this->_weak_ref;
+  if (weak_ref != nullptr) {
+    weak_ref->_lock.lock();
+    if (!weak_ref->was_deleted()) {
+      // We also need to check that the reference count is not zero (which can
+      // happen if the object is currently being destructed), since that could
+      // cause double deletion.
+      To *plain_ptr = (To *)WeakPointerToBase<T>::_void_ptr;
+      if (plain_ptr != nullptr && plain_ptr->ref_if_nonzero()) {
+        // It is valid and we successfully grabbed a reference.  Assign it,
+        // noting we have already incremented the reference count.
+        locked._void_ptr = plain_ptr;
+      }
+    }
+    weak_ref->_lock.unlock();
+  }
+}
+
 #ifndef CPPPARSER
 /**
  *
@@ -337,7 +450,11 @@ operator >= (std::nullptr_t) const {
 }
 
 /**
- *
+ * Returns true if both pointers have the same raw pointer value.  For this to
+ * be meaningful, neither pointer may have expired, since if one has expired
+ * while the other was allocated at the expired pointer's memory address, this
+ * comparison will be true even though they didn't refer to the same object.
+ * @see owner_before
  */
 template<class T>
 INLINE bool WeakPointerToBase<T>::
@@ -346,7 +463,7 @@ operator == (const WeakPointerToBase<To> &other) const {
 }
 
 /**
- *
+ * @see operator ==
  */
 template<class T>
 INLINE bool WeakPointerToBase<T>::
@@ -355,7 +472,8 @@ operator != (const WeakPointerToBase<To> &other) const {
 }
 
 /**
- *
+ * Defines an ordering between WeakPointerTo based on their raw pointer value.
+ * @deprecated Do not use this.  Use owner_before or std::owner_less instead.
  */
 template<class T>
 INLINE bool WeakPointerToBase<T>::
@@ -364,7 +482,8 @@ operator > (const WeakPointerToBase<To> &other) const {
 }
 
 /**
- *
+ * Defines an ordering between WeakPointerTo based on their raw pointer value.
+ * @deprecated Do not use this.  Use owner_before or std::owner_less instead.
  */
 template<class T>
 INLINE bool WeakPointerToBase<T>::
@@ -373,7 +492,8 @@ operator <= (const WeakPointerToBase<To> &other) const {
 }
 
 /**
- *
+ * Defines an ordering between WeakPointerTo based on their raw pointer value.
+ * @deprecated Do not use this.  Use owner_before or std::owner_less instead.
  */
 template<class T>
 INLINE bool WeakPointerToBase<T>::
@@ -382,7 +502,7 @@ operator >= (const WeakPointerToBase<To> &other) const {
 }
 
 /**
- *
+ * Returns true if both pointers point to the same object.
  */
 template<class T>
 INLINE bool WeakPointerToBase<T>::
@@ -391,7 +511,7 @@ operator == (const PointerToBase<To> &other) const {
 }
 
 /**
- *
+ * Returns false if both pointers point to the same object.
  */
 template<class T>
 INLINE bool WeakPointerToBase<T>::
@@ -445,7 +565,8 @@ operator < (std::nullptr_t) const {
 }
 
 /**
- *
+ * Defines an ordering between WeakPointerTo based on their raw pointer value.
+ * @deprecated Do not use this.  Use owner_before or std::owner_less instead.
  */
 template<class T>
 INLINE bool WeakPointerToBase<T>::
@@ -464,6 +585,35 @@ operator < (const PointerToBase<To> &other) const {
 
 #endif  // CPPPARSER
 
+/**
+ * Defines an ordering that is guaranteed to remain consistent even after the
+ * weak pointers have expired.  This may result in two pointers with the same
+ * get_orig() value comparing unequal if one of them is a new object that was
+ * allocated at the same memory address as the older, expired pointer.
+ */
+template<class T>
+template<class Y>
+INLINE bool WeakPointerToBase<T>::
+owner_before(const WeakPointerToBase<Y> &other) const noexcept {
+  return _weak_ref < other._weak_ref;
+}
+
+/**
+ * Defines an ordering that is guaranteed to remain consistent even after this
+ * weak pointer has expired.  This may result in two pointers with the same
+ * get_orig() value comparing unequal if one of them is a new object that was
+ * allocated at the same memory address as the older, expired pointer.
+ */
+template<class T>
+template<class Y>
+INLINE bool WeakPointerToBase<T>::
+owner_before(const PointerToBase<Y> &other) const noexcept {
+  // Unfortunately, this may needlessly cause a control block to be allocated,
+  // but I do not see a more efficient solution.
+  return (other._void_ptr != nullptr) &&
+    (_void_ptr == nullptr || _weak_ref < ((const Y *)other._void_ptr)->get_weak_list());
+}
+
 /**
  * A convenient way to set the PointerTo object to NULL. (Assignment to a NULL
  * pointer also works, of course.)
@@ -505,9 +655,17 @@ template<class T>
 INLINE void WeakPointerToBase<T>::
 output(std::ostream &out) const {
   out << _void_ptr;
-  if (was_deleted()) {
-    out << ":deleted";
-  } else if (_void_ptr != nullptr) {
-    out << ":" << ((To *)_void_ptr)->get_ref_count();
+
+  WeakReferenceList *weak_ref = this->_weak_ref;
+  if (weak_ref != nullptr) {
+    weak_ref->_lock.lock();
+    if (!weak_ref->was_deleted()) {
+      out << ":" << ((To *)_void_ptr)->get_ref_count();
+    } else {
+      out << ":deleted";
+    }
+    weak_ref->_lock.unlock();
+  } else {
+    out << ":invalid";
   }
 }

+ 21 - 1
panda/src/express/weakPointerToBase.h

@@ -28,19 +28,31 @@ public:
   typedef T To;
 
 protected:
-  INLINE WeakPointerToBase(To *ptr);
+  constexpr WeakPointerToBase() noexcept = default;
+  INLINE explicit WeakPointerToBase(To *ptr);
   INLINE WeakPointerToBase(const PointerToBase<T> &copy);
   INLINE WeakPointerToBase(const WeakPointerToBase<T> &copy);
   INLINE WeakPointerToBase(WeakPointerToBase<T> &&from) noexcept;
+  template<class Y>
+  INLINE WeakPointerToBase(const WeakPointerToBase<Y> &r);
+  template<class Y>
+  INLINE WeakPointerToBase(WeakPointerToBase<Y> &&r) noexcept;
+
   INLINE ~WeakPointerToBase();
 
   void reassign(To *ptr);
   INLINE void reassign(const PointerToBase<To> &copy);
   INLINE void reassign(const WeakPointerToBase<To> &copy);
   INLINE void reassign(WeakPointerToBase<To> &&from) noexcept;
+  template<class Y>
+  INLINE void reassign(const WeakPointerToBase<Y> &copy);
+  template<class Y>
+  INLINE void reassign(WeakPointerToBase<Y> &&from) noexcept;
 
   INLINE void update_type(To *ptr);
 
+  INLINE void lock_into(PointerToBase<To> &locked) const;
+
   // No assignment or retrieval functions are declared in WeakPointerToBase,
   // because we will have to specialize on const vs.  non-const later.
 
@@ -83,6 +95,14 @@ public:
   INLINE bool operator < (const PointerToBase<To> &other) const;
 #endif  // CPPPARSER
 
+  template<class Y>
+  INLINE bool owner_before(const WeakPointerToBase<Y> &other) const noexcept;
+  template<class Y>
+  INLINE bool owner_before(const PointerToBase<Y> &other) const noexcept;
+
+  // This is needed to be able to access the privates of other instantiations.
+  template<typename Y> friend class WeakPointerToBase;
+
 PUBLISHED:
   INLINE void clear();
   INLINE void refresh() const;

+ 6 - 9
panda/src/express/weakPointerToVoid.I

@@ -11,13 +11,6 @@
  * @date 2004-09-27
  */
 
-/**
- *
- */
-INLINE WeakPointerToVoid::
-WeakPointerToVoid() : _weak_ref(nullptr) {
-}
-
 /**
  * Sets a callback that will be made when the pointer is deleted.  Does
  * nothing if this is a null pointer.
@@ -47,7 +40,11 @@ remove_callback(WeakPointerCallback *callback) const {
 
 /**
  * Returns true if the object we are pointing to has been deleted, false
- * otherwise.
+ * otherwise.  If this returns true, it means that the pointer can not yet be
+ * reused, but it does not guarantee that it can be safely accessed.  See the
+ * lock() method for a safe way to access the underlying pointer.
+ *
+ * This will always return true for a null pointer, unlike is_valid_pointer().
  */
 INLINE bool WeakPointerToVoid::
 was_deleted() const {
@@ -56,7 +53,7 @@ was_deleted() const {
 
 /**
  * Returns true if the pointer is not null and the object has not been
- * deleted.
+ * deleted.  See was_deleted() for caveats.
  */
 INLINE bool WeakPointerToVoid::
 is_valid_pointer() const {

+ 2 - 2
panda/src/express/weakPointerToVoid.h

@@ -25,7 +25,7 @@
  */
 class EXPCL_PANDA_EXPRESS WeakPointerToVoid : public PointerToVoid {
 protected:
-  INLINE WeakPointerToVoid();
+  constexpr WeakPointerToVoid() noexcept = default;
 
 public:
   INLINE void add_callback(WeakPointerCallback *callback) const;
@@ -36,7 +36,7 @@ PUBLISHED:
   INLINE bool is_valid_pointer() const;
 
 protected:
-  mutable WeakReferenceList *_weak_ref;
+  mutable WeakReferenceList *_weak_ref = nullptr;
 };
 
 #include "weakPointerToVoid.I"

+ 9 - 1
panda/src/framework/pandaFramework.cxx

@@ -82,7 +82,7 @@ PandaFramework::
  * control parameters.
  */
 void PandaFramework::
-open_framework(int &argc, char **&argv) {
+open_framework() {
   if (_is_open) {
     return;
   }
@@ -162,6 +162,14 @@ open_framework(int &argc, char **&argv) {
   _event_handler.add_hook("window-event", event_window_event, this);
 }
 
+/**
+ * @deprecated See the version of open_framework() without arguments.
+ */
+void PandaFramework::
+open_framework(int &argc, char **&argv) {
+  open_framework();
+}
+
 /**
  * Should be called at the end of an application to close Panda.  This is
  * optional, as the destructor will do the same thing.

+ 1 - 0
panda/src/framework/pandaFramework.h

@@ -40,6 +40,7 @@ public:
   PandaFramework();
   virtual ~PandaFramework();
 
+  void open_framework();
   void open_framework(int &argc, char **&argv);
   void close_framework();
 

+ 15 - 8
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -648,6 +648,7 @@ issue_parameters(int altered) {
         continue;
 
       case Shader::SPT_int:
+      case Shader::SPT_uint:
         switch (spec._info._class) {
         case Shader::SAC_scalar:
           cgSetParameter1iv(p, (int*)ptr_data->_ptr);
@@ -884,17 +885,21 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) {
 
           _glgsg->enable_vertex_attrib_array(p);
 
-          if (bind._integer) {
-            _glgsg->_glVertexAttribIPointer(p, num_values, type,
-                                            stride, client_pointer);
-          } else if (numeric_type == GeomEnums::NT_packed_dabc) {
+          if (numeric_type == GeomEnums::NT_packed_dabc) {
             // GL_BGRA is a special accepted value available since OpenGL 3.2.
             // It requires us to pass GL_TRUE for normalized.
             _glgsg->_glVertexAttribPointer(p, GL_BGRA, GL_UNSIGNED_BYTE,
                                            GL_TRUE, stride, client_pointer);
-          } else {
+          } else if (bind._numeric_type == Shader::SPT_float ||
+                     numeric_type == GeomEnums::NT_float32) {
             _glgsg->_glVertexAttribPointer(p, num_values, type,
                                            normalized, stride, client_pointer);
+          } else if (bind._numeric_type == Shader::SPT_double) {
+            _glgsg->_glVertexAttribLPointer(p, num_values, type,
+                                            stride, client_pointer);
+          } else {
+            _glgsg->_glVertexAttribIPointer(p, num_values, type,
+                                            stride, client_pointer);
           }
 
           if (divisor > 0) {
@@ -951,10 +956,12 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) {
           // So, we work around this by just binding something silly to 0.
           // This breaks flat colors, but it's better than invisible objects?
           _glgsg->enable_vertex_attrib_array(0);
-          if (bind._integer) {
-            _glgsg->_glVertexAttribIPointer(0, 4, GL_INT, 0, 0);
-          } else {
+          if (bind._numeric_type == Shader::SPT_float) {
             _glgsg->_glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
+          } else if (bind._numeric_type == Shader::SPT_double) {
+            _glgsg->_glVertexAttribLPointer(0, 4, GL_DOUBLE, 0, 0);
+          } else {
+            _glgsg->_glVertexAttribIPointer(0, 4, GL_INT, 0, 0);
           }
 
         } else if (p >= 0) {

+ 20 - 8
panda/src/glstuff/glGeomMunger_src.cxx

@@ -426,11 +426,17 @@ premunge_format_impl(const GeomVertexFormat *orig) {
 int CLP(GeomMunger)::
 compare_to_impl(const GeomMunger *other) const {
   const CLP(GeomMunger) *om = (CLP(GeomMunger) *)other;
-  if (_texture != om->_texture) {
-    return _texture < om->_texture ? -1 : 1;
+  if (_texture.owner_before(om->_texture)) {
+    return -1;
   }
-  if (_tex_gen != om->_tex_gen) {
-    return _tex_gen < om->_tex_gen ? -1 : 1;
+  if (om->_texture.owner_before(_texture)) {
+    return 1;
+  }
+  if (_tex_gen.owner_before(om->_tex_gen)) {
+    return -1;
+  }
+  if (om->_tex_gen.owner_before(_tex_gen)) {
+    return 1;
   }
   if (_flags != om->_flags) {
     return _flags < om->_flags ? -1 : 1;
@@ -447,11 +453,17 @@ compare_to_impl(const GeomMunger *other) const {
 int CLP(GeomMunger)::
 geom_compare_to_impl(const GeomMunger *other) const {
   const CLP(GeomMunger) *om = (CLP(GeomMunger) *)other;
-  if (_texture != om->_texture) {
-    return _texture < om->_texture ? -1 : 1;
+  if (_texture.owner_before(om->_texture)) {
+    return -1;
+  }
+  if (om->_texture.owner_before(_texture)) {
+    return 1;
+  }
+  if (_tex_gen.owner_before(om->_tex_gen)) {
+    return -1;
   }
-  if (_tex_gen != om->_tex_gen) {
-    return _tex_gen < om->_tex_gen ? -1 : 1;
+  if (om->_tex_gen.owner_before(_tex_gen)) {
+    return 1;
   }
   if (_flags != om->_flags) {
     return _flags < om->_flags ? -1 : 1;

+ 87 - 15
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -92,10 +92,6 @@ PStatCollector CLP(GraphicsStateGuardian)::_texture_update_pcollector("Draw:Upda
 PStatCollector CLP(GraphicsStateGuardian)::_fbo_bind_pcollector("Draw:Bind FBO");
 PStatCollector CLP(GraphicsStateGuardian)::_check_error_pcollector("Draw:Check errors");
 
-#ifndef OPENGLES_1
-PT(Shader) CLP(GraphicsStateGuardian)::_default_shader = nullptr;
-#endif
-
 // The following noop functions are assigned to the corresponding glext
 // function pointers in the class, in case the functions are not defined by
 // the GL, just so it will always be safe to call the extension functions.
@@ -188,6 +184,48 @@ static const string default_vshader =
   "  color = p3d_Color * p3d_ColorScale;\n"
   "}\n";
 
+#ifndef OPENGLES
+// This version of the shader is used if vertices-float64 is enabled.
+static const string default_vshader_fp64 =
+#ifdef __APPLE__
+  "#version 150\n"
+#else
+  "#version 130\n"
+#endif
+  "#extension GL_ARB_vertex_attrib_64bit : require\n"
+  "#extension GL_ARB_gpu_shader_fp64 : require\n"
+  "in dvec3 p3d_Vertex;\n"
+  "in vec4 p3d_Color;\n"
+  "in dvec2 p3d_MultiTexCoord0;\n"
+  "out vec2 texcoord;\n"
+  "out vec4 color;\n"
+  "uniform mat4 p3d_ModelViewMatrix;\n"
+  "uniform mat4 p3d_ProjectionMatrix;\n"
+  "uniform vec4 p3d_ColorScale;\n"
+  "void main(void) {\n" // Apply proj & modelview in two steps, more precise
+  "  gl_Position = vec4(dmat4(p3d_ProjectionMatrix) * (dmat4(p3d_ModelViewMatrix) * dvec4(p3d_Vertex, 1)));\n"
+  "  texcoord = vec2(p3d_MultiTexCoord0);\n"
+  "  color = p3d_Color * p3d_ColorScale;\n"
+  "}\n";
+
+// Same as above, but for OpenGL 4.1.
+static const string default_vshader_fp64_gl41 =
+  "#version 410\n"
+  "in dvec3 p3d_Vertex;\n"
+  "in vec4 p3d_Color;\n"
+  "in dvec2 p3d_MultiTexCoord0;\n"
+  "out vec2 texcoord;\n"
+  "out vec4 color;\n"
+  "uniform mat4 p3d_ModelViewMatrix;\n"
+  "uniform mat4 p3d_ProjectionMatrix;\n"
+  "uniform vec4 p3d_ColorScale;\n"
+  "void main(void) {\n" // Apply proj & modelview in two steps, more precise
+  "  gl_Position = vec4(dmat4(p3d_ProjectionMatrix) * (dmat4(p3d_ModelViewMatrix) * dvec4(p3d_Vertex, 1)));\n"
+  "  texcoord = vec2(p3d_MultiTexCoord0);\n"
+  "  color = p3d_Color * p3d_ColorScale;\n"
+  "}\n";
+#endif
+
 static const string default_fshader =
 #ifndef OPENGLES
 #ifdef __APPLE__  // Apple's GL 3.2 contexts require at least GLSL 1.50.
@@ -1699,6 +1737,14 @@ reset() {
        get_extension_func("glUniform3iv");
     _glUniform4iv = (PFNGLUNIFORM4IVPROC)
        get_extension_func("glUniform4iv");
+    _glUniform1uiv = (PFNGLUNIFORM1UIVPROC)
+       get_extension_func("glUniform1uiv");
+    _glUniform2uiv = (PFNGLUNIFORM2UIVPROC)
+       get_extension_func("glUniform2uiv");
+    _glUniform3uiv = (PFNGLUNIFORM3UIVPROC)
+       get_extension_func("glUniform3uiv");
+    _glUniform4uiv = (PFNGLUNIFORM4UIVPROC)
+       get_extension_func("glUniform4uiv");
     _glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)
        get_extension_func("glUniformMatrix3fv");
     _glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)
@@ -1777,6 +1823,10 @@ reset() {
   _glUniform2fv = glUniform2fv;
   _glUniform3fv = glUniform3fv;
   _glUniform4fv = glUniform4fv;
+  _glUniform1iv = glUniform1iv;
+  _glUniform2iv = glUniform2iv;
+  _glUniform3iv = glUniform3iv;
+  _glUniform4iv = glUniform4iv;
   _glUniformMatrix3fv = glUniformMatrix3fv;
   _glUniformMatrix4fv = glUniformMatrix4fv;
   _glValidateProgram = glValidateProgram;
@@ -1827,7 +1877,17 @@ reset() {
   // shader just outputs a red color, indicating that something went wrong.
 #ifndef OPENGLES_1
   if (_default_shader == nullptr && !has_fixed_function_pipeline()) {
-    _default_shader = Shader::make(Shader::SL_GLSL, default_vshader, default_fshader);
+#ifndef OPENGLES
+    bool use_float64 = vertices_float64;
+    if (use_float64 && is_at_least_gl_version(4, 1)) {
+      _default_shader = Shader::make(Shader::SL_GLSL, default_vshader_fp64_gl41, default_fshader);
+    } else if (use_float64 && has_extension("GL_ARB_vertex_attrib_64bit")) {
+      _default_shader = Shader::make(Shader::SL_GLSL, default_vshader_fp64, default_fshader);
+    } else
+#endif
+    {
+      _default_shader = Shader::make(Shader::SL_GLSL, default_vshader, default_fshader);
+    }
   }
 #endif
 
@@ -2226,8 +2286,10 @@ reset() {
   if (is_at_least_gl_version(4, 5) || has_extension("GL_ARB_direct_state_access")) {
     _glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC)
       get_extension_func("glGenerateTextureMipmap");
+
+    _supports_dsa = true;
   } else {
-    _glGenerateTextureMipmap = nullptr;
+    _supports_dsa = false;
   }
 #endif
 
@@ -2770,7 +2832,9 @@ reset() {
   // Check availability of anisotropic texture filtering.
   _supports_anisotropy = false;
   _max_anisotropy = 1.0;
-  if (has_extension("GL_EXT_texture_filter_anisotropic")) {
+  if (is_at_least_gl_version(4, 6) ||
+      has_extension("GL_EXT_texture_filter_anisotropic") ||
+      has_extension("GL_ARB_texture_filter_anisotropic")) {
     GLfloat max_anisotropy;
     glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
     _max_anisotropy = (PN_stdfloat)max_anisotropy;
@@ -3177,7 +3241,7 @@ reset() {
   if (GLCAT.is_debug()) {
     if (_supports_get_program_binary) {
       GLCAT.debug()
-        << "Supported shader binary formats:\n";
+        << "Supported program binary formats:\n";
       GLCAT.debug() << " ";
 
       pset<GLenum>::const_iterator it;
@@ -3189,7 +3253,7 @@ reset() {
       }
       GLCAT.debug(false) << "\n";
     } else {
-      GLCAT.debug() << "No shader binary formats supported.\n";
+      GLCAT.debug() << "No program binary formats supported.\n";
     }
   }
 #endif
@@ -6389,10 +6453,11 @@ prepare_shader_buffer(ShaderBuffer *data) {
 
     if (_use_object_labels) {
       string name = data->get_name();
-      _glObjectLabel(GL_SHADER_STORAGE_BUFFER, gbc->_index, name.size(), name.data());
+      _glObjectLabel(GL_BUFFER, gbc->_index, name.size(), name.data());
     }
 
-    uint64_t num_bytes = data->get_data_size_bytes();
+    // Some drivers require the buffer to be padded to 16 byte boundary.
+    uint64_t num_bytes = (data->get_data_size_bytes() + 15u) & ~15u;
     if (_supports_buffer_storage) {
       _glBufferStorage(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), 0);
     } else {
@@ -7572,7 +7637,6 @@ do_issue_material() {
   } else if (material->has_ambient()) {
     // The material specifies an ambient, but not a diffuse component.  The
     // diffuse component comes from the object's color.
-    call_glMaterialfv(face, GL_AMBIENT, material->get_ambient());
     if (has_material_force_color) {
       glDisable(GL_COLOR_MATERIAL);
       call_glMaterialfv(face, GL_DIFFUSE, _material_force_color);
@@ -7582,11 +7646,11 @@ do_issue_material() {
 #endif  // OPENGLES
       glEnable(GL_COLOR_MATERIAL);
     }
+    call_glMaterialfv(face, GL_AMBIENT, material->get_ambient());
 
   } else if (material->has_diffuse()) {
     // The material specifies a diffuse, but not an ambient component.  The
     // ambient component comes from the object's color.
-    call_glMaterialfv(face, GL_DIFFUSE, material->get_diffuse());
     if (has_material_force_color) {
       glDisable(GL_COLOR_MATERIAL);
       call_glMaterialfv(face, GL_AMBIENT, _material_force_color);
@@ -7596,6 +7660,7 @@ do_issue_material() {
 #endif  // OPENGLES
       glEnable(GL_COLOR_MATERIAL);
     }
+    call_glMaterialfv(face, GL_DIFFUSE, material->get_diffuse());
 
   } else {
     // The material specifies neither a diffuse nor an ambient component.
@@ -13132,7 +13197,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
 void CLP(GraphicsStateGuardian)::
 generate_mipmaps(CLP(TextureContext) *gtc) {
 #ifndef OPENGLES
-  if (_glGenerateTextureMipmap != nullptr) {
+  if (_supports_dsa) {
     // OpenGL 4.5 offers an easy way to do this without binding.
     _glGenerateTextureMipmap(gtc->_index);
     return;
@@ -13442,7 +13507,14 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
 
   GLint internal_format = GL_RGBA;
 #ifndef OPENGLES
-  glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);
+  if (target != GL_TEXTURE_BUFFER) {
+    glGetTexLevelParameteriv(page_target, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);
+  } else {
+    // Some drivers give the wrong result for the above call.  No problem; we
+    // already know the internal format of a buffer texture since glTexBuffer
+    // required passing the exact sized format.
+    internal_format = gtc->_internal_format;
+  }
 #endif  // OPENGLES
 
   // Make sure we were able to query those parameters properly.

+ 10 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -175,6 +175,10 @@ typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, con
 typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value);
 typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value);
 typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value);
+typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value);
+typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value);
+typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value);
+typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value);
 typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
 typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program);
@@ -670,7 +674,7 @@ protected:
   PT(Shader) _texture_binding_shader;
   ShaderContext *_texture_binding_shader_context;
 
-  static PT(Shader) _default_shader;
+  PT(Shader) _default_shader;
 
 #ifndef OPENGLES
   bool _shader_point_size;
@@ -905,6 +909,7 @@ public:
   PFNGLBINDPROGRAMARBPROC _glBindProgram;
 
 #ifndef OPENGLES
+  bool _supports_dsa;
   PFNGLGENERATETEXTUREMIPMAPPROC _glGenerateTextureMipmap;
 #endif
 
@@ -984,6 +989,10 @@ public:
   PFNGLUNIFORM2IVPROC _glUniform2iv;
   PFNGLUNIFORM3IVPROC _glUniform3iv;
   PFNGLUNIFORM4IVPROC _glUniform4iv;
+  PFNGLUNIFORM1UIVPROC _glUniform1uiv;
+  PFNGLUNIFORM2UIVPROC _glUniform2uiv;
+  PFNGLUNIFORM3UIVPROC _glUniform3uiv;
+  PFNGLUNIFORM4UIVPROC _glUniform4uiv;
   PFNGLUNIFORMMATRIX3FVPROC _glUniformMatrix3fv;
   PFNGLUNIFORMMATRIX4FVPROC _glUniformMatrix4fv;
   PFNGLVALIDATEPROGRAMPROC _glValidateProgram;

+ 99 - 22
panda/src/glstuff/glShaderContext_src.cxx

@@ -426,18 +426,34 @@ reflect_attribute(int i, char *name_buffer, GLsizei name_buflen) {
   bind._elements = 1;
 
   // Check if this is an integer input- if so, we have to bind it differently.
-  bind._integer = (param_type == GL_BOOL ||
-                   param_type == GL_BOOL_VEC2 ||
-                   param_type == GL_BOOL_VEC3 ||
-                   param_type == GL_BOOL_VEC4 ||
-                   param_type == GL_INT ||
-                   param_type == GL_INT_VEC2 ||
-                   param_type == GL_INT_VEC3 ||
-                   param_type == GL_INT_VEC4 ||
-                   param_type == GL_UNSIGNED_INT_VEC2 ||
-                   param_type == GL_UNSIGNED_INT_VEC3 ||
-                   param_type == GL_UNSIGNED_INT_VEC4 ||
-                   param_type == GL_UNSIGNED_INT);
+  switch (param_type) {
+  case GL_INT:
+  case GL_INT_VEC2:
+  case GL_INT_VEC3:
+  case GL_INT_VEC4:
+    bind._numeric_type = Shader::SPT_int;
+    break;
+  case GL_BOOL:
+  case GL_BOOL_VEC2:
+  case GL_BOOL_VEC3:
+  case GL_BOOL_VEC4:
+  case GL_UNSIGNED_INT:
+  case GL_UNSIGNED_INT_VEC2:
+  case GL_UNSIGNED_INT_VEC3:
+  case GL_UNSIGNED_INT_VEC4:
+    bind._numeric_type = Shader::SPT_uint;
+    break;
+#ifndef OPENGLES
+  case GL_DOUBLE:
+  case GL_DOUBLE_VEC2:
+  case GL_DOUBLE_VEC3:
+  case GL_DOUBLE_VEC4:
+    bind._numeric_type = Shader::SPT_double;
+    break;
+#endif
+  default:
+    bind._numeric_type = Shader::SPT_float;
+  }
 
   // Check if it has a p3d_ prefix - if so, assign special meaning.
   if (strncmp(name_buffer, "p3d_", 4) == 0) {
@@ -1501,21 +1517,29 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       case GL_INT:
       case GL_INT_VEC2:
       case GL_INT_VEC3:
-      case GL_INT_VEC4: {
+      case GL_INT_VEC4:
+      case GL_UNSIGNED_INT:
+      case GL_UNSIGNED_INT_VEC2:
+      case GL_UNSIGNED_INT_VEC3:
+      case GL_UNSIGNED_INT_VEC4: {
         Shader::ShaderPtrSpec bind;
         bind._id = arg_id;
         switch (param_type) {
           case GL_BOOL:
           case GL_INT:
+          case GL_UNSIGNED_INT:
           case GL_FLOAT:      bind._dim[1] = 1; break;
           case GL_BOOL_VEC2:
           case GL_INT_VEC2:
+          case GL_UNSIGNED_INT_VEC2:
           case GL_FLOAT_VEC2: bind._dim[1] = 2; break;
           case GL_BOOL_VEC3:
           case GL_INT_VEC3:
+          case GL_UNSIGNED_INT_VEC3:
           case GL_FLOAT_VEC3: bind._dim[1] = 3; break;
           case GL_BOOL_VEC4:
           case GL_INT_VEC4:
+          case GL_UNSIGNED_INT_VEC4:
           case GL_FLOAT_VEC4: bind._dim[1] = 4; break;
           case GL_FLOAT_MAT3: bind._dim[1] = 9; break;
           case GL_FLOAT_MAT4: bind._dim[1] = 16; break;
@@ -1525,6 +1549,12 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
         case GL_BOOL_VEC2:
         case GL_BOOL_VEC3:
         case GL_BOOL_VEC4:
+        case GL_UNSIGNED_INT:
+        case GL_UNSIGNED_INT_VEC2:
+        case GL_UNSIGNED_INT_VEC3:
+        case GL_UNSIGNED_INT_VEC4:
+          bind._type = Shader::SPT_uint;
+          break;
         case GL_INT:
         case GL_INT_VEC2:
         case GL_INT_VEC3:
@@ -1604,6 +1634,10 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
     case GL_INT_VEC2:
     case GL_INT_VEC3:
     case GL_INT_VEC4:
+    case GL_UNSIGNED_INT:
+    case GL_UNSIGNED_INT_VEC2:
+    case GL_UNSIGNED_INT_VEC3:
+    case GL_UNSIGNED_INT_VEC4:
     case GL_FLOAT:
     case GL_FLOAT_VEC2:
     case GL_FLOAT_VEC3:
@@ -1633,6 +1667,12 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       case GL_BOOL_VEC2:
       case GL_BOOL_VEC3:
       case GL_BOOL_VEC4:
+      case GL_UNSIGNED_INT:
+      case GL_UNSIGNED_INT_VEC2:
+      case GL_UNSIGNED_INT_VEC3:
+      case GL_UNSIGNED_INT_VEC4:
+        bind._type = Shader::SPT_uint;
+        break;
       case GL_INT:
       case GL_INT_VEC2:
       case GL_INT_VEC3:
@@ -2003,6 +2043,8 @@ issue_parameters(int altered) {
         return;
       }
 
+      nassertd(spec._dim[1] > 0) continue;
+
       GLint p = spec._id._seqno;
       int array_size = min(spec._dim[0], (int)ptr_data->_size / spec._dim[1]);
       switch (spec._type) {
@@ -2019,6 +2061,14 @@ issue_parameters(int altered) {
             }
             break;
 
+          case Shader::SPT_uint:
+            // Convert unsigned int data to float data.
+            data = (float*) alloca(sizeof(float) * array_size * spec._dim[1]);
+            for (int i = 0; i < (array_size * spec._dim[1]); ++i) {
+              data[i] = (float)(((unsigned int*)ptr_data->_ptr)[i]);
+            }
+            break;
+
           case Shader::SPT_double:
             // Downgrade double data to float data.
             data = (float*) alloca(sizeof(float) * array_size * spec._dim[1]);
@@ -2048,7 +2098,8 @@ issue_parameters(int altered) {
         break;
 
       case Shader::SPT_int:
-        if (ptr_data->_type != Shader::SPT_int) {
+        if (ptr_data->_type != Shader::SPT_int &&
+            ptr_data->_type != Shader::SPT_uint) {
           GLCAT.error()
             << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
 
@@ -2068,6 +2119,28 @@ issue_parameters(int altered) {
         }
         break;
 
+      case Shader::SPT_uint:
+        if (ptr_data->_type != Shader::SPT_uint &&
+            ptr_data->_type != Shader::SPT_int) {
+          GLCAT.error()
+            << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
+
+          // Deactivate it to make sure the user doesn't get flooded with this
+          // error.
+          spec._dep[0] = 0;
+          spec._dep[1] = 0;
+
+        } else {
+          switch (spec._dim[1]) {
+          case 1: _glgsg->_glUniform1uiv(p, array_size, (GLuint *)ptr_data->_ptr); continue;
+          case 2: _glgsg->_glUniform2uiv(p, array_size, (GLuint *)ptr_data->_ptr); continue;
+          case 3: _glgsg->_glUniform3uiv(p, array_size, (GLuint *)ptr_data->_ptr); continue;
+          case 4: _glgsg->_glUniform4uiv(p, array_size, (GLuint *)ptr_data->_ptr); continue;
+          }
+          nassertd(false) continue;
+        }
+        break;
+
       case Shader::SPT_double:
         GLCAT.error() << "Passing double-precision shader inputs to GLSL shaders is not currently supported\n";
 
@@ -2207,7 +2280,7 @@ disable_shader_vertex_arrays() {
     return;
   }
 
-  for (int i=0; i<(int)_shader->_var_spec.size(); i++) {
+  for (size_t i = 0; i < _shader->_var_spec.size(); ++i) {
     const Shader::ShaderVarSpec &bind = _shader->_var_spec[i];
     GLint p = bind._id._seqno;
 
@@ -2332,21 +2405,25 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) {
         }
         client_pointer += start;
 
+        GLenum type = _glgsg->get_numeric_type(numeric_type);
         for (int i = 0; i < num_elements; ++i) {
           _glgsg->enable_vertex_attrib_array(p);
 
-          if (bind._integer) {
-            _glgsg->_glVertexAttribIPointer(p, num_values, _glgsg->get_numeric_type(numeric_type),
-                                            stride, client_pointer);
-          } else if (numeric_type == GeomEnums::NT_packed_dabc) {
+          if (numeric_type == GeomEnums::NT_packed_dabc) {
             // GL_BGRA is a special accepted value available since OpenGL 3.2.
             // It requires us to pass GL_TRUE for normalized.
             _glgsg->_glVertexAttribPointer(p, GL_BGRA, GL_UNSIGNED_BYTE,
                                            GL_TRUE, stride, client_pointer);
-          } else {
-            _glgsg->_glVertexAttribPointer(p, num_values,
-                                           _glgsg->get_numeric_type(numeric_type),
+          } else if (bind._numeric_type == Shader::SPT_float ||
+                     numeric_type == GeomEnums::NT_float32) {
+            _glgsg->_glVertexAttribPointer(p, num_values, type,
                                            normalized, stride, client_pointer);
+          } else if (bind._numeric_type == Shader::SPT_double) {
+            _glgsg->_glVertexAttribLPointer(p, num_values, type,
+                                            stride, client_pointer);
+          } else {
+            _glgsg->_glVertexAttribIPointer(p, num_values, type,
+                                            stride, client_pointer);
           }
 
           if (divisor > 0) {

File diff suppressed because it is too large
+ 631 - 31
panda/src/glstuff/panda_glext.h


+ 0 - 12
panda/src/gobj/config_gobj.cxx

@@ -260,18 +260,6 @@ ConfigVariableBool cache_generated_shaders
  PRC_DESC("Set this true to cause all generated shaders to be cached in "
           "memory.  This is useful to prevent unnecessary recompilation."));
 
-ConfigVariableBool enforce_attrib_lock
-("enforce-attrib-lock", true,
- PRC_DESC("When a MaterialAttrib, TextureAttrib, or LightAttrib is "
-          "constructed, the corresponding Material, Texture, or Light "
-          "is 'attrib locked.'  The attrib lock prevents qualitative "
-          "changes to the object.  This makes it possible to hardwire "
-          "information about material, light, and texture properties "
-          "into generated shaders.  This config variable can disable "
-          "the attrib lock.  Disabling the lock will break the shader "
-          "generator, but doing so may be necessary for backward "
-          "compatibility with old code."));
-
 ConfigVariableBool vertices_float64
 ("vertices-float64", false,
  PRC_DESC("When this is true, the default float format for vertices "

+ 0 - 1
panda/src/gobj/config_gobj.h

@@ -51,7 +51,6 @@ extern EXPCL_PANDA_GOBJ ConfigVariableBool connect_triangle_strips;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool preserve_triangle_strips;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool dump_generated_shaders;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool cache_generated_shaders;
-extern EXPCL_PANDA_GOBJ ConfigVariableBool enforce_attrib_lock;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool vertices_float64;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt vertex_column_alignment;
 extern EXPCL_PANDA_GOBJ ConfigVariableBool vertex_animation_align_16;

+ 49 - 12
panda/src/gobj/geom.cxx

@@ -195,6 +195,9 @@ offset_vertices(const GeomVertexData *data, int offset) {
   cdata->_data = (GeomVertexData *)data;
 
 #ifndef NDEBUG
+  GeomVertexDataPipelineReader data_reader(data, current_thread);
+  data_reader.check_array_readers();
+
   bool all_is_valid = true;
 #endif
   Primitives::iterator pi;
@@ -203,7 +206,7 @@ offset_vertices(const GeomVertexData *data, int offset) {
     prim->offset_vertices(offset);
 
 #ifndef NDEBUG
-    if (!prim->check_valid(data)) {
+    if (!prim->check_valid(&data_reader)) {
       gobj_cat.warning()
         << *prim << " is invalid for " << *data << ":\n";
       prim->write(gobj_cat.warning(false), 4);
@@ -423,6 +426,9 @@ decompose_in_place() {
   CDWriter cdata(_cycler, true, current_thread);
 
 #ifndef NDEBUG
+  GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
+  data_reader.check_array_readers();
+
   bool all_is_valid = true;
 #endif
   Primitives::iterator pi;
@@ -431,7 +437,7 @@ decompose_in_place() {
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 #ifndef NDEBUG
-    if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) {
+    if (!new_prim->check_valid(&data_reader)) {
       all_is_valid = false;
     }
 #endif
@@ -457,6 +463,9 @@ doubleside_in_place() {
   CDWriter cdata(_cycler, true, current_thread);
 
 #ifndef NDEBUG
+  GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
+  data_reader.check_array_readers();
+
   bool all_is_valid = true;
 #endif
   Primitives::iterator pi;
@@ -465,7 +474,7 @@ doubleside_in_place() {
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 #ifndef NDEBUG
-    if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) {
+    if (!new_prim->check_valid(&data_reader)) {
       all_is_valid = false;
     }
 #endif
@@ -491,6 +500,9 @@ reverse_in_place() {
   CDWriter cdata(_cycler, true, current_thread);
 
 #ifndef NDEBUG
+  GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
+  data_reader.check_array_readers();
+
   bool all_is_valid = true;
 #endif
   Primitives::iterator pi;
@@ -499,7 +511,7 @@ reverse_in_place() {
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 #ifndef NDEBUG
-    if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) {
+    if (!new_prim->check_valid(&data_reader)) {
       all_is_valid = false;
     }
 #endif
@@ -525,6 +537,9 @@ rotate_in_place() {
   CDWriter cdata(_cycler, true, current_thread);
 
 #ifndef NDEBUG
+  GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
+  data_reader.check_array_readers();
+
   bool all_is_valid = true;
 #endif
   Primitives::iterator pi;
@@ -533,7 +548,7 @@ rotate_in_place() {
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 #ifndef NDEBUG
-    if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) {
+    if (!new_prim->check_valid(&data_reader)) {
       all_is_valid = false;
     }
 #endif
@@ -640,6 +655,9 @@ unify_in_place(int max_indices, bool preserve_order) {
     // primitives.)
     nassertv(false);
   }
+
+  GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
+  data_reader.check_array_readers();
 #endif
 
   // Finally, iterate through the remaining primitives, and copy them to the
@@ -649,7 +667,7 @@ unify_in_place(int max_indices, bool preserve_order) {
   for (npi = new_prims.begin(); npi != new_prims.end(); ++npi) {
     GeomPrimitive *prim = (*npi).second;
 
-    nassertv(prim->check_valid(cdata->_data.get_read_pointer(current_thread)));
+    nassertv(prim->check_valid(&data_reader));
 
     // Each new primitive, naturally, inherits the Geom's overall shade model.
     prim->set_shade_model(cdata->_shade_model);
@@ -748,6 +766,9 @@ make_lines_in_place() {
   CDWriter cdata(_cycler, true, current_thread);
 
 #ifndef NDEBUG
+  GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
+  data_reader.check_array_readers();
+
   bool all_is_valid = true;
 #endif
   Primitives::iterator pi;
@@ -756,7 +777,7 @@ make_lines_in_place() {
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 #ifndef NDEBUG
-    if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) {
+    if (!new_prim->check_valid(&data_reader)) {
       all_is_valid = false;
     }
 #endif
@@ -782,6 +803,9 @@ make_points_in_place() {
   CDWriter cdata(_cycler, true, current_thread);
 
 #ifndef NDEBUG
+  GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
+  data_reader.check_array_readers();
+
   bool all_is_valid = true;
 #endif
   Primitives::iterator pi;
@@ -790,7 +814,7 @@ make_points_in_place() {
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 #ifndef NDEBUG
-    if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) {
+    if (!new_prim->check_valid(&data_reader)) {
       all_is_valid = false;
     }
 #endif
@@ -816,6 +840,9 @@ make_patches_in_place() {
   CDWriter cdata(_cycler, true, current_thread);
 
 #ifndef NDEBUG
+  GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
+  data_reader.check_array_readers();
+
   bool all_is_valid = true;
 #endif
   Primitives::iterator pi;
@@ -824,7 +851,7 @@ make_patches_in_place() {
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 #ifndef NDEBUG
-    if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) {
+    if (!new_prim->check_valid(&data_reader)) {
       all_is_valid = false;
     }
 #endif
@@ -850,6 +877,9 @@ make_adjacency_in_place() {
   CDWriter cdata(_cycler, true, current_thread);
 
 #ifndef NDEBUG
+  GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
+  data_reader.check_array_readers();
+
   bool all_is_valid = true;
 #endif
   Primitives::iterator pi;
@@ -859,7 +889,7 @@ make_adjacency_in_place() {
       (*pi) = (GeomPrimitive *)new_prim.p();
 
 #ifndef NDEBUG
-      if (!new_prim->check_valid(cdata->_data.get_read_pointer(current_thread))) {
+      if (!new_prim->check_valid(&data_reader)) {
         all_is_valid = false;
       }
 #endif
@@ -1465,13 +1495,20 @@ clear_prepared(PreparedGraphicsObjects *prepared_objects) {
  */
 bool Geom::
 check_will_be_valid(const GeomVertexData *vertex_data) const {
-  CDReader cdata(_cycler);
+  Thread *current_thread = Thread::get_current_thread();
+
+  CDReader cdata(_cycler, current_thread);
+
+  GeomVertexDataPipelineReader data_reader(vertex_data, current_thread);
+  data_reader.check_array_readers();
 
   Primitives::const_iterator pi;
   for (pi = cdata->_primitives.begin();
        pi != cdata->_primitives.end();
        ++pi) {
-    if (!(*pi).get_read_pointer()->check_valid(vertex_data)) {
+    GeomPrimitivePipelineReader reader((*pi).get_read_pointer(), current_thread);
+    reader.check_minmax();
+    if (!reader.check_valid(&data_reader)) {
       return false;
     }
   }

+ 11 - 3
panda/src/gobj/geomPrimitive.I

@@ -223,11 +223,19 @@ get_modified() const {
 INLINE bool GeomPrimitive::
 check_valid(const GeomVertexData *vertex_data) const {
   Thread *current_thread = Thread::get_current_thread();
-  GeomPrimitivePipelineReader reader(this, current_thread);
-  reader.check_minmax();
   GeomVertexDataPipelineReader data_reader(vertex_data, current_thread);
   data_reader.check_array_readers();
-  return reader.check_valid(&data_reader);
+  return check_valid(&data_reader);
+}
+
+/**
+ *
+ */
+INLINE bool GeomPrimitive::
+check_valid(const GeomVertexDataPipelineReader *data_reader) const {
+  GeomPrimitivePipelineReader reader(this, data_reader->get_current_thread());
+  reader.check_minmax();
+  return reader.check_valid(data_reader);
 }
 
 /**

+ 1 - 0
panda/src/gobj/geomPrimitive.h

@@ -146,6 +146,7 @@ PUBLISHED:
   bool request_resident(Thread *current_thread = Thread::get_current_thread()) const;
 
   INLINE bool check_valid(const GeomVertexData *vertex_data) const;
+  INLINE bool check_valid(const GeomVertexDataPipelineReader *data_reader) const;
 
   virtual void output(std::ostream &out) const;
   virtual void write(std::ostream &out, int indent_level) const;

+ 16 - 10
panda/src/gobj/lens.I

@@ -667,9 +667,11 @@ do_get_film_offset(const CData *cdata) const {
  */
 INLINE void Lens::
 do_set_near(CData *cdata, PN_stdfloat near_distance) {
-  cdata->_near_distance = near_distance;
-  do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv, 0);
-  do_throw_change_event(cdata);
+  if (near_distance != cdata->_near_distance) {
+    cdata->_near_distance = near_distance;
+    do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv, 0);
+    do_throw_change_event(cdata);
+  }
 }
 
 /**
@@ -685,9 +687,11 @@ do_get_near(const CData *cdata) const {
  */
 INLINE void Lens::
 do_set_far(CData *cdata, PN_stdfloat far_distance) {
-  cdata->_far_distance = far_distance;
-  do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv, 0);
-  do_throw_change_event(cdata);
+  if (far_distance != cdata->_far_distance) {
+    cdata->_far_distance = far_distance;
+    do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv, 0);
+    do_throw_change_event(cdata);
+  }
 }
 
 /**
@@ -703,10 +707,12 @@ do_get_far(const CData *cdata) const {
  */
 INLINE void Lens::
 do_set_near_far(CData *cdata, PN_stdfloat near_distance, PN_stdfloat far_distance) {
-  cdata->_near_distance = near_distance;
-  cdata->_far_distance = far_distance;
-  do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv, 0);
-  do_throw_change_event(cdata);
+  if (near_distance != cdata->_near_distance || far_distance != cdata->_far_distance) {
+    cdata->_near_distance = near_distance;
+    cdata->_far_distance = far_distance;
+    do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv, 0);
+    do_throw_change_event(cdata);
+  }
 }
 
 INLINE std::ostream &

+ 43 - 15
panda/src/gobj/material.I

@@ -32,8 +32,18 @@ Material(const std::string &name) : Namable(name) {
  *
  */
 INLINE Material::
-Material(const Material &copy) : Namable(copy) {
-  operator = (copy);
+Material(const Material &copy) :
+  Namable(copy) ,
+  _base_color(copy._base_color),
+  _ambient(copy._ambient),
+  _diffuse(copy._diffuse),
+  _specular(copy._specular),
+  _emission(copy._emission),
+  _shininess(copy._shininess),
+  _roughness(copy._roughness),
+  _metallic(copy._metallic),
+  _refractive_index(copy._refractive_index),
+  _flags(copy._flags & ~(F_attrib_lock | F_used_by_auto_shader)) {
 }
 
 /**
@@ -99,8 +109,8 @@ get_ambient() const {
  */
 INLINE void Material::
 clear_ambient() {
-  if (enforce_attrib_lock) {
-    nassertv(!is_attrib_locked());
+  if (has_ambient() && is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   _flags &= ~F_ambient;
   _ambient = _base_color;
@@ -129,8 +139,8 @@ get_diffuse() const {
  */
 INLINE void Material::
 clear_diffuse() {
-  if (enforce_attrib_lock) {
-    nassertv(!is_attrib_locked());
+  if (has_diffuse() && is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   _flags &= ~F_diffuse;
   _diffuse = _base_color * (1 - _metallic);
@@ -177,8 +187,8 @@ get_emission() const {
  */
 INLINE void Material::
 clear_emission() {
-  if (enforce_attrib_lock) {
-    nassertv(!is_attrib_locked());
+  if (has_emission() && is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   _flags &= ~F_emission;
   _emission.set(0.0f, 0.0f, 0.0f, 0.0f);
@@ -253,8 +263,8 @@ get_local() const {
  */
 INLINE void Material::
 set_local(bool local) {
-  if (enforce_attrib_lock) {
-    nassertv(!is_attrib_locked());
+  if (is_used_by_auto_shader() && get_local() != local) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   if (local) {
     _flags |= F_local;
@@ -278,8 +288,8 @@ get_twoside() const {
  */
 INLINE void Material::
 set_twoside(bool twoside) {
-  if (enforce_attrib_lock) {
-    nassertv(!is_attrib_locked());
+  if (is_used_by_auto_shader() && get_twoside() != twoside) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   if (twoside) {
     _flags |= F_twoside;
@@ -313,7 +323,7 @@ operator < (const Material &other) const {
 }
 
 /**
- *
+ * @deprecated This no longer has any meaning in 1.10.
  */
 INLINE bool Material::
 is_attrib_locked() const {
@@ -321,17 +331,35 @@ is_attrib_locked() const {
 }
 
 /**
- *
+ * @deprecated This no longer has any meaning in 1.10.
  */
 INLINE void Material::
 set_attrib_lock() {
   _flags |= F_attrib_lock;
 }
 
+/**
+ * Internal.  Returns true if a shader has been generated that uses this.
+ */
+INLINE bool Material::
+is_used_by_auto_shader() const {
+  return (_flags & F_attrib_lock) != 0;
+}
+
+/**
+ * Called by the shader generator to indicate that a shader has been generated
+ * that uses this material.
+ */
+INLINE void Material::
+mark_used_by_auto_shader() {
+  _flags |= F_used_by_auto_shader;
+}
+
 /**
  *
  */
 INLINE int Material::
 get_flags() const {
-  return _flags;
+  // F_used_by_auto_shader is an internal flag, ignore it.
+  return _flags & ~F_used_by_auto_shader;
 }

+ 25 - 34
panda/src/gobj/material.cxx

@@ -28,6 +28,11 @@ PT(Material) Material::_default;
 void Material::
 operator = (const Material &copy) {
   Namable::operator = (copy);
+
+  if (is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
+  }
+
   _base_color = copy._base_color;
   _ambient = copy._ambient;
   _diffuse = copy._diffuse;
@@ -37,7 +42,7 @@ operator = (const Material &copy) {
   _roughness = copy._roughness;
   _metallic = copy._metallic;
   _refractive_index = copy._refractive_index;
-  _flags = copy._flags & (~F_attrib_lock);
+  _flags = (copy._flags & ~(F_attrib_lock | F_used_by_auto_shader)) | (_flags & (F_attrib_lock | F_used_by_auto_shader));
 }
 
 /**
@@ -53,10 +58,8 @@ operator = (const Material &copy) {
  */
 void Material::
 set_base_color(const LColor &color) {
-  if (enforce_attrib_lock) {
-    if ((_flags & F_base_color) == 0) {
-      nassertv(!is_attrib_locked());
-    }
+  if (!has_base_color() && is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   _base_color = color;
   _flags |= F_base_color | F_metallic;
@@ -81,8 +84,8 @@ set_base_color(const LColor &color) {
  */
 void Material::
 clear_base_color() {
-  if (enforce_attrib_lock) {
-    nassertv(!is_attrib_locked());
+  if (has_base_color() && is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   _flags &= ~F_base_color;
   _base_color.set(0.0f, 0.0f, 0.0f, 0.0f);
@@ -116,10 +119,8 @@ clear_base_color() {
  */
 void Material::
 set_ambient(const LColor &color) {
-  if (enforce_attrib_lock) {
-    if ((_flags & F_ambient)==0) {
-      nassertv(!is_attrib_locked());
-    }
+  if (!has_ambient() && is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   _ambient = color;
   _flags |= F_ambient;
@@ -137,10 +138,8 @@ set_ambient(const LColor &color) {
  */
 void Material::
 set_diffuse(const LColor &color) {
-  if (enforce_attrib_lock) {
-    if ((_flags & F_diffuse)==0) {
-      nassertv(!is_attrib_locked());
-    }
+  if (!has_diffuse() && is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   _diffuse = color;
   _flags |= F_diffuse;
@@ -160,10 +159,8 @@ set_diffuse(const LColor &color) {
  */
 void Material::
 set_specular(const LColor &color) {
-  if (enforce_attrib_lock) {
-    if ((_flags & F_specular)==0) {
-      nassertv(!is_attrib_locked());
-    }
+  if (!has_specular() && is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   _specular = color;
   _flags |= F_specular;
@@ -174,8 +171,8 @@ set_specular(const LColor &color) {
  */
 void Material::
 clear_specular() {
-  if (enforce_attrib_lock) {
-    nassertv(!is_attrib_locked());
+  if (has_specular() && is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   _flags &= ~F_specular;
 
@@ -201,10 +198,8 @@ clear_specular() {
  */
 void Material::
 set_emission(const LColor &color) {
-  if (enforce_attrib_lock) {
-    if ((_flags & F_emission)==0) {
-      nassertv(!is_attrib_locked());
-    }
+  if (!has_emission() && is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
   }
   _emission = color;
   _flags |= F_emission;
@@ -275,11 +270,6 @@ set_roughness(PN_stdfloat roughness) {
  */
 void Material::
 set_metallic(PN_stdfloat metallic) {
-  if (enforce_attrib_lock) {
-    if ((_flags & F_metallic) == 0) {
-      nassertv(!is_attrib_locked());
-    }
-  }
   _metallic = metallic;
   _flags |= F_metallic;
 
@@ -305,9 +295,6 @@ set_metallic(PN_stdfloat metallic) {
  */
 void Material::
 clear_metallic() {
-  if (enforce_attrib_lock) {
-    nassertv(!is_attrib_locked());
-  }
   _flags &= ~F_metallic;
   _metallic = 0;
 
@@ -482,7 +469,7 @@ write_datagram(BamWriter *manager, Datagram &me) {
   me.add_string(get_name());
 
   if (manager->get_file_minor_ver() >= 39) {
-    me.add_int32(_flags);
+    me.add_int32(_flags & ~F_used_by_auto_shader);
 
     if (_flags & F_metallic) {
       // Metalness workflow.
@@ -570,4 +557,8 @@ fillin(DatagramIterator &scan, BamReader *manager) {
       set_roughness(_shininess);
     }
   }
+
+  if (is_used_by_auto_shader()) {
+    GraphicsStateGuardianBase::mark_rehash_generated_shaders();
+  }
 }

+ 6 - 0
panda/src/gobj/material.h

@@ -21,6 +21,7 @@
 #include "luse.h"
 #include "numeric_types.h"
 #include "config_gobj.h"
+#include "graphicsStateGuardianBase.h"
 
 class FactoryParams;
 
@@ -127,7 +128,11 @@ PUBLISHED:
   MAKE_PROPERTY(local, get_local, set_local);
   MAKE_PROPERTY(twoside, get_twoside, set_twoside);
 
+protected:
+  INLINE bool is_used_by_auto_shader() const;
+
 public:
+  INLINE void mark_used_by_auto_shader();
   INLINE int get_flags() const;
 
   enum Flags {
@@ -142,6 +147,7 @@ public:
     F_metallic    = 0x100,
     F_base_color  = 0x200,
     F_refractive_index = 0x400,
+    F_used_by_auto_shader = 0x800,
   };
 
 private:

+ 5 - 0
panda/src/gobj/perspectiveLens.cxx

@@ -71,9 +71,14 @@ do_compute_projection_mat(Lens::CData *lens_cdata) {
   PN_stdfloat fNear = do_get_near(lens_cdata);
   PN_stdfloat a, b;
 
+  // Take the limits if either near or far is infinite.
   if (cinf(fFar)) {
     a = 1;
     b = -2 * fNear;
+  } else if (cinf(fNear)) {
+    // This is valid if the near/far planes are inverted.
+    a = -1;
+    b = 2 * fFar;
   } else {
     PN_stdfloat far_minus_near = fFar-fNear;
     a = (fFar + fNear);

+ 22 - 5
panda/src/gobj/shader.cxx

@@ -619,12 +619,29 @@ cg_recurse_parameters(CGparameter parameter, const ShaderType &type,
             p._type       = arg_type;
             p._direction  = arg_dir;
             p._varying    = (vbl == CG_VARYING);
-            p._integer    = (base_type == CG_UINT || base_type == CG_INT ||
-                             base_type == CG_ULONG || base_type == CG_LONG ||
-                             base_type == CG_USHORT || base_type == CG_SHORT ||
-                             base_type == CG_UCHAR || base_type == CG_CHAR);
             p._cat        = shader_cat.get_safe_ptr();
 
+            //NB. Cg does have a CG_DOUBLE type, but at least for the ARB
+            // profiles and GLSL profiles it just maps to float.
+            switch (base_type) {
+            case CG_UINT:
+            case CG_ULONG:
+            case CG_USHORT:
+            case CG_UCHAR:
+            case CG_BOOL:
+              p._numeric_type = SPT_uint;
+              break;
+            case CG_INT:
+            case CG_LONG:
+            case CG_SHORT:
+            case CG_CHAR:
+              p._numeric_type = SPT_int;
+              break;
+            default:
+              p._numeric_type = SPT_float;
+              break;
+            }
+
             success &= compile_parameter(p, arg_dim);
             break;
           }
@@ -681,7 +698,7 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) {
     ShaderVarSpec bind;
     bind._id = p._id;
     bind._append_uv = -1;
-    bind._integer = p._integer;
+    bind._numeric_type = p._numeric_type;
 
     if (pieces.size() == 2) {
       if (pieces[1] == "position") {

+ 10 - 9
panda/src/gobj/shader.h

@@ -331,6 +331,14 @@ public:
     int        _seqno;
   };
 
+  enum ShaderPtrType {
+    SPT_float,
+    SPT_double,
+    SPT_int,
+    SPT_uint,
+    SPT_unknown
+  };
+
   struct ShaderArgInfo {
     ShaderArgId       _id;
     ShaderArgClass    _class;
@@ -338,17 +346,10 @@ public:
     ShaderArgType     _type;
     ShaderArgDir      _direction;
     bool              _varying;
-    bool              _integer;
+    ShaderPtrType     _numeric_type;
     NotifyCategory   *_cat;
   };
 
-  enum ShaderPtrType {
-    SPT_float,
-    SPT_double,
-    SPT_int,
-    SPT_unknown
-  };
-
   // Container structure for data of parameters ShaderPtrSpec.
   struct ShaderPtrData {
   private:
@@ -424,7 +425,7 @@ public:
     PT(InternalName)  _name;
     int               _append_uv;
     int               _elements;
-    bool              _integer;
+    ShaderPtrType     _numeric_type;
   };
 
   struct ShaderPtrSpec {

+ 8 - 4
panda/src/gobj/shaderBuffer.I

@@ -19,8 +19,7 @@ INLINE ShaderBuffer::
 ShaderBuffer(const std::string &name, uint64_t size, UsageHint usage_hint) :
   Namable(name),
   _data_size_bytes(size),
-  _usage_hint(usage_hint),
-  _contexts(nullptr) {
+  _usage_hint(usage_hint) {
 }
 
 /**
@@ -32,8 +31,13 @@ ShaderBuffer(const std::string &name, vector_uchar initial_data, UsageHint usage
   Namable(name),
   _data_size_bytes(initial_data.size()),
   _usage_hint(usage_hint),
-  _initial_data(initial_data),
-  _contexts(nullptr) {
+  _initial_data(std::move(initial_data)) {
+
+  // Make sure it is padded to 16 bytes.  Some drivers like that.
+  if ((_initial_data.size() & 15u) != 0) {
+    _initial_data.resize((_initial_data.size() + 15u) & ~15u, 0);
+    _data_size_bytes = _initial_data.size();
+  }
 }
 
 /**

+ 1 - 1
panda/src/gobj/shaderBuffer.cxx

@@ -193,7 +193,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 
   if (scan.get_bool() && _data_size_bytes > 0) {
     nassertv_always(_data_size_bytes <= scan.get_remaining_size());
-    _initial_data.resize(_data_size_bytes);
+    _initial_data.resize((_data_size_bytes + 15u) & ~15u);
     scan.extract_bytes(&_initial_data[0], _data_size_bytes);
   } else {
     _initial_data.clear();

+ 1 - 1
panda/src/gobj/shaderBuffer.h

@@ -63,7 +63,7 @@ private:
   vector_uchar _initial_data;
 
   typedef pmap<PreparedGraphicsObjects *, BufferContext *> Contexts;
-  Contexts *_contexts;
+  Contexts *_contexts = nullptr;
 
 public:
   static void register_with_read_factory();

+ 0 - 25
panda/src/gobj/test_gobj.cxx

@@ -1,25 +0,0 @@
-/**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University.  All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license.  You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file test_gobj.cxx
- * @author shochet
- * @date 2000-02-02
- */
-
-#include "geom.h"
-#include "perspectiveProjection.h"
-
-int main() {
-  nout << "running test_gobj" << std::endl;
-  PT(GeomTri) triangle = new GeomTri;
-  Frustumf frust;
-  PT(PerspectiveProjection) proj = new PerspectiveProjection(frust);
-  LMatrix4f mat = proj->get_projection_mat();
-  nout << "default proj matrix: " << mat;
-  return 0;
-}

+ 18 - 0
panda/src/gobj/texture.I

@@ -2342,6 +2342,24 @@ get_unsigned_int(const unsigned char *&p) {
   return (double)v.ui / 4294967295.0;
 }
 
+/**
+ * This is used by store() to retrieve the next consecutive component value
+ * from the indicated element of the array, which is taken to be an array of
+ * unsigned ints with the value packed in the 24 least significant bits.
+ */
+INLINE double Texture::
+get_unsigned_int_24(const unsigned char *&p) {
+  union {
+    uint32_t ui;
+    uint8_t uc[4];
+  } v;
+  v.uc[0] = (*p++);
+  v.uc[1] = (*p++);
+  v.uc[2] = (*p++);
+  v.uc[3] = (*p++);
+  return (double)(v.ui & 0xffffff) / (double)0xffffff;
+}
+
 /**
  * This is used by store() to retrieve the next consecutive component value
  * from the indicated element of the array, which is taken to be an array of

+ 1 - 0
panda/src/gobj/texture.h

@@ -858,6 +858,7 @@ private:
   INLINE static double get_unsigned_byte(const unsigned char *&p);
   INLINE static double get_unsigned_short(const unsigned char *&p);
   INLINE static double get_unsigned_int(const unsigned char *&p);
+  INLINE static double get_unsigned_int_24(const unsigned char *&p);
   INLINE static double get_float(const unsigned char *&p);
   INLINE static double get_half_float(const unsigned char *&p);
 

+ 4 - 0
panda/src/gobj/texturePeeker.cxx

@@ -94,6 +94,10 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) {
     _get_component = Texture::get_half_float;
     break;
 
+  case Texture::T_unsigned_int_24_8:
+    _get_component = Texture::get_unsigned_int_24;
+    break;
+
   default:
     // Not supported.
     _image.clear();

+ 20 - 0
panda/src/gobj/texturePool.I

@@ -275,3 +275,23 @@ PT(Texture) TexturePool::
 make_texture(const std::string &extension) {
   return get_global_ptr()->ns_make_texture(extension);
 }
+
+/**
+ * Defines relative ordering between LookupKey instances.
+ */
+INLINE bool TexturePool::LookupKey::
+operator < (const LookupKey &other) const {
+  if (_fullpath != other._fullpath) {
+    return _fullpath < other._fullpath;
+  }
+  if (_alpha_fullpath != other._alpha_fullpath) {
+    return _alpha_fullpath < other._alpha_fullpath;
+  }
+  if (_primary_file_num_channels != other._primary_file_num_channels) {
+    return _primary_file_num_channels < other._primary_file_num_channels;
+  }
+  if (_alpha_file_channel != other._alpha_file_channel) {
+    return _alpha_file_channel < other._alpha_file_channel;
+  }
+  return _texture_type < other._texture_type;
+}

Some files were not shown because too many files changed in this diff