Browse Source

Merge branch 'master' into webgl-port

rdb 9 years ago
parent
commit
529f2fa7cb
62 changed files with 800 additions and 243 deletions
  1. 1 1
      direct/src/actor/Actor.py
  2. 3 1
      direct/src/directbase/DirectStart.py
  3. 1 1
      direct/src/directdevices/DirectRadamec.py
  4. 1 1
      direct/src/directnotify/Notifier.py
  5. 16 14
      direct/src/directscripts/gendocs.py
  6. 1 1
      direct/src/distributed/ConnectionRepository.py
  7. 1 1
      direct/src/distributed/ServerRepository.py
  8. 4 2
      direct/src/extensions_native/extension_native_helpers.py
  9. 4 4
      direct/src/fsm/FSM.py
  10. 6 6
      direct/src/fsm/State.py
  11. 9 10
      direct/src/gui/DirectGuiBase.py
  12. 1 1
      direct/src/gui/DirectScrollBar.py
  13. 1 1
      direct/src/gui/DirectSlider.py
  14. 3 3
      direct/src/interval/FunctionInterval.py
  15. 1 1
      direct/src/interval/ProjectileInterval.py
  16. 9 9
      direct/src/p3d/AppRunner.py
  17. 11 5
      direct/src/p3d/JavaScript.py
  18. 1 1
      direct/src/p3d/PackageInstaller.py
  19. 4 4
      direct/src/p3d/PackageMerger.py
  20. 26 26
      direct/src/p3d/Packager.py
  21. 2 2
      direct/src/p3d/PatchMaker.py
  22. 1 1
      direct/src/p3d/SeqValue.py
  23. 6 6
      direct/src/p3d/packp3d.py
  24. 1 1
      direct/src/p3d/runp3d.py
  25. 1 1
      direct/src/particles/ParticleEffect.py
  26. 2 2
      direct/src/plugin_npapi/make_osx_bundle.py
  27. 2 2
      direct/src/plugin_standalone/make_osx_bundle.py
  28. 2 2
      direct/src/showbase/Audio3DManager.py
  29. 1 1
      direct/src/showbase/Loader.py
  30. 6 6
      direct/src/showbase/Messenger.py
  31. 9 9
      direct/src/showbase/PythonUtil.py
  32. 7 7
      direct/src/showbase/RandomNumGen.py
  33. 9 9
      direct/src/showbase/ShowBase.py
  34. 11 8
      direct/src/showutil/FreezeTool.py
  35. 1 1
      direct/src/stdpy/glob.py
  36. 2 2
      direct/src/stdpy/thread.py
  37. 3 4
      direct/src/stdpy/threading2.py
  38. 3 3
      direct/src/task/Task.py
  39. 1 1
      direct/src/tkpanels/FSMInspector.py
  40. 4 4
      direct/src/tkpanels/Inspector.py
  41. 1 1
      direct/src/tkwidgets/Tree.py
  42. 1 1
      direct/src/wxwidgets/ViewPort.py
  43. 1 1
      direct/src/wxwidgets/WxPandaWindow.py
  44. 1 1
      dtool/src/dtoolutil/executionEnvironment.cxx
  45. 1 1
      dtool/src/dtoolutil/pfstreamBuf.cxx
  46. 24 3
      makepanda/makepanda.py
  47. 20 10
      makepanda/makepandacore.py
  48. 2 2
      panda/src/downloader/download_utils.cxx
  49. 16 0
      panda/src/ffmpeg/ffmpegAudioCursor.cxx
  50. 19 3
      panda/src/ffmpeg/ffmpegVideoCursor.cxx
  51. 40 0
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  52. 3 0
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  53. 3 0
      panda/src/pgraph/config_pgraph.cxx
  54. 29 0
      panda/src/pgraph/logicOpAttrib.I
  55. 205 0
      panda/src/pgraph/logicOpAttrib.cxx
  56. 112 0
      panda/src/pgraph/logicOpAttrib.h
  57. 56 0
      panda/src/pgraph/nodePath.cxx
  58. 83 51
      panda/src/pgraph/nodePath.h
  59. 1 0
      panda/src/pgraph/p3pgraph_composite3.cxx
  60. 1 1
      panda/src/pgraph/renderAttribRegistry.h
  61. 1 1
      panda/src/pnmimagetypes/pnmFileTypeJPGWriter.cxx
  62. 2 2
      samples/music-box/main.py

+ 1 - 1
direct/src/actor/Actor.py

@@ -2058,7 +2058,7 @@ class Actor(DirectObject, NodePath):
                 if otherPartName != partName and otherPartDef.truePartName == parent:
                 if otherPartName != partName and otherPartDef.truePartName == parent:
                     joints = self.getOverlappingJoints(partName, otherPartName)
                     joints = self.getOverlappingJoints(partName, otherPartName)
                     if joints:
                     if joints:
-                        raise StandardError, 'Overlapping joints: %s and %s' % (partName, otherPartName)
+                        raise Exception('Overlapping joints: %s and %s' % (partName, otherPartName))
 
 
     def setSubpartsComplete(self, flag):
     def setSubpartsComplete(self, flag):
 
 

+ 3 - 1
direct/src/directbase/DirectStart.py

@@ -1,7 +1,9 @@
 """ This is a deprecated module that creates a global instance of ShowBase. """
 """ This is a deprecated module that creates a global instance of ShowBase. """
 
 
 __all__ = []
 __all__ = []
-print('Using deprecated DirectStart interface.')
+
+if __debug__:
+    print('Using deprecated DirectStart interface.')
 
 
 from direct.showbase import ShowBase
 from direct.showbase import ShowBase
 base = ShowBase.ShowBase()
 base = ShowBase.ShowBase()

+ 1 - 1
direct/src/directdevices/DirectRadamec.py

@@ -77,7 +77,7 @@ class DirectRadamec(DirectObject):
             maxRange = self.maxRange[chan]
             maxRange = self.maxRange[chan]
             minRange = self.minRange[chan]
             minRange = self.minRange[chan]
         except IndexError:
         except IndexError:
-            raise RuntimeError, "can't normalize this channel (chanel %d)" % chan
+            raise RuntimeError("can't normalize this channel (channel %d)" % chan)
         range = maxRange - minRange
         range = maxRange - minRange
         clampedVal = CLAMP(self.aList[chan], minRange, maxRange)
         clampedVal = CLAMP(self.aList[chan], minRange, maxRange)
         return ((maxVal - minVal) * (clampedVal - minRange) / range) + minVal
         return ((maxVal - minVal) * (clampedVal - minRange) / range) + minVal

+ 1 - 1
direct/src/directnotify/Notifier.py

@@ -116,7 +116,7 @@ class Notifier:
             return NSError
             return NSError
 
 
     # error funcs
     # error funcs
-    def error(self, errorString, exception=StandardError):
+    def error(self, errorString, exception=Exception):
         """
         """
         Raise an exception with given string and optional type:
         Raise an exception with given string and optional type:
         Exception: error
         Exception: error

+ 16 - 14
direct/src/directscripts/gendocs.py

@@ -108,7 +108,7 @@ def findFiles(dirlist, ext, ign, list):
     for dir in dirlist:
     for dir in dirlist:
         for file in os.listdir(dir):
         for file in os.listdir(dir):
             full = dir + "/" + file
             full = dir + "/" + file
-            if (ign.has_key(full)==0) and (ign.has_key(file)==0):
+            if full not in ign and file not in ign:
                 if (os.path.isfile(full)):
                 if (os.path.isfile(full)):
                     if (file.endswith(ext)):
                     if (file.endswith(ext)):
                         list.append(full)
                         list.append(full)
@@ -145,7 +145,7 @@ def textToHTML(comment, sep, delsection=None):
             sec = sec.replace("  "," ")
             sec = sec.replace("  "," ")
             if (delsection != None) and (delsection.match(sec)):
             if (delsection != None) and (delsection.match(sec)):
                 included[sec] = 1
                 included[sec] = 1
-            if (included.has_key(sec)==0):
+            if sec not in included:
                 included[sec] = 1
                 included[sec] = 1
                 total = total + sec + "<br>\n"
                 total = total + sec + "<br>\n"
     return total
     return total
@@ -341,14 +341,14 @@ class InterrogateDatabase:
 def printTree(tree, indent):
 def printTree(tree, indent):
     spacing = "                                                        "[:indent]
     spacing = "                                                        "[:indent]
     if isinstance(tree, types.TupleType) and isinstance(tree[0], types.IntType):
     if isinstance(tree, types.TupleType) and isinstance(tree[0], types.IntType):
-        if symbol.sym_name.has_key(tree[0]):
+        if tree[0] in symbol.sym_name:
             for i in range(len(tree)):
             for i in range(len(tree)):
                 if (i==0):
                 if (i==0):
                     print spacing + "(symbol." + symbol.sym_name[tree[0]] + ","
                     print spacing + "(symbol." + symbol.sym_name[tree[0]] + ","
                 else:
                 else:
                     printTree(tree[i], indent+1)
                     printTree(tree[i], indent+1)
             print spacing + "),"
             print spacing + "),"
-        elif token.tok_name.has_key(tree[0]):
+        elif tree[0] in token.tok_name:
             print spacing + "(token." + token.tok_name[tree[0]] + ", '" + tree[1] + "'),"
             print spacing + "(token." + token.tok_name[tree[0]] + ", '" + tree[1] + "'),"
         else:
         else:
             print spacing + str(tree)
             print spacing + str(tree)
@@ -535,10 +535,10 @@ class ParseTreeInfo:
 
 
     def extract_tokens(self, str, tree):
     def extract_tokens(self, str, tree):
         if (isinstance(tree, types.TupleType)):
         if (isinstance(tree, types.TupleType)):
-            if (token.tok_name.has_key(tree[0])):
+            if tree[0] in token.tok_name:
                 str = str + tree[1]
                 str = str + tree[1]
                 if (tree[1]==","): str=str+" "
                 if (tree[1]==","): str=str+" "
-            elif (symbol.sym_name.has_key(tree[0])):
+            elif tree[0] in symbol.sym_name:
                 for sub in tree[1:]:
                 for sub in tree[1:]:
                     str = self.extract_tokens(str, sub)
                     str = self.extract_tokens(str, sub)
         return str
         return str
@@ -569,7 +569,7 @@ class CodeDatabase:
             tokzr = InterrogateTokenizer(cxx)
             tokzr = InterrogateTokenizer(cxx)
             idb = InterrogateDatabase(tokzr)
             idb = InterrogateDatabase(tokzr)
             for type in idb.types.values():
             for type in idb.types.values():
-                if (type.flags & 8192) or (self.types.has_key(type.scopedname)==0):
+                if (type.flags & 8192) or type.scopedname not in self.types:
                     self.types[type.scopedname] = type
                     self.types[type.scopedname] = type
                 if (type.flags & 8192) and (type.atomictype == 0) and (type.scopedname.count(" ")==0) and (type.scopedname.count(":")==0):
                 if (type.flags & 8192) and (type.atomictype == 0) and (type.scopedname.count(" ")==0) and (type.scopedname.count(":")==0):
                     self.goodtypes[type.scopedname] = type
                     self.goodtypes[type.scopedname] = type
@@ -706,7 +706,7 @@ class CodeDatabase:
     def getFunctionPrototype(self, fn, urlprefix, urlsuffix):
     def getFunctionPrototype(self, fn, urlprefix, urlsuffix):
         func = self.funcs.get(fn)
         func = self.funcs.get(fn)
         if (isinstance(func, InterrogateFunction)):
         if (isinstance(func, InterrogateFunction)):
-            if self.formattedprotos.has_key(fn):
+            if fn in self.formattedprotos:
                 proto = self.formattedprotos[fn]
                 proto = self.formattedprotos[fn]
             else:
             else:
                 proto = func.prototype
                 proto = func.prototype
@@ -864,7 +864,7 @@ def generate(pversion, indirlist, directdirlist, docdir, header, footer, urlpref
                 body = body + generateFunctionDocs(code, method, urlprefix, urlsuffix)
                 body = body + generateFunctionDocs(code, method, urlprefix, urlsuffix)
         body = header + body + footer
         body = header + body + footer
         writeFile(docdir + "/" + type + ".html", body)
         writeFile(docdir + "/" + type + ".html", body)
-        if (CLASS_RENAME_DICT.has_key(type)):
+        if type in CLASS_RENAME_DICT:
             modtype = CLASS_RENAME_DICT[type]
             modtype = CLASS_RENAME_DICT[type]
             writeFile(docdir + "/" + modtype + ".html", body)
             writeFile(docdir + "/" + modtype + ".html", body)
             xclasses.append(modtype)
             xclasses.append(modtype)
@@ -892,8 +892,10 @@ def generate(pversion, indirlist, directdirlist, docdir, header, footer, urlpref
         for method in code.getClassMethods(type)[:]:
         for method in code.getClassMethods(type)[:]:
             name = code.getFunctionName(method)
             name = code.getFunctionName(method)
             prefix = name[0].upper()
             prefix = name[0].upper()
-            if (table.has_key(prefix)==0): table[prefix] = {}
-            if (table[prefix].has_key(name)==0): table[prefix][name] = []
+            if prefix not in table:
+                table[prefix] = {}
+            if name not in table[prefix]:
+                table[prefix][name] = []
             table[prefix][name].append(type)
             table[prefix][name].append(type)
 
 
     index = "<h1>List of Methods - Panda " + pversion + "</h1>\n"
     index = "<h1>List of Methods - Panda " + pversion + "</h1>\n"
@@ -971,13 +973,13 @@ def expandImports(indirlist, directdirlist, fixdirlist):
                     print fixfile+" : "+module+" : repairing"
                     print fixfile+" : "+module+" : repairing"
                     for x in funcExports:
                     for x in funcExports:
                         fn = code.getFunctionName(x)
                         fn = code.getFunctionName(x)
-                        if (used.has_key(fn)):
+                        if fn in used:
                             result.append("from "+module+" import "+fn)
                             result.append("from "+module+" import "+fn)
                     for x in typeExports:
                     for x in typeExports:
-                        if (used.has_key(x)):
+                        if x in used:
                             result.append("from "+module+" import "+x)
                             result.append("from "+module+" import "+x)
                     for x in varExports:
                     for x in varExports:
-                        if (used.has_key(x)):
+                        if x in used:
                             result.append("from "+module+" import "+x)
                             result.append("from "+module+" import "+x)
             else:
             else:
                 result.append(line)
                 result.append(line)

+ 1 - 1
direct/src/distributed/ConnectionRepository.py

@@ -418,7 +418,7 @@ class ConnectionRepository(
                 if hasattr(module, symbolName):
                 if hasattr(module, symbolName):
                     dcImports[symbolName] = getattr(module, symbolName)
                     dcImports[symbolName] = getattr(module, symbolName)
                 else:
                 else:
-                    raise StandardError, 'Symbol %s not defined in module %s.' % (symbolName, moduleName)
+                    raise Exception('Symbol %s not defined in module %s.' % (symbolName, moduleName))
         else:
         else:
             # "import moduleName"
             # "import moduleName"
 
 

+ 1 - 1
direct/src/distributed/ServerRepository.py

@@ -192,7 +192,7 @@ class ServerRepository:
                     dcImports[symbolName] = getattr(module, symbolName)
                     dcImports[symbolName] = getattr(module, symbolName)
 
 
                 else:
                 else:
-                    raise StandardError, 'Symbol %s not defined in module %s.' % (symbolName, moduleName)
+                    raise Exception('Symbol %s not defined in module %s.' % (symbolName, moduleName))
 
 
         else:
         else:
             # "import moduleName"
             # "import moduleName"

+ 4 - 2
direct/src/extensions_native/extension_native_helpers.py

@@ -11,8 +11,10 @@ def Dtool_funcToMethod(func, cls, method_name=None):
     The new method is accessible to any instance immediately."""
     The new method is accessible to any instance immediately."""
     if sys.version_info < (3, 0):
     if sys.version_info < (3, 0):
         func.im_class = cls
         func.im_class = cls
-    func.im_func = func
-    func.im_self = None
+        func.im_func = func
+        func.im_self = None
+    func.__func__ = func
+    func.__self__ = None
     if not method_name:
     if not method_name:
         method_name = func.__name__
         method_name = func.__name__
     cls.DtoolClassDict[method_name] = func
     cls.DtoolClassDict[method_name] = func

+ 4 - 4
direct/src/fsm/FSM.py

@@ -190,7 +190,7 @@ class FSM(DirectObject):
     def getCurrentFilter(self):
     def getCurrentFilter(self):
         if not self.state:
         if not self.state:
             error = "FSM cannot determine current filter while in transition (%s -> %s)." % (self.oldState, self.newState)
             error = "FSM cannot determine current filter while in transition (%s -> %s)." % (self.oldState, self.newState)
-            raise AlreadyInTransition, error
+            raise AlreadyInTransition(error)
 
 
         filter = getattr(self, "filter" + self.state, None)
         filter = getattr(self, "filter" + self.state, None)
         if not filter:
         if not filter:
@@ -276,7 +276,7 @@ class FSM(DirectObject):
                 return
                 return
 
 
             if not self.request(request, *args):
             if not self.request(request, *args):
-                raise RequestDenied, "%s (from state: %s)" % (request, self.state)
+                raise RequestDenied("%s (from state: %s)" % (request, self.state))
         finally:
         finally:
             self.fsmLock.release()
             self.fsmLock.release()
 
 
@@ -310,7 +310,7 @@ class FSM(DirectObject):
                 self.name, request, str(args)[1:]))
                 self.name, request, str(args)[1:]))
 
 
             filter = self.getCurrentFilter()
             filter = self.getCurrentFilter()
-            result = filter(request, args)
+            result = list(filter(request, args))
             if result:
             if result:
                 if isinstance(result, types.StringTypes):
                 if isinstance(result, types.StringTypes):
                     # If the return value is a string, it's just the name
                     # If the return value is a string, it's just the name
@@ -381,7 +381,7 @@ class FSM(DirectObject):
             # request) not listed in defaultTransitions and not
             # request) not listed in defaultTransitions and not
             # handled by an earlier filter.
             # handled by an earlier filter.
             if request[0].isupper():
             if request[0].isupper():
-                raise RequestDenied, "%s (from state: %s)" % (request, self.state)
+                raise RequestDenied("%s (from state: %s)" % (request, self.state))
 
 
         # In either case, we quietly ignore unhandled command
         # In either case, we quietly ignore unhandled command
         # (lowercase) requests.
         # (lowercase) requests.

+ 6 - 6
direct/src/fsm/State.py

@@ -30,18 +30,18 @@ class State(DirectObject):
                 exitFunc = state.getExitFunc()
                 exitFunc = state.getExitFunc()
                 # print 'testing: ', state, enterFunc, exitFunc, oldFunction
                 # print 'testing: ', state, enterFunc, exitFunc, oldFunction
                 if type(enterFunc) == types.MethodType:
                 if type(enterFunc) == types.MethodType:
-                    if (enterFunc.im_func == oldFunction):
+                    if enterFunc.__func__ == oldFunction:
                         # print 'found: ', enterFunc, oldFunction
                         # print 'found: ', enterFunc, oldFunction
                         state.setEnterFunc(types.MethodType(newFunction,
                         state.setEnterFunc(types.MethodType(newFunction,
-                                                            enterFunc.im_self,
-                                                            enterFunc.im_class))
+                                                            enterFunc.__self__,
+                                                            enterFunc.__self__.__class__))
                         count += 1
                         count += 1
                 if type(exitFunc) == types.MethodType:
                 if type(exitFunc) == types.MethodType:
-                    if (exitFunc.im_func == oldFunction):
+                    if exitFunc.__func__ == oldFunction:
                         # print 'found: ', exitFunc, oldFunction
                         # print 'found: ', exitFunc, oldFunction
                         state.setExitFunc(types.MethodType(newFunction,
                         state.setExitFunc(types.MethodType(newFunction,
-                                                           exitFunc.im_self,
-                                                           exitFunc.im_class))
+                                                           exitFunc.__self__,
+                                                           exitFunc.__self__.__class__))
                         count += 1
                         count += 1
             return count
             return count
 
 

+ 9 - 10
direct/src/gui/DirectGuiBase.py

@@ -258,8 +258,8 @@ class DirectGuiBase(DirectObject.DirectObject):
                     text = 'Unknown option "'
                     text = 'Unknown option "'
                 else:
                 else:
                     text = 'Unknown options "'
                     text = 'Unknown options "'
-                raise KeyError, text + ', '.join(unusedOptions) + \
-                        '" for ' + myClass.__name__
+                raise KeyError(text + ', '.join(unusedOptions) + \
+                        '" for ' + myClass.__name__)
             # Can now call post init func
             # Can now call post init func
             self.postInitialiseFunc()
             self.postInitialiseFunc()
 
 
@@ -399,8 +399,8 @@ class DirectGuiBase(DirectObject.DirectObject):
 
 
                         if len(componentConfigFuncs) == 0 and \
                         if len(componentConfigFuncs) == 0 and \
                                 component not in self._dynamicGroups:
                                 component not in self._dynamicGroups:
-                            raise KeyError, 'Unknown option "' + option + \
-                                    '" for ' + self.__class__.__name__
+                            raise KeyError('Unknown option "' + option + \
+                                    '" for ' + self.__class__.__name__)
 
 
                     # Add the configure method(s) (may be more than
                     # Add the configure method(s) (may be more than
                     # one if this is configuring a component group)
                     # one if this is configuring a component group)
@@ -413,8 +413,8 @@ class DirectGuiBase(DirectObject.DirectObject):
                         indirectOptions[componentConfigFunc][componentOption] \
                         indirectOptions[componentConfigFunc][componentOption] \
                                 = value
                                 = value
                 else:
                 else:
-                    raise KeyError, 'Unknown option "' + option + \
-                            '" for ' + self.__class__.__name__
+                    raise KeyError('Unknown option "' + option + \
+                            '" for ' + self.__class__.__name__)
 
 
         # Call the configure methods for any components.
         # Call the configure methods for any components.
         # Pass in the dictionary of keyword/values created above
         # Pass in the dictionary of keyword/values created above
@@ -468,8 +468,8 @@ class DirectGuiBase(DirectObject.DirectObject):
                             return componentCget(componentOption)
                             return componentCget(componentOption)
 
 
         # Option not found
         # Option not found
-        raise KeyError, 'Unknown option "' + option + \
-                '" for ' + self.__class__.__name__
+        raise KeyError('Unknown option "' + option + \
+                '" for ' + self.__class__.__name__)
 
 
     # Allow index style refererences
     # Allow index style refererences
     __getitem__ = cget
     __getitem__ = cget
@@ -481,8 +481,7 @@ class DirectGuiBase(DirectObject.DirectObject):
         """
         """
         # Check for invalid component name
         # Check for invalid component name
         if '_' in componentName:
         if '_' in componentName:
-            raise ValueError, \
-                    'Component name "%s" must not contain "_"' % componentName
+            raise ValueError('Component name "%s" must not contain "_"' % componentName)
 
 
         # Get construction keywords
         # Get construction keywords
         if hasattr(self, '_constructorKeywords'):
         if hasattr(self, '_constructorKeywords'):

+ 1 - 1
direct/src/gui/DirectScrollBar.py

@@ -142,7 +142,7 @@ class DirectScrollBar(DirectFrame):
         elif self['orientation'] == DGG.VERTICAL_INVERTED:
         elif self['orientation'] == DGG.VERTICAL_INVERTED:
             self.guiItem.setAxis(Vec3(0, 0, 1))
             self.guiItem.setAxis(Vec3(0, 0, 1))
         else:
         else:
-            raise ValueError, 'Invalid value for orientation: %s' % (self['orientation'])
+            raise ValueError('Invalid value for orientation: %s' % (self['orientation']))
 
 
     def setManageButtons(self):
     def setManageButtons(self):
         self.guiItem.setManagePieces(self['manageButtons'])
         self.guiItem.setManagePieces(self['manageButtons'])

+ 1 - 1
direct/src/gui/DirectSlider.py

@@ -111,7 +111,7 @@ class DirectSlider(DirectFrame):
         elif self['orientation'] == DGG.VERTICAL:
         elif self['orientation'] == DGG.VERTICAL:
             self.guiItem.setAxis(Vec3(0, 0, 1))
             self.guiItem.setAxis(Vec3(0, 0, 1))
         else:
         else:
-            raise ValueError, 'Invalid value for orientation: %s' % (self['orientation'])
+            raise ValueError('Invalid value for orientation: %s' % (self['orientation']))
 
 
     def destroy(self):
     def destroy(self):
         if (hasattr(self, 'thumb')):
         if (hasattr(self, 'thumb')):

+ 3 - 3
direct/src/interval/FunctionInterval.py

@@ -34,11 +34,11 @@ class FunctionInterval(Interval.Interval):
                 # print 'testing: ', ival.function, oldFunction
                 # print 'testing: ', ival.function, oldFunction
                 # Note: you can only replace methods currently
                 # Note: you can only replace methods currently
                 if type(ival.function) == types.MethodType:
                 if type(ival.function) == types.MethodType:
-                    if (ival.function.im_func == oldFunction):
+                    if ival.function.__func__ == oldFunction:
                         # print 'found: ', ival.function, oldFunction
                         # print 'found: ', ival.function, oldFunction
                         ival.function = types.MethodType(newFunction,
                         ival.function = types.MethodType(newFunction,
-                                                         ival.function.im_self,
-                                                         ival.function.im_class)
+                                                         ival.function.__self__,
+                                                         ival.function.__self__.__class__)
                         count += 1
                         count += 1
             return count
             return count
 
 

+ 1 - 1
direct/src/interval/ProjectileInterval.py

@@ -221,7 +221,7 @@ class ProjectileInterval(Interval):
     def testTrajectory(self):
     def testTrajectory(self):
         try:
         try:
             self.__calcTrajectory(*self.trajectoryArgs)
             self.__calcTrajectory(*self.trajectoryArgs)
-        except StandardError:
+        except Exception:
             assert self.notify.error('invalid projectile parameters')
             assert self.notify.error('invalid projectile parameters')
             return False
             return False
         return True
         return True

+ 9 - 9
direct/src/p3d/AppRunner.py

@@ -798,7 +798,7 @@ class AppRunner(DirectObject):
             if not host.hasContentsFile:
             if not host.hasContentsFile:
                 # This is weird.  How did we launch without having
                 # This is weird.  How did we launch without having
                 # this file at all?
                 # this file at all?
-                raise OSError, message
+                raise OSError(message)
 
 
             # Just make it a warning and continue.
             # Just make it a warning and continue.
             self.notify.warning(message)
             self.notify.warning(message)
@@ -819,20 +819,20 @@ class AppRunner(DirectObject):
                     return self.addPackageInfo(name, platform, version, hostUrl, hostDir = hostDir, recurse = True)
                     return self.addPackageInfo(name, platform, version, hostUrl, hostDir = hostDir, recurse = True)
 
 
             message = "Couldn't find %s %s on %s" % (name, version, hostUrl)
             message = "Couldn't find %s %s on %s" % (name, version, hostUrl)
-            raise OSError, message
+            raise OSError(message)
 
 
         package.checkStatus()
         package.checkStatus()
         if not package.downloadDescFile(self.http):
         if not package.downloadDescFile(self.http):
             message = "Couldn't get desc file for %s" % (name)
             message = "Couldn't get desc file for %s" % (name)
-            raise OSError, message
+            raise OSError(message)
 
 
         if not package.downloadPackage(self.http):
         if not package.downloadPackage(self.http):
             message = "Couldn't download %s" % (name)
             message = "Couldn't download %s" % (name)
-            raise OSError, message
+            raise OSError(message)
 
 
         if not package.installPackage(self):
         if not package.installPackage(self):
             message = "Couldn't install %s" % (name)
             message = "Couldn't install %s" % (name)
-            raise OSError, message
+            raise OSError(message)
 
 
         if package.guiApp:
         if package.guiApp:
             self.guiApp = True
             self.guiApp = True
@@ -877,17 +877,17 @@ class AppRunner(DirectObject):
         vfs = VirtualFileSystem.getGlobalPtr()
         vfs = VirtualFileSystem.getGlobalPtr()
 
 
         if not vfs.exists(fname):
         if not vfs.exists(fname):
-            raise ArgumentError, "No such file: %s" % (p3dFilename)
+            raise ArgumentError("No such file: %s" % (p3dFilename))
 
 
         fname.makeAbsolute()
         fname.makeAbsolute()
         fname.setBinary()
         fname.setBinary()
         mf = Multifile()
         mf = Multifile()
         if p3dOffset == 0:
         if p3dOffset == 0:
             if not mf.openRead(fname):
             if not mf.openRead(fname):
-                raise ArgumentError, "Not a Panda3D application: %s" % (p3dFilename)
+                raise ArgumentError("Not a Panda3D application: %s" % (p3dFilename))
         else:
         else:
             if not mf.openRead(fname, p3dOffset):
             if not mf.openRead(fname, p3dOffset):
-                raise ArgumentError, "Not a Panda3D application: %s at offset: %s" % (p3dFilename, p3dOffset)
+                raise ArgumentError("Not a Panda3D application: %s at offset: %s" % (p3dFilename, p3dOffset))
 
 
         # Now load the p3dInfo file.
         # Now load the p3dInfo file.
         self.p3dInfo = None
         self.p3dInfo = None
@@ -925,7 +925,7 @@ class AppRunner(DirectObject):
         # The interactiveConsole flag can only be set true if the
         # The interactiveConsole flag can only be set true if the
         # application has allow_python_dev set.
         # application has allow_python_dev set.
         if not self.allowPythonDev and interactiveConsole:
         if not self.allowPythonDev and interactiveConsole:
-            raise StandardError, "Impossible, interactive_console set without allow_python_dev."
+            raise Exception("Impossible, interactive_console set without allow_python_dev.")
         self.interactiveConsole = interactiveConsole
         self.interactiveConsole = interactiveConsole
 
 
         if self.allowPythonDev:
         if self.allowPythonDev:

+ 11 - 5
direct/src/p3d/JavaScript.py

@@ -13,9 +13,11 @@ class UndefinedObject:
     attributes, similar to None, but it is a slightly different
     attributes, similar to None, but it is a slightly different
     concept in JavaScript. """
     concept in JavaScript. """
 
 
-    def __nonzero__(self):
+    def __bool__(self):
         return False
         return False
 
 
+    __nonzero__ = __bool__ # Python 2
+
     def __str__(self):
     def __str__(self):
         return "Undefined"
         return "Undefined"
 
 
@@ -83,16 +85,18 @@ class BrowserObject:
     def __str__(self):
     def __str__(self):
         return self.toString()
         return self.toString()
 
 
-    def __nonzero__(self):
+    def __bool__(self):
         return True
         return True
 
 
+    __nonzero__ = __bool__ # Python 2
+
     def __call__(self, *args, **kw):
     def __call__(self, *args, **kw):
         needsResponse = True
         needsResponse = True
         if 'needsResponse' in kw:
         if 'needsResponse' in kw:
             needsResponse = kw['needsResponse']
             needsResponse = kw['needsResponse']
             del kw['needsResponse']
             del kw['needsResponse']
         if kw:
         if kw:
-            raise ArgumentError, 'Keyword arguments not supported'
+            raise ArgumentError('Keyword arguments not supported')
 
 
         try:
         try:
             parentObj, attribName = self.__childObject
             parentObj, attribName = self.__childObject
@@ -242,16 +246,18 @@ class MethodWrapper:
         parentObj, attribName = self.__childObject
         parentObj, attribName = self.__childObject
         return "%s.%s" % (parentObj, attribName)
         return "%s.%s" % (parentObj, attribName)
 
 
-    def __nonzero__(self):
+    def __bool__(self):
         return True
         return True
 
 
+    __nonzero__ = __bool__ # Python 2
+
     def __call__(self, *args, **kw):
     def __call__(self, *args, **kw):
         needsResponse = True
         needsResponse = True
         if 'needsResponse' in kw:
         if 'needsResponse' in kw:
             needsResponse = kw['needsResponse']
             needsResponse = kw['needsResponse']
             del kw['needsResponse']
             del kw['needsResponse']
         if kw:
         if kw:
-            raise ArgumentError, 'Keyword arguments not supported'
+            raise ArgumentError('Keyword arguments not supported')
 
 
         try:
         try:
             parentObj, attribName = self.__childObject
             parentObj, attribName = self.__childObject

+ 1 - 1
direct/src/p3d/PackageInstaller.py

@@ -239,7 +239,7 @@ class PackageInstaller(DirectObject):
         downloaded.  Call donePackages() to finish the list. """
         downloaded.  Call donePackages() to finish the list. """
 
 
         if self.state != self.S_initial:
         if self.state != self.S_initial:
-            raise ValueError, 'addPackage called after donePackages'
+            raise ValueError('addPackage called after donePackages')
 
 
         host = self.appRunner.getHostWithAlt(hostUrl)
         host = self.appRunner.getHostWithAlt(hostUrl)
         pp = self.PendingPackage(packageName, version, host)
         pp = self.PendingPackage(packageName, version, host)

+ 4 - 4
direct/src/p3d/PackageMerger.py

@@ -7,7 +7,7 @@ from panda3d.core import *
 import shutil
 import shutil
 import os
 import os
 
 
-class PackageMergerError(StandardError):
+class PackageMergerError(Exception):
     pass
     pass
 
 
 class PackageMerger:
 class PackageMerger:
@@ -102,12 +102,12 @@ class PackageMerger:
             doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
             doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
             if not doc.LoadFile():
             if not doc.LoadFile():
                 message = "Could not read XML file: %s" % (self.descFile.filename)
                 message = "Could not read XML file: %s" % (self.descFile.filename)
-                raise OSError, message
+                raise OSError(message)
 
 
             xpackage = doc.FirstChildElement('package')
             xpackage = doc.FirstChildElement('package')
             if not xpackage:
             if not xpackage:
                 message = "No package definition: %s" % (self.descFile.filename)
                 message = "No package definition: %s" % (self.descFile.filename)
-                raise OSError, message
+                raise OSError(message)
 
 
             xcompressed = xpackage.FirstChildElement('compressed_archive')
             xcompressed = xpackage.FirstChildElement('compressed_archive')
             if xcompressed:
             if xcompressed:
@@ -286,7 +286,7 @@ class PackageMerger:
 
 
         if not self.__readContentsFile(sourceDir, packageNames):
         if not self.__readContentsFile(sourceDir, packageNames):
             message = "Couldn't read %s" % (sourceDir)
             message = "Couldn't read %s" % (sourceDir)
-            raise PackageMergerError, message
+            raise PackageMergerError(message)
 
 
     def close(self):
     def close(self):
         """ Finalizes the results of all of the previous calls to
         """ Finalizes the results of all of the previous calls to

+ 26 - 26
direct/src/p3d/Packager.py

@@ -26,7 +26,7 @@ from direct.directnotify.DirectNotifyGlobal import *
 
 
 vfs = VirtualFileSystem.getGlobalPtr()
 vfs = VirtualFileSystem.getGlobalPtr()
 
 
-class PackagerError(StandardError):
+class PackagerError(Exception):
     pass
     pass
 
 
 class OutsideOfPackageError(PackagerError):
 class OutsideOfPackageError(PackagerError):
@@ -390,7 +390,7 @@ class Packager:
 
 
             if not self.p3dApplication and not self.packager.allowPackages:
             if not self.p3dApplication and not self.packager.allowPackages:
                 message = 'Cannot generate packages without an installDir; use -i'
                 message = 'Cannot generate packages without an installDir; use -i'
-                raise PackagerError, message
+                raise PackagerError(message)
 
 
             if self.ignoredDirFiles:
             if self.ignoredDirFiles:
                 exts = sorted(self.ignoredDirFiles.keys())
                 exts = sorted(self.ignoredDirFiles.keys())
@@ -429,11 +429,11 @@ class Packager:
 
 
                 if self.version != PandaSystem.getPackageVersionString():
                 if self.version != PandaSystem.getPackageVersionString():
                     message = 'mismatched Panda3D version: requested %s, but Panda3D is built as %s' % (self.version, PandaSystem.getPackageVersionString())
                     message = 'mismatched Panda3D version: requested %s, but Panda3D is built as %s' % (self.version, PandaSystem.getPackageVersionString())
-                    raise PackagerError, message
+                    raise PackagerError(message)
 
 
                 if self.host != PandaSystem.getPackageHostUrl():
                 if self.host != PandaSystem.getPackageHostUrl():
                     message = 'mismatched Panda3D host: requested %s, but Panda3D is built as %s' % (self.host, PandaSystem.getPackageHostUrl())
                     message = 'mismatched Panda3D host: requested %s, but Panda3D is built as %s' % (self.host, PandaSystem.getPackageHostUrl())
-                    raise PackagerError, message
+                    raise PackagerError(message)
 
 
             if self.p3dApplication:
             if self.p3dApplication:
                 # Default compression level for an app.
                 # Default compression level for an app.
@@ -554,7 +554,7 @@ class Packager:
             # Add the main module, if any.
             # Add the main module, if any.
             if not self.mainModule and self.p3dApplication:
             if not self.mainModule and self.p3dApplication:
                 message = 'No main_module specified for application %s' % (self.packageName)
                 message = 'No main_module specified for application %s' % (self.packageName)
-                raise PackagerError, message
+                raise PackagerError(message)
             if self.mainModule:
             if self.mainModule:
                 moduleName, newName = self.mainModule
                 moduleName, newName = self.mainModule
                 if newName not in self.freezer.modules:
                 if newName not in self.freezer.modules:
@@ -790,7 +790,7 @@ class Packager:
 
 
             if not self.packager.allowPackages:
             if not self.packager.allowPackages:
                 message = 'Cannot generate packages without an installDir; use -i'
                 message = 'Cannot generate packages without an installDir; use -i'
-                raise PackagerError, message
+                raise PackagerError(message)
 
 
             installPath = Filename(self.packager.installDir, packageDir)
             installPath = Filename(self.packager.installDir, packageDir)
             # Remove any files already in the installPath.
             # Remove any files already in the installPath.
@@ -811,7 +811,7 @@ class Packager:
                 return
                 return
 
 
             if len(files) != 1:
             if len(files) != 1:
-                raise PackagerError, 'Multiple files in "solo" package %s' % (self.packageName)
+                raise PackagerError('Multiple files in "solo" package %s' % (self.packageName))
 
 
             Filename(installPath, '').makeDir()
             Filename(installPath, '').makeDir()
 
 
@@ -1535,7 +1535,7 @@ class Packager:
             compressedPath = Filename(self.packager.installDir, newCompressedFilename)
             compressedPath = Filename(self.packager.installDir, newCompressedFilename)
             if not compressFile(self.packageFullpath, compressedPath, 6):
             if not compressFile(self.packageFullpath, compressedPath, 6):
                 message = 'Unable to write %s' % (compressedPath)
                 message = 'Unable to write %s' % (compressedPath)
-                raise PackagerError, message
+                raise PackagerError(message)
 
 
         def readDescFile(self):
         def readDescFile(self):
             """ Reads the existing package.xml file before rewriting
             """ Reads the existing package.xml file before rewriting
@@ -1838,7 +1838,7 @@ class Packager:
                 if parentName not in self.freezer.modules:
                 if parentName not in self.freezer.modules:
                     message = 'Cannot add Python file %s; not in package' % (file.newName)
                     message = 'Cannot add Python file %s; not in package' % (file.newName)
                     if file.required or file.explicit:
                     if file.required or file.explicit:
-                        raise StandardError, message
+                        raise Exception(message)
                     else:
                     else:
                         self.notify.warning(message)
                         self.notify.warning(message)
                     return
                     return
@@ -1852,7 +1852,7 @@ class Packager:
             # Precompile egg files to bam's.
             # Precompile egg files to bam's.
             np = self.packager.loader.loadModel(file.filename)
             np = self.packager.loader.loadModel(file.filename)
             if not np:
             if not np:
-                raise StandardError, 'Could not read egg file %s' % (file.filename)
+                raise Exception('Could not read egg file %s' % (file.filename))
 
 
             bamName = Filename(file.newName)
             bamName = Filename(file.newName)
             bamName.setExtension('bam')
             bamName.setExtension('bam')
@@ -1862,14 +1862,14 @@ class Packager:
             # Load the bam file so we can massage its textures.
             # Load the bam file so we can massage its textures.
             bamFile = BamFile()
             bamFile = BamFile()
             if not bamFile.openRead(file.filename):
             if not bamFile.openRead(file.filename):
-                raise StandardError, 'Could not read bam file %s' % (file.filename)
+                raise Exception('Could not read bam file %s' % (file.filename))
 
 
             if not bamFile.resolve():
             if not bamFile.resolve():
-                raise StandardError, 'Could not resolve bam file %s' % (file.filename)
+                raise Exception('Could not resolve bam file %s' % (file.filename))
 
 
             node = bamFile.readNode()
             node = bamFile.readNode()
             if not node:
             if not node:
-                raise StandardError, 'Not a model file: %s' % (file.filename)
+                raise Exception('Not a model file: %s' % (file.filename))
 
 
             self.addNode(node, file.filename, file.newName)
             self.addNode(node, file.filename, file.newName)
 
 
@@ -2703,7 +2703,7 @@ class Packager:
             self.allowPackages = False
             self.allowPackages = False
 
 
         if not PandaSystem.getPackageVersionString() or not PandaSystem.getPackageHostUrl():
         if not PandaSystem.getPackageVersionString() or not PandaSystem.getPackageHostUrl():
-            raise PackagerError, 'This script must be run using a version of Panda3D that has been built\nfor distribution.  Try using ppackage.p3d or packp3d.p3d instead.\nIf you are running this script for development purposes, you may also\nset the Config variable panda-package-host-url to the URL you expect\nto download these contents from (for instance, a file:// URL).'
+            raise PackagerError('This script must be run using a version of Panda3D that has been built\nfor distribution.  Try using ppackage.p3d or packp3d.p3d instead.\nIf you are running this script for development purposes, you may also\nset the Config variable panda-package-host-url to the URL you expect\nto download these contents from (for instance, a file:// URL).')
 
 
         self.readContentsFile()
         self.readContentsFile()
 
 
@@ -2804,7 +2804,7 @@ class Packager:
                             self.notify.info("No files added to %s" % (name))
                             self.notify.info("No files added to %s" % (name))
                         for (lineno, stype, sname, args, kw) in statements:
                         for (lineno, stype, sname, args, kw) in statements:
                             if stype == 'class':
                             if stype == 'class':
-                                raise PackagerError, 'Nested classes not allowed'
+                                raise PackagerError('Nested classes not allowed')
                             self.__evalFunc(sname, args, kw)
                             self.__evalFunc(sname, args, kw)
                         package = self.endPackage()
                         package = self.endPackage()
                         if package is not None:
                         if package is not None:
@@ -2812,7 +2812,7 @@ class Packager:
                         elif packageNames is not None:
                         elif packageNames is not None:
                             # If the name is explicitly specified, this means
                             # If the name is explicitly specified, this means
                             # we should abort if the package faild to construct.
                             # we should abort if the package faild to construct.
-                            raise PackagerError, 'Failed to construct %s' % name
+                            raise PackagerError('Failed to construct %s' % name)
                 else:
                 else:
                     self.__evalFunc(name, args, kw)
                     self.__evalFunc(name, args, kw)
         except PackagerError:
         except PackagerError:
@@ -2838,7 +2838,7 @@ class Packager:
             func(*args, **kw)
             func(*args, **kw)
         except OutsideOfPackageError:
         except OutsideOfPackageError:
             message = '%s encountered outside of package definition' % (name)
             message = '%s encountered outside of package definition' % (name)
-            raise OutsideOfPackageError, message
+            raise OutsideOfPackageError(message)
 
 
     def __expandTabs(self, line, tabWidth = 8):
     def __expandTabs(self, line, tabWidth = 8):
         """ Expands tab characters in the line to 8 spaces. """
         """ Expands tab characters in the line to 8 spaces. """
@@ -2883,10 +2883,10 @@ class Packager:
             value = value.strip()
             value = value.strip()
             if parameter not in argList:
             if parameter not in argList:
                 message = 'Unknown parameter %s' % (parameter)
                 message = 'Unknown parameter %s' % (parameter)
-                raise PackagerError, message
+                raise PackagerError(message)
             if parameter in args:
             if parameter in args:
                 message = 'Duplicate parameter %s' % (parameter)
                 message = 'Duplicate parameter %s' % (parameter)
-                raise PackagerError, message
+                raise PackagerError(message)
 
 
             args[parameter] = value
             args[parameter] = value
 
 
@@ -2900,7 +2900,7 @@ class Packager:
         to file() etc., and close the package with endPackage(). """
         to file() etc., and close the package with endPackage(). """
 
 
         if self.currentPackage:
         if self.currentPackage:
-            raise PackagerError, 'unclosed endPackage %s' % (self.currentPackage.packageName)
+            raise PackagerError('unclosed endPackage %s' % (self.currentPackage.packageName))
 
 
         package = self.Package(packageName, self)
         package = self.Package(packageName, self)
         self.currentPackage = package
         self.currentPackage = package
@@ -2910,7 +2910,7 @@ class Packager:
 
 
         if not package.p3dApplication and not self.allowPackages:
         if not package.p3dApplication and not self.allowPackages:
             message = 'Cannot generate packages without an installDir; use -i'
             message = 'Cannot generate packages without an installDir; use -i'
-            raise PackagerError, message
+            raise PackagerError(message)
 
 
 
 
     def endPackage(self):
     def endPackage(self):
@@ -2919,7 +2919,7 @@ class Packager:
         or None if the package failed to close (e.g. missing files). """
         or None if the package failed to close (e.g. missing files). """
 
 
         if not self.currentPackage:
         if not self.currentPackage:
-            raise PackagerError, 'unmatched endPackage'
+            raise PackagerError('unmatched endPackage')
 
 
         package = self.currentPackage
         package = self.currentPackage
         package.signParams += self.signParams[:]
         package.signParams += self.signParams[:]
@@ -3310,7 +3310,7 @@ class Packager:
             raise OutsideOfPackageError
             raise OutsideOfPackageError
 
 
         if (newName or filename) and len(moduleNames) != 1:
         if (newName or filename) and len(moduleNames) != 1:
-            raise PackagerError, 'Cannot specify newName with multiple modules'
+            raise PackagerError('Cannot specify newName with multiple modules')
 
 
         if required:
         if required:
             self.currentPackage.requiredModules += moduleNames
             self.currentPackage.requiredModules += moduleNames
@@ -3469,7 +3469,7 @@ class Packager:
             package.mainModule = None
             package.mainModule = None
         if not package.mainModule and compileToExe:
         if not package.mainModule and compileToExe:
             message = "No main_module specified for exe %s" % (filename)
             message = "No main_module specified for exe %s" % (filename)
-            raise PackagerError, message
+            raise PackagerError(message)
 
 
         if package.mainModule:
         if package.mainModule:
             moduleName, newName = package.mainModule
             moduleName, newName = package.mainModule
@@ -3641,12 +3641,12 @@ class Packager:
         if newName:
         if newName:
             if len(files) != 1:
             if len(files) != 1:
                 message = 'Cannot install multiple files on target filename %s' % (newName)
                 message = 'Cannot install multiple files on target filename %s' % (newName)
-                raise PackagerError, message
+                raise PackagerError(message)
 
 
         if text:
         if text:
             if len(files) != 1:
             if len(files) != 1:
                 message = 'Cannot install text to multiple files'
                 message = 'Cannot install text to multiple files'
-                raise PackagerError, message
+                raise PackagerError(message)
             if not newName:
             if not newName:
                 newName = str(filenames[0])
                 newName = str(filenames[0])
 
 

+ 2 - 2
direct/src/p3d/PatchMaker.py

@@ -765,7 +765,7 @@ class PatchMaker:
             filename = Filename(package.currentFile.filename + '.%s.patch' % (package.patchVersion))
             filename = Filename(package.currentFile.filename + '.%s.patch' % (package.patchVersion))
             assert filename not in self.patchFilenames
             assert filename not in self.patchFilenames
             if not self.buildPatch(topPv, currentPv, package, filename):
             if not self.buildPatch(topPv, currentPv, package, filename):
-                raise StandardError, "Couldn't build patch."
+                raise Exception("Couldn't build patch.")
 
 
     def buildPatch(self, v1, v2, package, patchFilename):
     def buildPatch(self, v1, v2, package, patchFilename):
         """ Builds a patch from PackageVersion v1 to PackageVersion
         """ Builds a patch from PackageVersion v1 to PackageVersion
@@ -780,7 +780,7 @@ class PatchMaker:
         compressedPathname = Filename(pathname + '.pz')
         compressedPathname = Filename(pathname + '.pz')
         compressedPathname.unlink()
         compressedPathname.unlink()
         if not compressFile(pathname, compressedPathname, 9):
         if not compressFile(pathname, compressedPathname, 9):
-            raise StandardError, "Couldn't compress patch."
+            raise Exception("Couldn't compress patch.")
         pathname.unlink()
         pathname.unlink()
 
 
         patchfile = self.Patchfile(package)
         patchfile = self.Patchfile(package)

+ 1 - 1
direct/src/p3d/SeqValue.py

@@ -26,7 +26,7 @@ class SeqValue:
         elif isinstance(value, types.StringTypes):
         elif isinstance(value, types.StringTypes):
             self.setFromString(value)
             self.setFromString(value)
         else:
         else:
-            raise TypeError, 'Invalid sequence type: %s' % (value,)
+            raise TypeError('Invalid sequence type: %s' % (value,))
 
 
     def setFromTuple(self, value):
     def setFromTuple(self, value):
         """ Sets the seq from the indicated tuple of integers. """
         """ Sets the seq from the indicated tuple of integers. """

+ 6 - 6
direct/src/p3d/packp3d.py

@@ -107,7 +107,7 @@ from panda3d.core import *
 # Temp hack for debugging.
 # Temp hack for debugging.
 #from direct.p3d.AppRunner import dummyAppRunner; dummyAppRunner()
 #from direct.p3d.AppRunner import dummyAppRunner; dummyAppRunner()
 
 
-class ArgumentError(StandardError):
+class ArgumentError(Exception):
     pass
     pass
 
 
 def makePackedApp(args):
 def makePackedApp(args):
@@ -167,13 +167,13 @@ def makePackedApp(args):
             sys.exit(0)
             sys.exit(0)
 
 
     if not appFilename:
     if not appFilename:
-        raise ArgumentError, "No target app specified.  Use:\n  %s -o app.p3d\nUse -h to get more usage information." % (os.path.split(sys.argv[0])[1])
+        raise ArgumentError("No target app specified.  Use:\n  %s -o app.p3d\nUse -h to get more usage information." % (os.path.split(sys.argv[0])[1]))
 
 
     if args:
     if args:
-        raise ArgumentError, "Extra arguments on command line."
+        raise ArgumentError("Extra arguments on command line.")
 
 
     if appFilename.getExtension() != 'p3d':
     if appFilename.getExtension() != 'p3d':
-        raise ArgumentError, 'Application filename must end in ".p3d".'
+        raise ArgumentError('Application filename must end in ".p3d".')
 
 
     appDir = Filename(appFilename.getDirname())
     appDir = Filename(appFilename.getDirname())
     if not appDir:
     if not appDir:
@@ -188,9 +188,9 @@ def makePackedApp(args):
         if not main.exists():
         if not main.exists():
             main = glob.glob(os.path.join(root.toOsSpecific(), '*.py'))
             main = glob.glob(os.path.join(root.toOsSpecific(), '*.py'))
             if len(main) == 0:
             if len(main) == 0:
-                raise ArgumentError, 'No Python files in root directory.'
+                raise ArgumentError('No Python files in root directory.')
             elif len(main) > 1:
             elif len(main) > 1:
-                raise ArgumentError, 'Multiple Python files in root directory; specify the main application with -m "main".'
+                raise ArgumentError('Multiple Python files in root directory; specify the main application with -m "main".')
 
 
             main = Filename.fromOsSpecific(os.path.split(main[0])[1])
             main = Filename.fromOsSpecific(os.path.split(main[0])[1])
             main.makeAbsolute(root)
             main.makeAbsolute(root)

+ 1 - 1
direct/src/p3d/runp3d.py

@@ -46,7 +46,7 @@ def parseSysArgs():
             sys.exit(1)
             sys.exit(1)
 
 
     if not args or not args[0]:
     if not args or not args[0]:
-        raise ArgumentError, "No Panda app specified.  Use:\nrunp3d.py app.p3d"
+        raise ArgumentError("No Panda app specified.  Use:\nrunp3d.py app.p3d")
 
 
     arg0 = args[0]
     arg0 = args[0]
     p3dFilename = Filename.fromOsSpecific(arg0)
     p3dFilename = Filename.fromOsSpecific(arg0)

+ 1 - 1
direct/src/particles/ParticleEffect.py

@@ -204,7 +204,7 @@ class ParticleEffect(NodePath):
 
 
     def loadConfig(self, filename):
     def loadConfig(self, filename):
         data = vfs.readFile(filename, 1)
         data = vfs.readFile(filename, 1)
-        data = data.replace('\r', '')
+        data = data.replace(b'\r', b'')
         try:
         try:
             exec(data)
             exec(data)
         except:
         except:

+ 2 - 2
direct/src/plugin_npapi/make_osx_bundle.py

@@ -33,7 +33,7 @@ def makeBundle(startDir):
         path.appendPath(os.environ['DYLD_LIBRARY_PATH'])
         path.appendPath(os.environ['DYLD_LIBRARY_PATH'])
     nppanda3d = path.findFile('nppanda3d')
     nppanda3d = path.findFile('nppanda3d')
     if not nppanda3d:
     if not nppanda3d:
-        raise StandardError, "Couldn't find nppanda3d on path."
+        raise Exception("Couldn't find nppanda3d on path.")
 
 
     # Generate the bundle directory structure
     # Generate the bundle directory structure
     rootFilename = Filename(fstartDir, 'bundle')
     rootFilename = Filename(fstartDir, 'bundle')
@@ -54,7 +54,7 @@ def makeBundle(startDir):
         resourceFilename.toOsSpecific(), Filename(fstartDir, "nppanda3d.r").toOsSpecific()))
         resourceFilename.toOsSpecific(), Filename(fstartDir, "nppanda3d.r").toOsSpecific()))
 
 
     if not resourceFilename.exists():
     if not resourceFilename.exists():
-        raise IOError, 'Unable to run Rez'
+        raise IOError('Unable to run Rez')
 
 
     # Copy in Info.plist and the compiled executable.
     # Copy in Info.plist and the compiled executable.
     shutil.copyfile(Filename(fstartDir, "nppanda3d.plist").toOsSpecific(), plistFilename.toOsSpecific())
     shutil.copyfile(Filename(fstartDir, "nppanda3d.plist").toOsSpecific(), plistFilename.toOsSpecific())

+ 2 - 2
direct/src/plugin_standalone/make_osx_bundle.py

@@ -32,7 +32,7 @@ def makeBundle(startDir):
     path.appendPath(os.defpath)
     path.appendPath(os.defpath)
     panda3d_mac = path.findFile('panda3d_mac')
     panda3d_mac = path.findFile('panda3d_mac')
     if not panda3d_mac:
     if not panda3d_mac:
-        raise StandardError, "Couldn't find panda3d_mac on path."
+        raise Exception("Couldn't find panda3d_mac on path.")
 
 
     # Construct a search path to look for the images.
     # Construct a search path to look for the images.
     search = DSearchPath()
     search = DSearchPath()
@@ -53,7 +53,7 @@ def makeBundle(startDir):
     # Now find the icon file on the above search path.
     # Now find the icon file on the above search path.
     icons = search.findFile('panda3d.icns')
     icons = search.findFile('panda3d.icns')
     if not icons:
     if not icons:
-        raise StandardError, "Couldn't find panda3d.icns on model-path."
+        raise Exception("Couldn't find panda3d.icns on model-path.")
 
 
     # Generate the bundle directory structure
     # Generate the bundle directory structure
     rootFilename = Filename(fstartDir)
     rootFilename = Filename(fstartDir)

+ 2 - 2
direct/src/showbase/Audio3DManager.py

@@ -123,7 +123,7 @@ class Audio3DManager:
         Default: VBase3(0, 0, 0)
         Default: VBase3(0, 0, 0)
         """
         """
         if not isinstance(velocity, VBase3):
         if not isinstance(velocity, VBase3):
-            raise TypeError, "Invalid argument 1, expected <VBase3>"
+            raise TypeError("Invalid argument 1, expected <VBase3>")
         self.vel_dict[sound]=velocity
         self.vel_dict[sound]=velocity
 
 
     def setSoundVelocityAuto(self, sound):
     def setSoundVelocityAuto(self, sound):
@@ -156,7 +156,7 @@ class Audio3DManager:
         Default: VBase3(0, 0, 0)
         Default: VBase3(0, 0, 0)
         """
         """
         if not isinstance(velocity, VBase3):
         if not isinstance(velocity, VBase3):
-            raise TypeError, "Invalid argument 0, expected <VBase3>"
+            raise TypeError("Invalid argument 0, expected <VBase3>")
         self.listener_vel=velocity
         self.listener_vel=velocity
 
 
     def setListenerVelocityAuto(self):
     def setListenerVelocityAuto(self):

+ 1 - 1
direct/src/showbase/Loader.py

@@ -324,7 +324,7 @@ class Loader(DirectObject):
                 nodeList[i] = nodeList[i].node()
                 nodeList[i] = nodeList[i].node()
 
 
         # From here on, we deal with a list of (filename, node) pairs.
         # From here on, we deal with a list of (filename, node) pairs.
-        modelList = zip(modelList, nodeList)
+        modelList = list(zip(modelList, nodeList))
 
 
         if callback is None:
         if callback is None:
             # We got no callback, so it's a synchronous save.
             # We got no callback, so it's a synchronous save.

+ 6 - 6
direct/src/showbase/Messenger.py

@@ -132,7 +132,7 @@ class Messenger:
 
 
         # Make sure extraArgs is a list or tuple
         # Make sure extraArgs is a list or tuple
         if not (isinstance(extraArgs, list) or isinstance(extraArgs, tuple) or isinstance(extraArgs, set)):
         if not (isinstance(extraArgs, list) or isinstance(extraArgs, tuple) or isinstance(extraArgs, set)):
-            raise TypeError, "A list is required as extraArgs argument"
+            raise TypeError("A list is required as extraArgs argument")
 
 
         self.lock.acquire()
         self.lock.acquire()
         try:
         try:
@@ -442,7 +442,7 @@ class Messenger:
                 object, params = objectEntry
                 object, params = objectEntry
                 method = params[0]
                 method = params[0]
                 if (type(method) == types.MethodType):
                 if (type(method) == types.MethodType):
-                    function = method.im_func
+                    function = method.__func__
                 else:
                 else:
                     function = method
                     function = method
                 #print ('function: ' + repr(function) + '\n' +
                 #print ('function: ' + repr(function) + '\n' +
@@ -451,7 +451,7 @@ class Messenger:
                 #       'newFunction: ' + repr(newFunction) + '\n')
                 #       'newFunction: ' + repr(newFunction) + '\n')
                 if (function == oldMethod):
                 if (function == oldMethod):
                     newMethod = types.MethodType(
                     newMethod = types.MethodType(
-                        newFunction, method.im_self, method.im_class)
+                        newFunction, method.__self__, method.__self__.__class__)
                     params[0] = newMethod
                     params[0] = newMethod
                     # Found it retrun true
                     # Found it retrun true
                     retFlag += 1
                     retFlag += 1
@@ -560,8 +560,8 @@ class Messenger:
         return string version of class.method or method.
         return string version of class.method or method.
         """
         """
         if (type(method) == types.MethodType):
         if (type(method) == types.MethodType):
-            functionName = method.im_class.__name__ + '.' + \
-                method.im_func.__name__
+            functionName = method.__self__.__class__.__name__ + '.' + \
+                method.__func__.__name__
         else:
         else:
             if hasattr(method, "__name__"):
             if hasattr(method, "__name__"):
                 functionName = method.__name__
                 functionName = method.__name__
@@ -629,7 +629,7 @@ class Messenger:
                 if (type(function) == types.MethodType):
                 if (type(function) == types.MethodType):
                     str = (str + '\t' +
                     str = (str + '\t' +
                            'Method:       ' + repr(function) + '\n\t' +
                            'Method:       ' + repr(function) + '\n\t' +
-                           'Function:     ' + repr(function.im_func) + '\n')
+                           'Function:     ' + repr(function.__func__) + '\n')
                 else:
                 else:
                     str = (str + '\t' +
                     str = (str + '\t' +
                            'Function:     ' + repr(function) + '\n')
                            'Function:     ' + repr(function) + '\n')

+ 9 - 9
direct/src/showbase/PythonUtil.py

@@ -660,7 +660,7 @@ if __debug__:
 
 
         def profileDecorator(f):
         def profileDecorator(f):
             def _profiled(*args, **kArgs):
             def _profiled(*args, **kArgs):
-                name = '(%s) %s from %s' % (category, f.func_name, f.__module__)
+                name = '(%s) %s from %s' % (category, f.__name__, f.__module__)
 
 
                 # showbase might not be loaded yet, so don't use
                 # showbase might not be loaded yet, so don't use
                 # base.config.  Instead, query the ConfigVariableBool.
                 # base.config.  Instead, query the ConfigVariableBool.
@@ -1265,12 +1265,12 @@ class Enum:
             invalidChars = invalidChars.replace('_','')
             invalidChars = invalidChars.replace('_','')
             invalidFirstChars = invalidChars+string.digits
             invalidFirstChars = invalidChars+string.digits
             if item[0] in invalidFirstChars:
             if item[0] in invalidFirstChars:
-                raise SyntaxError, ("Enum '%s' contains invalid first char" %
+                raise SyntaxError("Enum '%s' contains invalid first char" %
                                     item)
                                     item)
             if not disjoint(item, invalidChars):
             if not disjoint(item, invalidChars):
                 for char in item:
                 for char in item:
                     if char in invalidChars:
                     if char in invalidChars:
-                        raise SyntaxError, (
+                        raise SyntaxError(
                             "Enum\n'%s'\ncontains illegal char '%s'" %
                             "Enum\n'%s'\ncontains illegal char '%s'" %
                             (item, char))
                             (item, char))
             return 1
             return 1
@@ -2070,7 +2070,7 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara
                 rArgs = '(' + reduce(str.__add__,rArgs)[:-2] + ')'
                 rArgs = '(' + reduce(str.__add__,rArgs)[:-2] + ')'
 
 
 
 
-            outStr = '%s%s' % (f.func_name, rArgs)
+            outStr = '%s%s' % (f.__name__, rArgs)
 
 
             # Insert prefix place holder, if needed
             # Insert prefix place holder, if needed
             if prefixes:
             if prefixes:
@@ -2127,9 +2127,9 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara
                 pass
                 pass
             return rVal
             return rVal
 
 
-        wrap.func_name = f.func_name
-        wrap.func_dict = f.func_dict
-        wrap.func_doc = f.func_doc
+        wrap.__name__ = f.__name__
+        wrap.__dict__ = f.__dict__
+        wrap.__doc__ = f.__doc__
         wrap.__module__ = f.__module__
         wrap.__module__ = f.__module__
         return wrap
         return wrap
     return decorator
     return decorator
@@ -2179,7 +2179,7 @@ if __debug__:
                     return f(*args, **kArgs)
                     return f(*args, **kArgs)
                 except Exception, e:
                 except Exception, e:
                     try:
                     try:
-                        s = '%s(' % f.func_name
+                        s = '%s(' % f.__name__
                         for arg in args:
                         for arg in args:
                             s += '%s, ' % arg
                             s += '%s, ' % arg
                         for key, value in kArgs.items():
                         for key, value in kArgs.items():
@@ -2193,7 +2193,7 @@ if __debug__:
                             exceptionLoggedNotify.info(s)
                             exceptionLoggedNotify.info(s)
                     except:
                     except:
                         exceptionLoggedNotify.info(
                         exceptionLoggedNotify.info(
-                            '%s: ERROR IN PRINTING' % f.func_name)
+                            '%s: ERROR IN PRINTING' % f.__name__)
                     raise
                     raise
             _exceptionLogged.__doc__ = f.__doc__
             _exceptionLogged.__doc__ = f.__doc__
             return _exceptionLogged
             return _exceptionLogged

+ 7 - 7
direct/src/showbase/RandomNumGen.py

@@ -96,30 +96,30 @@ class RandomNumGen:
         # common case while still doing adequate error checking
         # common case while still doing adequate error checking
         istart = int(start)
         istart = int(start)
         if istart != start:
         if istart != start:
-            raise ValueError, "non-integer arg 1 for randrange()"
+            raise ValueError("non-integer arg 1 for randrange()")
         if stop is None:
         if stop is None:
             if istart > 0:
             if istart > 0:
                 return self.__rand(istart)
                 return self.__rand(istart)
-            raise ValueError, "empty range for randrange()"
+            raise ValueError("empty range for randrange()")
         istop = int(stop)
         istop = int(stop)
         if istop != stop:
         if istop != stop:
-            raise ValueError, "non-integer stop for randrange()"
+            raise ValueError("non-integer stop for randrange()")
         if step == 1:
         if step == 1:
             if istart < istop:
             if istart < istop:
                 return istart + self.__rand(istop - istart)
                 return istart + self.__rand(istop - istart)
-            raise ValueError, "empty range for randrange()"
+            raise ValueError("empty range for randrange()")
         istep = int(step)
         istep = int(step)
         if istep != step:
         if istep != step:
-            raise ValueError, "non-integer step for randrange()"
+            raise ValueError("non-integer step for randrange()")
         if istep > 0:
         if istep > 0:
             n = (istop - istart + istep - 1) / istep
             n = (istop - istart + istep - 1) / istep
         elif istep < 0:
         elif istep < 0:
             n = (istop - istart + istep + 1) / istep
             n = (istop - istart + istep + 1) / istep
         else:
         else:
-            raise ValueError, "zero step for randrange()"
+            raise ValueError("zero step for randrange()")
 
 
         if n <= 0:
         if n <= 0:
-            raise ValueError, "empty range for randrange()"
+            raise ValueError("empty range for randrange()")
         return istart + istep*int(self.__rand(n))
         return istart + istep*int(self.__rand(n))
 
 
     def randint(self, a, b):
     def randint(self, a, b):

+ 9 - 9
direct/src/showbase/ShowBase.py

@@ -45,7 +45,7 @@ if __debug__:
 import AppRunnerGlobal
 import AppRunnerGlobal
 
 
 def legacyRun():
 def legacyRun():
-    builtins.base.notify.warning("run() is deprecated, use base.run() instead")
+    assert builtins.base.notify.warning("run() is deprecated, use base.run() instead")
     builtins.base.run()
     builtins.base.run()
 
 
 @atexit.register
 @atexit.register
@@ -338,7 +338,7 @@ class ShowBase(DirectObject.DirectObject):
 
 
         # Make sure we're not making more than one ShowBase.
         # Make sure we're not making more than one ShowBase.
         if hasattr(builtins, 'base'):
         if hasattr(builtins, 'base'):
-            raise StandardError, "Attempt to spawn multiple ShowBase instances!"
+            raise Exception("Attempt to spawn multiple ShowBase instances!")
 
 
         # DO NOT ADD TO THIS LIST.  We're trying to phase out the use of
         # DO NOT ADD TO THIS LIST.  We're trying to phase out the use of
         # built-in variables by ShowBase.  Use a Global module if necessary.
         # built-in variables by ShowBase.  Use a Global module if necessary.
@@ -695,7 +695,7 @@ class ShowBase(DirectObject.DirectObject):
             if requireWindow:
             if requireWindow:
                 # Unless require-window is set to false, it is an
                 # Unless require-window is set to false, it is an
                 # error not to open a window.
                 # error not to open a window.
-                raise StandardError, 'Could not open window.'
+                raise Exception('Could not open window.')
         else:
         else:
             self.notify.info("Successfully opened window of type %s (%s)" % (
             self.notify.info("Successfully opened window of type %s (%s)" % (
                 win.getType(), win.getPipe().getInterfaceName()))
                 win.getType(), win.getPipe().getInterfaceName()))
@@ -1793,14 +1793,14 @@ class ShowBase(DirectObject.DirectObject):
     # backwards compatibility. Please do not add code here, add
     # backwards compatibility. Please do not add code here, add
     # it to the loader.
     # it to the loader.
     def loadSfx(self, name):
     def loadSfx(self, name):
-        self.notify.warning("base.loadSfx is deprecated, use base.loader.loadSfx instead.")
+        assert self.notify.warning("base.loadSfx is deprecated, use base.loader.loadSfx instead.")
         return self.loader.loadSfx(name)
         return self.loader.loadSfx(name)
 
 
     # This function should only be in the loader but is here for
     # This function should only be in the loader but is here for
     # backwards compatibility. Please do not add code here, add
     # backwards compatibility. Please do not add code here, add
     # it to the loader.
     # it to the loader.
     def loadMusic(self, name):
     def loadMusic(self, name):
-        self.notify.warning("base.loadMusic is deprecated, use base.loader.loadMusic instead.")
+        assert self.notify.warning("base.loadMusic is deprecated, use base.loader.loadMusic instead.")
         return self.loader.loadMusic(name)
         return self.loader.loadMusic(name)
 
 
     def playSfx(
     def playSfx(
@@ -2519,7 +2519,7 @@ class ShowBase(DirectObject.DirectObject):
         rig = NodePath(namePrefix)
         rig = NodePath(namePrefix)
         buffer = source.makeCubeMap(namePrefix, size, rig, cameraMask, 1)
         buffer = source.makeCubeMap(namePrefix, size, rig, cameraMask, 1)
         if buffer == None:
         if buffer == None:
-            raise StandardError, "Could not make cube map."
+            raise Exception("Could not make cube map.")
 
 
         # Set the near and far planes from the default lens.
         # Set the near and far planes from the default lens.
         lens = rig.find('**/+Camera').node().getLens()
         lens = rig.find('**/+Camera').node().getLens()
@@ -2589,7 +2589,7 @@ class ShowBase(DirectObject.DirectObject):
         buffer = toSphere.makeCubeMap(namePrefix, size, rig, cameraMask, 0)
         buffer = toSphere.makeCubeMap(namePrefix, size, rig, cameraMask, 0)
         if buffer == None:
         if buffer == None:
             self.graphicsEngine.removeWindow(toSphere)
             self.graphicsEngine.removeWindow(toSphere)
-            raise StandardError, "Could not make cube map."
+            raise Exception("Could not make cube map.")
 
 
         # Set the near and far planes from the default lens.
         # Set the near and far planes from the default lens.
         lens = rig.find('**/+Camera').node().getLens()
         lens = rig.find('**/+Camera').node().getLens()
@@ -2904,7 +2904,7 @@ class ShowBase(DirectObject.DirectObject):
 
 
         # Use importlib to prevent this import from being picked up
         # Use importlib to prevent this import from being picked up
         # by modulefinder when packaging an application.
         # by modulefinder when packaging an application.
-        tkinter = importlib.import_module('Tkinter').tkinter
+        tkinter = importlib.import_module('_tkinter')
         Pmw = importlib.import_module('Pmw')
         Pmw = importlib.import_module('Pmw')
 
 
         # Create a new Tk root.
         # Create a new Tk root.
@@ -2938,7 +2938,7 @@ class ShowBase(DirectObject.DirectObject):
                 # dooneevent will return 0 if there are no more events
                 # dooneevent will return 0 if there are no more events
                 # waiting or 1 if there are still more.
                 # waiting or 1 if there are still more.
                 # DONT_WAIT tells tkinter not to block waiting for events
                 # DONT_WAIT tells tkinter not to block waiting for events
-                while tkinter.dooneevent(tkinter.ALL_EVENTS | tkinter.DONT_WAIT):
+                while self.tkRoot.dooneevent(tkinter.ALL_EVENTS | tkinter.DONT_WAIT):
                     pass
                     pass
 
 
                 return task.again
                 return task.again

+ 11 - 8
direct/src/showutil/FreezeTool.py

@@ -171,7 +171,7 @@ class CompilationEnvironment:
             }
             }
         print >> sys.stderr, compile
         print >> sys.stderr, compile
         if os.system(compile) != 0:
         if os.system(compile) != 0:
-            raise StandardError, 'failed to compile %s.' % basename
+            raise Exception('failed to compile %s.' % basename)
 
 
         link = self.linkExe % {
         link = self.linkExe % {
             'python' : self.Python,
             'python' : self.Python,
@@ -186,7 +186,7 @@ class CompilationEnvironment:
             }
             }
         print >> sys.stderr, link
         print >> sys.stderr, link
         if os.system(link) != 0:
         if os.system(link) != 0:
-            raise StandardError, 'failed to link %s.' % basename
+            raise Exception('failed to link %s.' % basename)
 
 
     def compileDll(self, filename, basename):
     def compileDll(self, filename, basename):
         compile = self.compileObj % {
         compile = self.compileObj % {
@@ -203,7 +203,7 @@ class CompilationEnvironment:
             }
             }
         print >> sys.stderr, compile
         print >> sys.stderr, compile
         if os.system(compile) != 0:
         if os.system(compile) != 0:
-            raise StandardError, 'failed to compile %s.' % basename
+            raise Exception('failed to compile %s.' % basename)
 
 
         link = self.linkDll % {
         link = self.linkDll % {
             'python' : self.Python,
             'python' : self.Python,
@@ -219,7 +219,7 @@ class CompilationEnvironment:
             }
             }
         print >> sys.stderr, link
         print >> sys.stderr, link
         if os.system(link) != 0:
         if os.system(link) != 0:
-            raise StandardError, 'failed to link %s.' % basename
+            raise Exception('failed to link %s.' % basename)
 
 
 # The code from frozenmain.c in the Python source repository.
 # The code from frozenmain.c in the Python source repository.
 frozenMainCode = """
 frozenMainCode = """
@@ -832,6 +832,7 @@ class Freezer:
         # bring in Python's startup modules.
         # bring in Python's startup modules.
         if addStartupModules:
         if addStartupModules:
             self.modules['_frozen_importlib'] = self.ModuleDef('importlib._bootstrap', implicit = True)
             self.modules['_frozen_importlib'] = self.ModuleDef('importlib._bootstrap', implicit = True)
+            self.modules['_frozen_importlib_external'] = self.ModuleDef('importlib._bootstrap_external', implicit = True)
 
 
             for moduleName in startupModules:
             for moduleName in startupModules:
                 if moduleName not in self.modules:
                 if moduleName not in self.modules:
@@ -1206,7 +1207,7 @@ class Freezer:
         Filename(mfname).unlink()
         Filename(mfname).unlink()
         multifile = Multifile()
         multifile = Multifile()
         if not multifile.openReadWrite(mfname):
         if not multifile.openReadWrite(mfname):
-            raise StandardError
+            raise Exception
 
 
         self.addToMultifile(multifile)
         self.addToMultifile(multifile)
 
 
@@ -1283,7 +1284,7 @@ class Freezer:
             # We must have a __main__ module to make an exe file.
             # We must have a __main__ module to make an exe file.
             if not self.__writingModule('__main__'):
             if not self.__writingModule('__main__'):
                 message = "Can't generate an executable without a __main__ module."
                 message = "Can't generate an executable without a __main__ module."
-                raise StandardError, message
+                raise Exception(message)
 
 
         filename = basename + self.sourceExtension
         filename = basename + self.sourceExtension
 
 
@@ -1406,9 +1407,11 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
                 return (None, name, ('', '', imp.PY_FROZEN))
                 return (None, name, ('', '', imp.PY_FROZEN))
 
 
         message = "DLL loader cannot find %s." % (name)
         message = "DLL loader cannot find %s." % (name)
-        raise ImportError, message
+        raise ImportError(message)
+
+    def load_module(self, fqname, fp, pathname, file_info):
+        suffix, mode, type = file_info
 
 
-    def load_module(self, fqname, fp, pathname, (suffix, mode, type)):
         if type == imp.PY_FROZEN:
         if type == imp.PY_FROZEN:
             # It's a frozen module.
             # It's a frozen module.
             co, isPackage = p3extend_frozen.get_frozen_module_code(pathname)
             co, isPackage = p3extend_frozen.get_frozen_module_code(pathname)

+ 1 - 1
direct/src/stdpy/glob.py

@@ -61,7 +61,7 @@ def glob1(dirname, pattern):
     except os.error:
     except os.error:
         return []
         return []
     if pattern[0] != '.':
     if pattern[0] != '.':
-        names = filter(lambda x: x[0] != '.', names)
+        names = [x for x in names if x[0] != '.']
     return fnmatch.filter(names, pattern)
     return fnmatch.filter(names, pattern)
 
 
 def glob0(dirname, basename):
 def glob0(dirname, basename):

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

@@ -21,7 +21,7 @@ from panda3d import core
 forceYield = core.Thread.forceYield
 forceYield = core.Thread.forceYield
 considerYield = core.Thread.considerYield
 considerYield = core.Thread.considerYield
 
 
-class error(StandardError):
+class error(Exception):
     pass
     pass
 
 
 class LockType:
 class LockType:
@@ -54,7 +54,7 @@ class LockType:
         self.__lock.acquire()
         self.__lock.acquire()
         try:
         try:
             if not self.__locked:
             if not self.__locked:
-                raise error, 'Releasing unheld lock.'
+                raise error('Releasing unheld lock.')
 
 
             self.__locked = False
             self.__locked = False
             self.__cvar.notify()
             self.__cvar.notify()

+ 3 - 4
direct/src/stdpy/threading2.py

@@ -139,10 +139,9 @@ class _RLock(_Verbose):
 
 
     # Internal methods used by condition variables
     # Internal methods used by condition variables
 
 
-    def _acquire_restore(self, (count, owner)):
+    def _acquire_restore(self, state):
         self.__block.acquire()
         self.__block.acquire()
-        self.__count = count
-        self.__owner = owner
+        self.__count, self.__owner = state
         if __debug__:
         if __debug__:
             self._note("%s._acquire_restore()", self)
             self._note("%s._acquire_restore()", self)
 
 
@@ -334,7 +333,7 @@ class _BoundedSemaphore(_Semaphore):
 
 
     def release(self):
     def release(self):
         if self._Semaphore__value >= self._initial_value:
         if self._Semaphore__value >= self._initial_value:
-            raise ValueError, "Semaphore released too many times"
+            raise ValueError("Semaphore released too many times")
         return _Semaphore.release(self)
         return _Semaphore.release(self)
 
 
 
 

+ 3 - 3
direct/src/task/Task.py

@@ -571,13 +571,13 @@ class TaskManager:
 
 
         method = task.getFunction()
         method = task.getFunction()
         if (type(method) == types.MethodType):
         if (type(method) == types.MethodType):
-            function = method.im_func
+            function = method.__func__
         else:
         else:
             function = method
             function = method
         if (function == oldMethod):
         if (function == oldMethod):
             newMethod = types.MethodType(newFunction,
             newMethod = types.MethodType(newFunction,
-                                         method.im_self,
-                                         method.im_class)
+                                         method.__self__,
+                                         method.__self__.__class__)
             task.setFunction(newMethod)
             task.setFunction(newMethod)
             # Found a match
             # Found a match
             return 1
             return 1

+ 1 - 1
direct/src/tkpanels/FSMInspector.py

@@ -151,7 +151,7 @@ class FSMInspector(AppShell):
                       toCenter,
                       toCenter,
                       self.computePoint(toState.radius,
                       self.computePoint(toState.radius,
                                          angle - DELTA))
                                          angle - DELTA))
-        return newFromPt + newToPt
+        return list(newFromPt) + list(newToPt)
 
 
     def computePoint(self, radius, angle):
     def computePoint(self, radius, angle):
         x = radius * math.cos(angle)
         x = radius * math.cos(angle)

+ 4 - 4
direct/src/tkpanels/Inspector.py

@@ -166,7 +166,7 @@ class FunctionInspector(Inspector):
 
 
 class InstanceMethodInspector(Inspector):
 class InstanceMethodInspector(Inspector):
     def title(self):
     def title(self):
-        return str(self.object.im_class) + "." + self.object.__name__ + "()"
+        return str(self.object.__self__.__class__) + "." + self.object.__name__ + "()"
 
 
 class CodeInspector(Inspector):
 class CodeInspector(Inspector):
     def title(self):
     def title(self):
@@ -391,10 +391,10 @@ class InspectorWindow:
 
 
     #Private
     #Private
     def selectedIndex(self):
     def selectedIndex(self):
-        indicies = map(int, self.listWidget.curselection())
-        if len(indicies) == 0:
+        indices = list(map(int, self.listWidget.curselection()))
+        if len(indices) == 0:
             return None
             return None
-        partNumber = indicies[0]
+        partNumber = indices[0]
         return partNumber
         return partNumber
 
 
     def inspectorForSelectedPart(self):
     def inspectorForSelectedPart(self):

+ 1 - 1
direct/src/tkwidgets/Tree.py

@@ -27,7 +27,7 @@ from panda3d.core import *
 # Initialize icon directory
 # Initialize icon directory
 ICONDIR = ConfigVariableSearchPath('model-path').findFile(Filename('icons')).toOsSpecific()
 ICONDIR = ConfigVariableSearchPath('model-path').findFile(Filename('icons')).toOsSpecific()
 if not os.path.isdir(ICONDIR):
 if not os.path.isdir(ICONDIR):
-    raise RuntimeError, "can't find DIRECT icon directory (%s)" % repr(ICONDIR)
+    raise RuntimeError("can't find DIRECT icon directory (%s)" % repr(ICONDIR))
 
 
 class TreeNode:
 class TreeNode:
 
 

+ 1 - 1
direct/src/wxwidgets/ViewPort.py

@@ -171,7 +171,7 @@ class Viewport(WxPandaWindow, DirectObject):
     if vpType == VPFRONT: return Viewport.makeFront(parent)
     if vpType == VPFRONT: return Viewport.makeFront(parent)
     if vpType == VPTOP:   return Viewport.makeTop(parent)
     if vpType == VPTOP:   return Viewport.makeTop(parent)
     if vpType == VPPERSPECTIVE:  return Viewport.makePerspective(parent)
     if vpType == VPPERSPECTIVE:  return Viewport.makePerspective(parent)
-    raise TypeError, "Unknown viewport type: %s" % vpType
+    raise TypeError("Unknown viewport type: %s" % vpType)
 
 
   @staticmethod
   @staticmethod
   def makeOrthographic(parent, name, campos):
   def makeOrthographic(parent, name, campos):

+ 1 - 1
direct/src/wxwidgets/WxPandaWindow.py

@@ -172,7 +172,7 @@ else:
                         break
                         break
 
 
             if pipe.getInterfaceName() != 'OpenGL':
             if pipe.getInterfaceName() != 'OpenGL':
-                raise StandardError, "Couldn't get an OpenGL pipe."
+                raise Exception("Couldn't get an OpenGL pipe.")
 
 
             self.win = base.openWindow(callbackWindowDict = callbackWindowDict, pipe = pipe, gsg = gsg, type = 'onscreen')
             self.win = base.openWindow(callbackWindowDict = callbackWindowDict, pipe = pipe, gsg = gsg, type = 'onscreen')
             self.hasCapture = False
             self.hasCapture = False

+ 1 - 1
dtool/src/dtoolutil/executionEnvironment.cxx

@@ -247,7 +247,7 @@ ns_get_environment_variable(const string &var) const {
   } else if (var == "MAIN_DIR") {
   } else if (var == "MAIN_DIR") {
     // Return the binary name's parent directory.  If we're running inside the
     // Return the binary name's parent directory.  If we're running inside the
     // Python interpreter, this will be overridden by a setting from
     // Python interpreter, this will be overridden by a setting from
-    // panda3dcore.py.
+    // panda3d/core.py.
     if (!_binary_name.empty()) {
     if (!_binary_name.empty()) {
       Filename main_dir (_binary_name);
       Filename main_dir (_binary_name);
       main_dir.make_absolute();
       main_dir.make_absolute();

+ 1 - 1
dtool/src/dtoolutil/pfstreamBuf.cxx

@@ -121,7 +121,7 @@ int PipeStreamBuf::underflow(void) {
 #endif /* PHAVE_IOSTREAM */
 #endif /* PHAVE_IOSTREAM */
     gbump(-((int)n));
     gbump(-((int)n));
   }
   }
-  delete buf;
+  delete[] buf;
   return ret;
   return ret;
 }
 }
 
 

+ 24 - 3
makepanda/makepanda.py

@@ -2668,7 +2668,26 @@ CreatePandaVersionFiles()
 ##########################################################################################
 ##########################################################################################
 
 
 if (PkgSkip("DIRECT")==0):
 if (PkgSkip("DIRECT")==0):
-    CopyPythonTree(GetOutputDir() + '/direct', 'direct/src', lib2to3_fixers=['all'])
+    fixers = [
+        'apply',
+        'callable',
+        'dict',
+        'except',
+        'execfile',
+        'import',
+        'imports',
+        'long',
+        'metaclass',
+        'next',
+        'numliterals',
+        'print',
+        'types',
+        'unicode',
+        'xrange',
+        'xreadlines',
+    ]
+
+    CopyPythonTree(GetOutputDir() + '/direct', 'direct/src', lib2to3_fixers=fixers, threads=THREADCOUNT)
     ConditionalWriteFile(GetOutputDir() + '/direct/__init__.py', "")
     ConditionalWriteFile(GetOutputDir() + '/direct/__init__.py', "")
 
 
     # This file used to be copied, but would nowadays cause conflicts.
     # This file used to be copied, but would nowadays cause conflicts.
@@ -2718,7 +2737,8 @@ if not PkgSkip("PYTHON"):
 
 
     # Also add this file, for backward compatibility.
     # Also add this file, for backward compatibility.
     ConditionalWriteFile(GetOutputDir() + '/panda3d/dtoolconfig.py', """
     ConditionalWriteFile(GetOutputDir() + '/panda3d/dtoolconfig.py', """
-print("Warning: panda3d.dtoolconfig is deprecated, use panda3d.interrogatedb instead.")
+if __debug__:
+    print("Warning: panda3d.dtoolconfig is deprecated, use panda3d.interrogatedb instead.")
 from .interrogatedb import *
 from .interrogatedb import *
 """)
 """)
 
 
@@ -2749,7 +2769,8 @@ if not PkgSkip("VRPN"):
 panda_modules_code = """
 panda_modules_code = """
 "This module is deprecated.  Import from panda3d.core and other panda3d.* modules instead."
 "This module is deprecated.  Import from panda3d.core and other panda3d.* modules instead."
 
 
-print("Warning: pandac.PandaModules is deprecated, import from panda3d.core instead")
+if __debug__:
+    print("Warning: pandac.PandaModules is deprecated, import from panda3d.core instead")
 """
 """
 
 
 for module in panda_modules:
 for module in panda_modules:

+ 20 - 10
makepanda/makepandacore.py

@@ -2652,18 +2652,25 @@ def CopyTree(dstdir, srcdir, omitVCS=True):
         if omitVCS:
         if omitVCS:
             DeleteVCS(dstdir)
             DeleteVCS(dstdir)
 
 
-def CopyPythonTree(dstdir, srcdir, lib2to3_fixers=[]):
+def CopyPythonTree(dstdir, srcdir, lib2to3_fixers=[], threads=0):
     if (not os.path.isdir(dstdir)):
     if (not os.path.isdir(dstdir)):
         os.mkdir(dstdir)
         os.mkdir(dstdir)
 
 
     lib2to3 = None
     lib2to3 = None
+    lib2to3_args = ['-w', '-n', '--no-diffs']
+
     if len(lib2to3_fixers) > 0 and sys.version_info >= (3, 0):
     if len(lib2to3_fixers) > 0 and sys.version_info >= (3, 0):
         from lib2to3.main import main as lib2to3
         from lib2to3.main import main as lib2to3
-        lib2to3_args = ['-w', '-n', '--no-diffs', '-x', 'buffer', '-x', 'idioms', '-x', 'set_literal', '-x', 'ws_comma']
-        if lib2to3_fixers != ['all']:
+
+        if lib2to3_fixers == ['all']:
+            lib2to3_args += ['-x', 'buffer', '-x', 'idioms', '-x', 'set_literal', '-x', 'ws_comma']
+        else:
             for fixer in lib2to3_fixers:
             for fixer in lib2to3_fixers:
                 lib2to3_args += ['-f', fixer]
                 lib2to3_args += ['-f', fixer]
 
 
+    if threads:
+        lib2to3_args += ['-j', str(threads)]
+
     exclude_files = set(VCS_FILES)
     exclude_files = set(VCS_FILES)
     exclude_files.add('panda3d.py')
     exclude_files.add('panda3d.py')
 
 
@@ -2679,20 +2686,23 @@ def CopyPythonTree(dstdir, srcdir, lib2to3_fixers=[]):
 
 
                     if ext == '.py' and not entry.endswith('-extensions.py'):
                     if ext == '.py' and not entry.endswith('-extensions.py'):
                         refactor.append((dstpth, srcpth))
                         refactor.append((dstpth, srcpth))
+                        lib2to3_args.append(dstpth)
                     else:
                     else:
                         JustBuilt([dstpth], [srcpth])
                         JustBuilt([dstpth], [srcpth])
 
 
         elif entry not in VCS_DIRS:
         elif entry not in VCS_DIRS:
-            CopyPythonTree(dstpth, srcpth, lib2to3_fixers)
+            CopyPythonTree(dstpth, srcpth, lib2to3_fixers, threads=threads)
 
 
-    for dstpth, srcpth in refactor:
-        if lib2to3 is not None:
-            ret = lib2to3("lib2to3.fixes", lib2to3_args + [dstpth])
-            if ret != 0:
+    if refactor and lib2to3 is not None:
+        ret = lib2to3("lib2to3.fixes", lib2to3_args)
+
+        if ret != 0:
+            for dstpth, srcpth in refactor:
                 os.remove(dstpth)
                 os.remove(dstpth)
                 exit("Error in lib2to3.")
                 exit("Error in lib2to3.")
-        JustBuilt([dstpth], [srcpth])
-
+        else:
+            for dstpth, srcpth in refactor:
+                JustBuilt([dstpth], [srcpth])
 
 
 ########################################################################
 ########################################################################
 ##
 ##

+ 2 - 2
panda/src/downloader/download_utils.cxx

@@ -40,7 +40,7 @@ check_crc(Filename name) {
   unsigned long crc = crc32(0L, Z_NULL, 0);
   unsigned long crc = crc32(0L, Z_NULL, 0);
   crc = crc32(crc, (unsigned char *)buffer, buffer_length);
   crc = crc32(crc, (unsigned char *)buffer, buffer_length);
 
 
-  delete buffer;
+  delete[] buffer;
 
 
   return crc;
   return crc;
 }
 }
@@ -66,7 +66,7 @@ check_adler(Filename name) {
   unsigned long adler = adler32(0L, Z_NULL, 0);
   unsigned long adler = adler32(0L, Z_NULL, 0);
   adler = adler32(adler, (unsigned char *)buffer, buffer_length);
   adler = adler32(adler, (unsigned char *)buffer, buffer_length);
 
 
-  delete buffer;
+  delete[] buffer;
 
 
   return adler;
   return adler;
 }
 }

+ 16 - 0
panda/src/ffmpeg/ffmpegAudioCursor.cxx

@@ -203,7 +203,11 @@ cleanup() {
 
 
   if (_packet) {
   if (_packet) {
     if (_packet->data) {
     if (_packet->data) {
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
+      av_packet_unref(_packet);
+#else
       av_free_packet(_packet);
       av_free_packet(_packet);
+#endif
     }
     }
     delete _packet;
     delete _packet;
     _packet = NULL;
     _packet = NULL;
@@ -242,7 +246,11 @@ cleanup() {
 void FfmpegAudioCursor::
 void FfmpegAudioCursor::
 fetch_packet() {
 fetch_packet() {
   if (_packet->data) {
   if (_packet->data) {
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
+    av_packet_unref(_packet);
+#else
     av_free_packet(_packet);
     av_free_packet(_packet);
+#endif
   }
   }
   while (av_read_frame(_format_ctx, _packet) >= 0) {
   while (av_read_frame(_format_ctx, _packet) >= 0) {
     if (_packet->stream_index == _audio_index) {
     if (_packet->stream_index == _audio_index) {
@@ -250,7 +258,11 @@ fetch_packet() {
       _packet_data = _packet->data;
       _packet_data = _packet->data;
       return;
       return;
     }
     }
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
+    av_packet_unref(_packet);
+#else
     av_free_packet(_packet);
     av_free_packet(_packet);
+#endif
   }
   }
   _packet->data = 0;
   _packet->data = 0;
   _packet_size = 0;
   _packet_size = 0;
@@ -300,7 +312,11 @@ reload_buffer() {
       pkt.size = _packet_size;
       pkt.size = _packet_size;
       int len = avcodec_decode_audio4(_audio_ctx, _frame, &got_frame, &pkt);
       int len = avcodec_decode_audio4(_audio_ctx, _frame, &got_frame, &pkt);
       movies_debug("avcodec_decode_audio4 returned " << len);
       movies_debug("avcodec_decode_audio4 returned " << len);
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
+      av_packet_unref(&pkt);
+#else
       av_free_packet(&pkt);
       av_free_packet(&pkt);
+#endif
 
 
       bufsize = 0;
       bufsize = 0;
       if (got_frame) {
       if (got_frame) {

+ 19 - 3
panda/src/ffmpeg/ffmpegVideoCursor.cxx

@@ -82,9 +82,13 @@ init_from(FfmpegVideo *source) {
 
 
 #ifdef HAVE_SWSCALE
 #ifdef HAVE_SWSCALE
   nassertv(_convert_ctx == NULL);
   nassertv(_convert_ctx == NULL);
-  _convert_ctx = sws_getContext(_size_x, _size_y,
-                                _video_ctx->pix_fmt, _size_x, _size_y,
-                                PIX_FMT_BGR24, SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
+  _convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt,
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 74, 100)
+                                _size_x, _size_y, AV_PIX_FMT_BGR24,
+#else
+                                _size_x, _size_y, PIX_FMT_BGR24,
+#endif
+                                SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL);
 #endif  // HAVE_SWSCALE
 #endif  // HAVE_SWSCALE
 
 
 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100)
 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100)
@@ -568,7 +572,11 @@ cleanup() {
 
 
   if (_packet) {
   if (_packet) {
     if (_packet->data) {
     if (_packet->data) {
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
+      av_packet_unref(_packet);
+#else
       av_free_packet(_packet);
       av_free_packet(_packet);
+#endif
     }
     }
     delete _packet;
     delete _packet;
     _packet = NULL;
     _packet = NULL;
@@ -737,14 +745,22 @@ fetch_packet(int default_frame) {
 bool FfmpegVideoCursor::
 bool FfmpegVideoCursor::
 do_fetch_packet(int default_frame) {
 do_fetch_packet(int default_frame) {
   if (_packet->data) {
   if (_packet->data) {
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
+    av_packet_unref(_packet);
+#else
     av_free_packet(_packet);
     av_free_packet(_packet);
+#endif
   }
   }
   while (av_read_frame(_format_ctx, _packet) >= 0) {
   while (av_read_frame(_format_ctx, _packet) >= 0) {
     if (_packet->stream_index == _video_index) {
     if (_packet->stream_index == _video_index) {
       _packet_frame = _packet->dts;
       _packet_frame = _packet->dts;
       return false;
       return false;
     }
     }
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
+    av_packet_unref(_packet);
+#else
     av_free_packet(_packet);
     av_free_packet(_packet);
+#endif
   }
   }
   _packet->data = 0;
   _packet->data = 0;
 
 

+ 40 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -56,6 +56,7 @@
 #include "depthWriteAttrib.h"
 #include "depthWriteAttrib.h"
 #include "fogAttrib.h"
 #include "fogAttrib.h"
 #include "lightAttrib.h"
 #include "lightAttrib.h"
+#include "logicOpAttrib.h"
 #include "materialAttrib.h"
 #include "materialAttrib.h"
 #include "rescaleNormalAttrib.h"
 #include "rescaleNormalAttrib.h"
 #include "scissorAttrib.h"
 #include "scissorAttrib.h"
@@ -476,6 +477,7 @@ reset() {
   _inv_state_mask.clear_bit(TransparencyAttrib::get_class_slot());
   _inv_state_mask.clear_bit(TransparencyAttrib::get_class_slot());
   _inv_state_mask.clear_bit(ColorWriteAttrib::get_class_slot());
   _inv_state_mask.clear_bit(ColorWriteAttrib::get_class_slot());
   _inv_state_mask.clear_bit(ColorBlendAttrib::get_class_slot());
   _inv_state_mask.clear_bit(ColorBlendAttrib::get_class_slot());
+  _inv_state_mask.clear_bit(LogicOpAttrib::get_class_slot());
   _inv_state_mask.clear_bit(TextureAttrib::get_class_slot());
   _inv_state_mask.clear_bit(TextureAttrib::get_class_slot());
   _inv_state_mask.clear_bit(TexGenAttrib::get_class_slot());
   _inv_state_mask.clear_bit(TexGenAttrib::get_class_slot());
   _inv_state_mask.clear_bit(TexMatrixAttrib::get_class_slot());
   _inv_state_mask.clear_bit(TexMatrixAttrib::get_class_slot());
@@ -6645,6 +6647,34 @@ do_issue_material() {
 }
 }
 #endif  // SUPPORT_FIXED_FUNCTION
 #endif  // SUPPORT_FIXED_FUNCTION
 
 
+/**
+ * Issues the logic operation attribute to the GL.
+ */
+#if !defined(OPENGLES) || defined(OPENGLES_1)
+void CLP(GraphicsStateGuardian)::
+do_issue_logic_op() {
+  const LogicOpAttrib *target_logic_op;
+  _target_rs->get_attrib_def(target_logic_op);
+
+  if (target_logic_op->get_operation() != LogicOpAttrib::O_none) {
+    glEnable(GL_COLOR_LOGIC_OP);
+    glLogicOp(GL_CLEAR - 1 + (int)target_logic_op->get_operation());
+
+    if (GLCAT.is_spam()) {
+      GLCAT.spam() << "glEnable(GL_COLOR_LOGIC_OP)\n";
+      GLCAT.spam() << "glLogicOp(" << target_logic_op->get_operation() << ")\n";
+    }
+  } else {
+    glDisable(GL_COLOR_LOGIC_OP);
+    glLogicOp(GL_COPY);
+
+    if (GLCAT.is_spam()) {
+      GLCAT.spam() << "glDisable(GL_COLOR_LOGIC_OP)\n";
+    }
+  }
+}
+#endif
+
 /**
 /**
  *
  *
  */
  */
@@ -9683,6 +9713,16 @@ set_state_and_transform(const RenderState *target,
   }
   }
 #endif
 #endif
 
 
+#if !defined(OPENGLES) || defined(OPENGLES_1)
+  int logic_op_slot = LogicOpAttrib::get_class_slot();
+  if (_target_rs->get_attrib(logic_op_slot) != _state_rs->get_attrib(logic_op_slot) ||
+      !_state_mask.get_bit(logic_op_slot)) {
+    // PStatGPUTimer timer(this, _draw_set_state_logic_op_pcollector);
+    do_issue_logic_op();
+    _state_mask.set_bit(logic_op_slot);
+  }
+#endif
+
   int transparency_slot = TransparencyAttrib::get_class_slot();
   int transparency_slot = TransparencyAttrib::get_class_slot();
   int color_write_slot = ColorWriteAttrib::get_class_slot();
   int color_write_slot = ColorWriteAttrib::get_class_slot();
   int color_blend_slot = ColorBlendAttrib::get_class_slot();
   int color_blend_slot = ColorBlendAttrib::get_class_slot();

+ 3 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -405,6 +405,9 @@ protected:
   void do_issue_material();
   void do_issue_material();
 #endif
 #endif
   void do_issue_texture();
   void do_issue_texture();
+#if !defined(OPENGLES) || defined(OPENGLES_1)
+  void do_issue_logic_op();
+#endif
   void do_issue_blending();
   void do_issue_blending();
 #ifdef SUPPORT_FIXED_FUNCTION
 #ifdef SUPPORT_FIXED_FUNCTION
   void do_issue_tex_gen();
   void do_issue_tex_gen();

+ 3 - 0
panda/src/pgraph/config_pgraph.cxx

@@ -50,6 +50,7 @@
 #include "loaderFileType.h"
 #include "loaderFileType.h"
 #include "loaderFileTypeBam.h"
 #include "loaderFileTypeBam.h"
 #include "loaderFileTypeRegistry.h"
 #include "loaderFileTypeRegistry.h"
+#include "logicOpAttrib.h"
 #include "materialAttrib.h"
 #include "materialAttrib.h"
 #include "modelFlattenRequest.h"
 #include "modelFlattenRequest.h"
 #include "modelLoadRequest.h"
 #include "modelLoadRequest.h"
@@ -419,6 +420,7 @@ init_libpgraph() {
   Loader::init_type();
   Loader::init_type();
   LoaderFileType::init_type();
   LoaderFileType::init_type();
   LoaderFileTypeBam::init_type();
   LoaderFileTypeBam::init_type();
+  LogicOpAttrib::init_type();
   MaterialAttrib::init_type();
   MaterialAttrib::init_type();
   ModelFlattenRequest::init_type();
   ModelFlattenRequest::init_type();
   ModelLoadRequest::init_type();
   ModelLoadRequest::init_type();
@@ -483,6 +485,7 @@ init_libpgraph() {
   LensNode::register_with_read_factory();
   LensNode::register_with_read_factory();
   LightAttrib::register_with_read_factory();
   LightAttrib::register_with_read_factory();
   LightRampAttrib::register_with_read_factory();
   LightRampAttrib::register_with_read_factory();
+  LogicOpAttrib::register_with_read_factory();
   MaterialAttrib::register_with_read_factory();
   MaterialAttrib::register_with_read_factory();
   ModelNode::register_with_read_factory();
   ModelNode::register_with_read_factory();
   ModelRoot::register_with_read_factory();
   ModelRoot::register_with_read_factory();

+ 29 - 0
panda/src/pgraph/logicOpAttrib.I

@@ -0,0 +1,29 @@
+/**
+ * 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 logicOpAttrib.I
+ * @author rdb
+ * @date 2016-03-24
+ */
+
+/**
+ * Use LogicOpAttrib::make() to construct a new LogicOpAttrib object.
+ */
+INLINE LogicOpAttrib::
+LogicOpAttrib(LogicOpAttrib::Operation op) :
+  _op(op)
+{
+}
+
+/**
+ * Returns the logic operation specified by this attribute.
+ */
+INLINE LogicOpAttrib::Operation LogicOpAttrib::
+get_operation() const {
+  return _op;
+}

+ 205 - 0
panda/src/pgraph/logicOpAttrib.cxx

@@ -0,0 +1,205 @@
+/**
+ * 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 logicOpAttrib.I
+ * @author rdb
+ * @date 2016-03-24
+ */
+
+#include "logicOpAttrib.h"
+#include "graphicsStateGuardianBase.h"
+#include "dcast.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle LogicOpAttrib::_type_handle;
+int LogicOpAttrib::_attrib_slot;
+
+/**
+ * Constructs a new LogicOpAttrib object that disables special-effect
+ * blending, allowing normal transparency to be used instead.
+ */
+CPT(RenderAttrib) LogicOpAttrib::
+make_off() {
+  return RenderAttribRegistry::quick_get_global_ptr()->get_slot_default(_attrib_slot);
+}
+
+/**
+ * Constructs a new LogicOpAttrib object with the given logic operation.
+ */
+CPT(RenderAttrib) LogicOpAttrib::
+make(LogicOpAttrib::Operation op) {
+  LogicOpAttrib *attrib = new LogicOpAttrib(op);
+  return return_new(attrib);
+}
+
+/**
+ * Returns a RenderAttrib that corresponds to whatever the standard default
+ * properties for render attributes of this type ought to be.
+ */
+CPT(RenderAttrib) LogicOpAttrib::
+make_default() {
+  return RenderAttribRegistry::quick_get_global_ptr()->get_slot_default(_attrib_slot);
+}
+
+/**
+ *
+ */
+void LogicOpAttrib::
+output(ostream &out) const {
+  out << get_type() << ":" << get_operation();
+}
+
+/**
+ * Intended to be overridden by derived LogicOpAttrib types to return a
+ * unique number indicating whether this LogicOpAttrib is equivalent to the
+ * other one.
+ *
+ * This should return 0 if the two LogicOpAttrib objects are equivalent, a
+ * number less than zero if this one should be sorted before the other one,
+ * and a number greater than zero otherwise.
+ *
+ * This will only be called with two LogicOpAttrib objects whose get_type()
+ * functions return the same.
+ */
+int LogicOpAttrib::
+compare_to_impl(const RenderAttrib *other) const {
+  const LogicOpAttrib *la = (const LogicOpAttrib *)other;
+  return (int)_op - (int)la->_op;
+}
+
+/**
+ * Intended to be overridden by derived RenderAttrib types to return a unique
+ * hash for these particular properties.  RenderAttribs that compare the same
+ * with compare_to_impl(), above, should return the same hash; RenderAttribs
+ * that compare differently should return a different hash.
+ */
+size_t LogicOpAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_op);
+  return hash;
+}
+
+/**
+ *
+ */
+CPT(RenderAttrib) LogicOpAttrib::
+get_auto_shader_attrib_impl(const RenderState *state) const {
+  return RenderAttribRegistry::quick_get_global_ptr()->get_slot_default(_attrib_slot);
+}
+
+/**
+ * Tells the BamReader how to create objects of type LogicOpAttrib.
+ */
+void LogicOpAttrib::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void LogicOpAttrib::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  RenderAttrib::write_datagram(manager, dg);
+
+  dg.add_uint8(_op);
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type LogicOpAttrib is encountered in the Bam file.  It should create the
+ * LogicOpAttrib and extract its information from the file.
+ */
+TypedWritable *LogicOpAttrib::
+make_from_bam(const FactoryParams &params) {
+  LogicOpAttrib *attrib = new LogicOpAttrib(O_none);
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  attrib->fillin(scan, manager);
+
+  return attrib;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new LogicOpAttrib.
+ */
+void LogicOpAttrib::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  RenderAttrib::fillin(scan, manager);
+
+  _op = (Operation)scan.get_uint8();
+}
+
+/**
+ *
+ */
+ostream &
+operator << (ostream &out, LogicOpAttrib::Operation op) {
+  switch (op) {
+  case LogicOpAttrib::O_none:
+    return out << "none";
+
+  case LogicOpAttrib::O_clear:
+    return out << "clear";
+
+  case LogicOpAttrib::O_and:
+    return out << "and";
+
+  case LogicOpAttrib::O_and_reverse:
+    return out << "and_reverse";
+
+  case LogicOpAttrib::O_copy:
+    return out << "copy";
+
+  case LogicOpAttrib::O_and_inverted:
+    return out << "and_inverted";
+
+  case LogicOpAttrib::O_noop:
+    return out << "noop";
+
+  case LogicOpAttrib::O_xor:
+    return out << "xor";
+
+  case LogicOpAttrib::O_or:
+    return out << "or";
+
+  case LogicOpAttrib::O_nor:
+    return out << "nor";
+
+  case LogicOpAttrib::O_equivalent:
+    return out << "equivalent";
+
+  case LogicOpAttrib::O_invert:
+    return out << "invert";
+
+  case LogicOpAttrib::O_or_reverse:
+    return out << "or_reverse";
+
+  case LogicOpAttrib::O_copy_inverted:
+    return out << "copy_inverted";
+
+  case LogicOpAttrib::O_or_inverted:
+    return out << "or_inverted";
+
+  case LogicOpAttrib::O_nand:
+    return out << "nand";
+
+  case LogicOpAttrib::O_set:
+    return out << "set";
+  }
+
+  return out << "**invalid LogicOpAttrib::Operation(" << (int)op << ")**";
+}

+ 112 - 0
panda/src/pgraph/logicOpAttrib.h

@@ -0,0 +1,112 @@
+/**
+ * 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 logicOpAttrib.I
+ * @author rdb
+ * @date 2016-03-24
+ */
+
+#ifndef LOGICOPATTRIB_H
+#define LOGICOPATTRIB_H
+
+#include "pandabase.h"
+#include "luse.h"
+#include "renderAttrib.h"
+
+class FactoryParams;
+
+/**
+ * If enabled, specifies that a custom logical operation be performed instead
+ * of any color blending.  Setting it to a value other than M_none will cause
+ * color blending to be disabled and the given logic operation to be performed.
+ */
+class EXPCL_PANDA_PGRAPH LogicOpAttrib : public RenderAttrib {
+PUBLISHED:
+  enum Operation {
+    O_none,  // LogicOp disabled, regular blending occurs.
+    O_clear, // Clears framebuffer value.
+    O_and,
+    O_and_reverse,
+    O_copy,  // Writes the incoming color to the framebuffer.
+    O_and_inverted,
+    O_noop,  // Leaves the framebuffer value unaltered.
+    O_xor,
+    O_or,
+    O_nor,
+    O_equivalent,
+    O_invert,
+    O_or_reverse,
+    O_copy_inverted,
+    O_or_inverted,
+    O_nand,
+    O_set,   // Sets all the bits in the framebuffer to 1.
+  };
+
+private:
+  INLINE LogicOpAttrib(Operation op);
+
+PUBLISHED:
+  static CPT(RenderAttrib) make_off();
+  static CPT(RenderAttrib) make(Operation op);
+  static CPT(RenderAttrib) make_default();
+
+  INLINE Operation get_operation() const;
+  MAKE_PROPERTY(operation, get_operation);
+
+public:
+  virtual void output(ostream &out) const;
+
+protected:
+  virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
+  virtual CPT(RenderAttrib) get_auto_shader_attrib_impl(const RenderState *state) const;
+
+private:
+  Operation _op;
+
+PUBLISHED:
+  static int get_class_slot() {
+    return _attrib_slot;
+  }
+  virtual int get_slot() const {
+    return get_class_slot();
+  }
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    RenderAttrib::init_type();
+    register_type(_type_handle, "LogicOpAttrib",
+                  RenderAttrib::get_class_type());
+    _attrib_slot = register_slot(_type_handle, 100, new LogicOpAttrib(O_none));
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+  static int _attrib_slot;
+};
+
+EXPCL_PANDA_PGRAPH ostream &operator << (ostream &out, LogicOpAttrib::Operation op);
+
+#include "logicOpAttrib.I"
+
+#endif

+ 56 - 0
panda/src/pgraph/nodePath.cxx

@@ -4832,6 +4832,62 @@ get_transparency() const {
   return TransparencyAttrib::M_none;
   return TransparencyAttrib::M_none;
 }
 }
 
 
+/**
+ * Specifically sets or disables a logical operation on this particular node.
+ * If no other nodes override, this will cause geometry to be rendered without
+ * color blending but instead using the given logical operator.
+ */
+void NodePath::
+set_logic_op(LogicOpAttrib::Operation op, int priority) {
+  nassertv_always(!is_empty());
+
+  node()->set_attrib(LogicOpAttrib::make(op), priority);
+}
+
+/**
+ * Completely removes any logical operation that may have been set on this
+ * node via set_logic_op(). The geometry at this level and below will
+ * subsequently be rendered using standard color blending.
+ */
+void NodePath::
+clear_logic_op() {
+  nassertv_always(!is_empty());
+  node()->clear_attrib(LogicOpAttrib::get_class_slot());
+}
+
+/**
+ * Returns true if a logical operation has been explicitly set on this
+ * particular node via set_logic_op().  If this returns true, then
+ * get_logic_op() may be called to determine whether a logical operation has
+ * been explicitly disabled for this node or set to particular operation.
+ */
+bool NodePath::
+has_logic_op() const {
+  nassertr_always(!is_empty(), false);
+  return node()->has_attrib(LogicOpAttrib::get_class_slot());
+}
+
+/**
+ * Returns the logical operation that has been specifically set on this node
+ * via set_logic_op(), or O_none if standard color blending has been
+ * specifically set, or if nothing has been specifically set.  See also
+ * has_logic_op().  This does not necessarily imply that the geometry will
+ * or will not be rendered with the given logical operation, as there may be
+ * other nodes that override.
+ */
+LogicOpAttrib::Operation NodePath::
+get_logic_op() const {
+  nassertr_always(!is_empty(), LogicOpAttrib::O_none);
+  const RenderAttrib *attrib =
+    node()->get_attrib(LogicOpAttrib::get_class_slot());
+  if (attrib != (const RenderAttrib *)NULL) {
+    const LogicOpAttrib *ta = DCAST(LogicOpAttrib, attrib);
+    return ta->get_operation();
+  }
+
+  return LogicOpAttrib::O_none;
+}
+
 /**
 /**
  * Specifies the antialiasing type that should be applied at this node and
  * Specifies the antialiasing type that should be applied at this node and
  * below.  See AntialiasAttrib.
  * below.  See AntialiasAttrib.

+ 83 - 51
panda/src/pgraph/nodePath.h

@@ -26,6 +26,7 @@
 #include "transformState.h"
 #include "transformState.h"
 #include "renderModeAttrib.h"
 #include "renderModeAttrib.h"
 #include "transparencyAttrib.h"
 #include "transparencyAttrib.h"
+#include "logicOpAttrib.h"
 #include "nodePathComponent.h"
 #include "nodePathComponent.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "referenceCount.h"
 #include "referenceCount.h"
@@ -62,57 +63,83 @@ class SamplerState;
 class Shader;
 class Shader;
 class ShaderInput;
 class ShaderInput;
 
 
-/*
- * A NodePath is the fundamental unit of high-level interaction with the scene
- * graph.  It encapsulates the complete path down to a node from some other
- * node, usually the root of the scene graph.  This is used to resolve
- * ambiguities associated with instancing.  NodePath also contains a number of
- * handy high-level methods for common scene-graph manipulations, such as
- * reparenting, and common state changes, such as repositioning.  There are
- * also a number of NodePath methods for finding nodes deep within the tree by
- * name or by type.  These take a path string, which at its simplest consists
- * of a series of node names separated by slashes, like a directory pathname.
- * Each component of the path string may optionally consist of one of the
- * following special names, instead of a node name: *          -- matches
- * exactly one node, with any name.  **         -- matches any sequence of
- * zero or more nodes.  +typename  -- matches any node that is or derives from
- * the given type.  -typename  -- matches any node that is the given type
- * exactly.  =tag       -- matches any node that has the indicated tag.
- * =tag=value -- matches any node whose tag matches the indicated value.
- * Furthermore, a node name may itself contain standard filename globbing
- * characters, like *, ?, and [a-z], that will be accepted as a partial match.
- * (In fact, the '*' special name may be seen as just a special case of this.)
- * The globbing characters may not be used with the typename matches or with
- * tag matches, but they may be used to match a tag's value in the =tag=value
- * syntax.  The special characters "@@", appearing at the beginning of a node
- * name, indicate a stashed node.  Normally, stashed nodes are not returned by
- * a find (but see the special flags, below), but a stashed node may be found
- * if it is explicitly named with its leading @@ characters.  By extension,
- * "@@*" may be used to identify any stashed node.  Examples: "roomgraph" will
- * look for a node named "graph", which is a child of an unnamed node, which
- * is a child of a node named "room", which is a child of the starting path.
- * "**red*" will look for any node anywhere in the tree (below the starting
- * path) with a name that begins with "red". "**+PartBundleNode**head" will
- * look for a node named "head", somewhere below a PartBundleNode anywhere in
- * the tree.  The search is always potentially ambiguous, even if the special
- * wildcard operators are not used, because there may be multiple nodes in the
- * tree with the same name.  In general, in the case of an ambiguity, the
- * shortest path is preferred; when a method (such as extend_by) must choose
- * only only one of several possible paths, it will choose the shortest
- * available; on the other hand, when a method (such as find_all_matches) is
- * to return all of the matching paths, it will sort them so that the shortest
- * paths appear first in the output.  Special flags.  The entire string may
- * optionally be followed by the ";" character, followed by one or more of the
- * following special control flags, with no intervening spaces or punctuation:
- * -h    Do not return hidden nodes.  +h    Do return hidden nodes.  -s    Do
- * not return stashed nodes unless explicitly referenced with @@. +s    Return
- * stashed nodes even without any explicit @@ characters.  -i    Node name
- * comparisons are not case insensitive: case must match exactly.  +i    Node
- * name comparisons are case insensitive: case is not important.  This affects
- * matches against the node name only; node type and tag strings are always
- * case sensitive.  The default flags are +h-s-i.
- */
-
+//
+// A NodePath is the fundamental unit of high-level interaction with the scene
+// graph.  It encapsulates the complete path down to a node from some other
+// node, usually the root of the scene graph.  This is used to resolve
+// ambiguities associated with instancing.
+//
+// NodePath also contains a number of handy high-level methods for common
+// scene-graph manipulations, such as reparenting, and common state changes,
+// such as repositioning.
+//
+// There are also a number of NodePath methods for finding nodes deep within
+// the tree by name or by type.  These take a path string, which at its
+// simplest consists of a series of node names separated by slashes, like a
+// directory pathname.
+//
+// Each component of the path string may optionally consist of one of the
+// following special names, instead of a node name:
+//
+//   *          -- matches exactly one node, with any name.
+//   **         -- matches any sequence of zero or more nodes.
+//   +typename  -- matches any node that is or derives from the given type.
+//   -typename  -- matches any node that is the given type exactly.
+//   =tag       -- matches any node that has the indicated tag.
+//   =tag=value -- matches any node whose tag matches the indicated value.
+//
+// Furthermore, a node name may itself contain standard filename globbing
+// characters, like *, ?, and [a-z], that will be accepted as a partial match.
+// (In fact, the '*' special name may be seen as just a special case of this.)
+// The globbing characters may not be used with the typename matches or with
+// tag matches, but they may be used to match a tag's value in the =tag=value
+// syntax.
+//
+// The special characters "@@", appearing at the beginning of a node name,
+// indicate a stashed node.  Normally, stashed nodes are not returned by a
+// find (but see the special flags, below), but a stashed node may be found if
+// it is explicitly named with its leading @@ characters.  By extension, "@@*"
+// may be used to identify any stashed node.
+//
+// Examples:
+//
+// "room//graph" will look for a node named "graph", which is a child of an
+// unnamed node, which is a child of a node named "room", which is a child of
+// the starting path.
+//
+// "**/red*" will look for any node anywhere in the tree (below the starting
+// path) with a name that begins with "red".
+//
+// "**/+PartBundleNode/**/head" will look for a node named "head", somewhere
+// below a PartBundleNode anywhere in the tree.
+//
+//
+// The search is always potentially ambiguous, even if the special wildcard
+// operators are not used, because there may be multiple nodes in the tree
+// with the same name.  In general, in the case of an ambiguity, the shortest
+// path is preferred; when a method (such as extend_by) must choose only only
+// one of several possible paths, it will choose the shortest available; on
+// the other hand, when a method (such as find_all_matches) is to return all
+// of the matching paths, it will sort them so that the shortest paths appear
+// first in the output.
+//
+//
+// Special flags.  The entire string may optionally be followed by the ";"
+// character, followed by one or more of the following special control flags,
+// with no intervening spaces or punctuation:
+//
+//    -h    Do not return hidden nodes.
+//    +h    Do return hidden nodes.
+//    -s    Do not return stashed nodes unless explicitly referenced with @@.
+//    +s    Return stashed nodes even without any explicit @@ characters.
+//    -i    Node name comparisons are not case insensitive: case must match
+//          exactly.
+//    +i    Node name comparisons are case insensitive: case is not important.
+//          This affects matches against the node name only; node type and tag
+//          strings are always case sensitive.
+//
+// The default flags are +h-s-i.
+//
 
 
 /**
 /**
  * NodePath is the fundamental system for disambiguating instances, and also
  * NodePath is the fundamental system for disambiguating instances, and also
@@ -786,6 +813,11 @@ PUBLISHED:
   bool has_transparency() const;
   bool has_transparency() const;
   TransparencyAttrib::Mode get_transparency() const;
   TransparencyAttrib::Mode get_transparency() const;
 
 
+  void set_logic_op(LogicOpAttrib::Operation op, int priority = 0);
+  void clear_logic_op();
+  bool has_logic_op() const;
+  LogicOpAttrib::Operation get_logic_op() const;
+
   void set_antialias(unsigned short mode, int priority = 0);
   void set_antialias(unsigned short mode, int priority = 0);
   void clear_antialias();
   void clear_antialias();
   bool has_antialias() const;
   bool has_antialias() const;

+ 1 - 0
panda/src/pgraph/p3pgraph_composite3.cxx

@@ -7,6 +7,7 @@
 #include "loaderFileType.cxx"
 #include "loaderFileType.cxx"
 #include "loaderFileTypeBam.cxx"
 #include "loaderFileTypeBam.cxx"
 #include "loaderFileTypeRegistry.cxx"
 #include "loaderFileTypeRegistry.cxx"
+#include "logicOpAttrib.cxx"
 #include "materialAttrib.cxx"
 #include "materialAttrib.cxx"
 #include "materialCollection.cxx"
 #include "materialCollection.cxx"
 #include "modelFlattenRequest.cxx"
 #include "modelFlattenRequest.cxx"

+ 1 - 1
panda/src/pgraph/renderAttribRegistry.h

@@ -47,7 +47,7 @@ public:
 
 
   // Raise this number whenever we add a new attrib.  This used to be
   // Raise this number whenever we add a new attrib.  This used to be
   // determined at runtime, but it's better to have it as a constexpr.
   // determined at runtime, but it's better to have it as a constexpr.
-  static const int _max_slots = 29;
+  static const int _max_slots = 32;
 
 
   int register_slot(TypeHandle type_handle, int sort,
   int register_slot(TypeHandle type_handle, int sort,
                     RenderAttrib *default_attrib);
                     RenderAttrib *default_attrib);

+ 1 - 1
panda/src/pnmimagetypes/pnmFileTypeJPGWriter.cxx

@@ -319,7 +319,7 @@ write_data(xel *array, xelval *) {
     row_pointer[0] = row;
     row_pointer[0] = row;
     (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
     (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
   }
   }
-  delete row;
+  delete[] row;
 
 
   /* Step 6: Finish compression */
   /* Step 6: Finish compression */
 
 

+ 2 - 2
samples/music-box/main.py

@@ -41,7 +41,7 @@ class MusicBox(DirectObject):
 
 
         # Loading sounds is done in a similar way to loading other things
         # Loading sounds is done in a similar way to loading other things
         # Loading the main music box song
         # Loading the main music box song
-        self.musicBoxSound = base.loadMusic('music/musicbox.ogg')
+        self.musicBoxSound = loader.loadMusic('music/musicbox.ogg')
         self.musicBoxSound.setVolume(.5)  # Volume is a percentage from 0 to 1
         self.musicBoxSound.setVolume(.5)  # Volume is a percentage from 0 to 1
         # 0 means loop forever, 1 (default) means
         # 0 means loop forever, 1 (default) means
         # play once. 2 or higher means play that many times
         # play once. 2 or higher means play that many times
@@ -69,7 +69,7 @@ class MusicBox(DirectObject):
         # Loading the open/close effect
         # Loading the open/close effect
         # loadSFX and loadMusic are identical. They are often used for organization
         # loadSFX and loadMusic are identical. They are often used for organization
         #(loadMusic is used for background music, loadSfx is used for other effects)
         #(loadMusic is used for background music, loadSfx is used for other effects)
-        self.lidSfx = base.loadSfx('music/openclose.ogg')
+        self.lidSfx = loader.loadSfx('music/openclose.ogg')
         # The open/close file has both effects in it. Fortunatly we can use intervals
         # The open/close file has both effects in it. Fortunatly we can use intervals
         # to easily define parts of a sound file to play
         # to easily define parts of a sound file to play
         self.lidOpenSfx = SoundInterval(self.lidSfx, duration=2, startTime=0)
         self.lidOpenSfx = SoundInterval(self.lidSfx, duration=2, startTime=0)