Browse Source

Merge branch 'master' into cmake

Sam Edwards 6 years ago
parent
commit
f8161cc529
100 changed files with 1679 additions and 796 deletions
  1. 5 0
      .travis.yml
  2. 28 0
      BACKERS.md
  3. 49 19
      README.md
  4. 1 1
      direct/src/actor/Actor.py
  5. 14 4
      direct/src/directscripts/Doxyfile.cxx
  6. 1 1
      direct/src/directscripts/Doxyfile.python
  7. 3 3
      direct/src/directscripts/gendocs.py
  8. 5 5
      direct/src/dist/FreezeTool.py
  9. 2 1
      direct/src/dist/commands.py
  10. 6 4
      direct/src/gui/DirectScrolledFrame.py
  11. 4 0
      direct/src/interval/cLerpNodePathInterval.I
  12. 2 2
      direct/src/interval/cLerpNodePathInterval.cxx
  13. 1 1
      direct/src/leveleditor/ActionMgr.py
  14. 1 1
      direct/src/leveleditor/CurveEditor.py
  15. 1 1
      direct/src/leveleditor/FileMgr.py
  16. 1 1
      direct/src/leveleditor/LayerEditorUI.py
  17. 2 2
      direct/src/leveleditor/LevelEditor.py
  18. 2 2
      direct/src/leveleditor/LevelEditorBase.py
  19. 1 1
      direct/src/leveleditor/LevelEditorStart.py
  20. 4 4
      direct/src/leveleditor/LevelEditorUIBase.py
  21. 1 1
      direct/src/leveleditor/ObjectMgrBase.py
  22. 1 1
      direct/src/leveleditor/ObjectPaletteUI.py
  23. 1 1
      direct/src/leveleditor/ObjectPropertyUI.py
  24. 2 2
      direct/src/leveleditor/PaletteTreeCtrl.py
  25. 1 1
      direct/src/leveleditor/ProtoObjsUI.py
  26. 3 3
      direct/src/leveleditor/ProtoPaletteUI.py
  27. 14 14
      direct/src/leveleditor/SceneGraphUIBase.py
  28. 1 1
      direct/src/leveleditor/testData.py
  29. 35 38
      direct/src/particles/ParticleEffect.py
  30. 1 0
      direct/src/particles/Particles.py
  31. 36 44
      direct/src/showbase/EventManager.py
  32. 0 100
      direct/src/showbase/FindCtaPaths.py
  33. 42 20
      direct/src/showbase/Loader.py
  34. 1 1
      direct/src/showbase/PythonUtil.py
  35. 1 1
      direct/src/showbase/ShowBase.py
  36. 9 7
      direct/src/showutil/TexMemWatcher.py
  37. 15 16
      direct/src/tkpanels/ParticlePanel.py
  38. 1 0
      direct/src/tkwidgets/Valuator.py
  39. 4 4
      direct/src/wxwidgets/WxPandaShell.py
  40. 53 0
      doc/ReleaseNotes
  41. 0 44
      doc/man/fltcopy.1
  42. 1 1
      dtool/PandaVersion.pp
  43. 1 1
      dtool/src/dtoolbase/atomicAdjustI386Impl.h
  44. 3 0
      dtool/src/dtoolbase/epvector.h
  45. 1 1
      dtool/src/dtoolbase/mutexImpl.h
  46. 39 3
      dtool/src/dtoolbase/mutexWin32Impl.I
  47. 400 4
      dtool/src/dtoolbase/mutexWin32Impl.cxx
  48. 55 4
      dtool/src/dtoolbase/mutexWin32Impl.h
  49. 2 1
      dtool/src/dtoolbase/pdeque.h
  50. 2 0
      dtool/src/dtoolbase/plist.h
  51. 4 0
      dtool/src/dtoolbase/pset.h
  52. 2 0
      dtool/src/dtoolbase/pvector.h
  53. 0 10
      dtool/src/dtoolbase/typeRegistry.I
  54. 48 50
      dtool/src/dtoolbase/typeRegistry.cxx
  55. 1 3
      dtool/src/dtoolbase/typeRegistry.h
  56. 3 0
      dtool/src/dtoolutil/load_dso.cxx
  57. 16 3
      dtool/src/interrogate/functionRemap.cxx
  58. 30 6
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  59. 1 1
      dtool/src/interrogate/interfaceMakerPythonNative.h
  60. 13 0
      dtool/src/interrogatedb/py_panda.I
  61. 2 0
      dtool/src/interrogatedb/py_panda.h
  62. 5 0
      dtool/src/interrogatedb/py_wrappers.cxx
  63. 46 0
      dtool/src/parser-inc/atomic
  64. 10 0
      dtool/src/parser-inc/synchapi.h
  65. 16 0
      dtool/src/parser-inc/winnt.h
  66. 6 9
      dtool/src/prc/configDeclaration.cxx
  67. 6 1
      dtool/src/prc/configPageManager.cxx
  68. 5 1
      dtool/src/prc/configVariableBool.cxx
  69. 5 6
      dtool/src/prc/configVariableFilename.cxx
  70. 5 6
      dtool/src/prc/configVariableString.cxx
  71. 15 4
      dtool/src/prc/streamReader.cxx
  72. 2 2
      dtool/src/prc/streamReader_ext.cxx
  73. 1 1
      makepanda/config.in
  74. 22 17
      makepanda/installpanda.py
  75. 9 9
      makepanda/makechm.py
  76. 36 17
      makepanda/makepackage.py
  77. 59 161
      makepanda/makepanda.py
  78. 0 37
      makepanda/makepanda.vcproj
  79. 130 20
      makepanda/makepandacore.py
  80. 29 6
      makepanda/makewheel.py
  81. 0 1
      makepanda/test_imports.py
  82. 2 9
      panda/metalibs/pandagl/pandagl.cxx
  83. 1 0
      panda/src/android/android_main.cxx
  84. 1 1
      panda/src/bullet/bulletRigidBodyNode.cxx
  85. 2 2
      panda/src/bullet/bulletVehicle.cxx
  86. 1 1
      panda/src/bullet/bulletVehicle.h
  87. 1 1
      panda/src/bullet/bulletWorld.h
  88. 2 2
      panda/src/chan/animControl.h
  89. 10 1
      panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm
  90. 4 1
      panda/src/cocoadisplay/cocoaGraphicsWindow.mm
  91. 0 1
      panda/src/cocoadisplay/cocoaPandaWindowDelegate.h
  92. 5 5
      panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm
  93. 0 1
      panda/src/collide/collisionBox.h
  94. 1 0
      panda/src/collide/collisionCapsule.h
  95. 115 0
      panda/src/collide/collisionInvSphere.cxx
  96. 4 0
      panda/src/collide/collisionInvSphere.h
  97. 1 0
      panda/src/collide/collisionNode.cxx
  98. 138 31
      panda/src/collide/collisionPolygon.cxx
  99. 2 1
      panda/src/collide/collisionPolygon.h
  100. 3 1
      panda/src/device/evdevInputDevice.cxx

+ 5 - 0
.travis.yml

@@ -113,3 +113,8 @@ notifications:
     on_failure: always
     on_failure: always
     use_notice: true
     use_notice: true
     skip_join: false
     skip_join: false
+  webhooks:
+    urls:
+      - https://www.panda3d.org/webhooks/travis-ci.php
+    on_success: change
+    on_failure: always

+ 28 - 0
BACKERS.md

@@ -0,0 +1,28 @@
+# Panda3D Backers
+
+This is a list of all the people who are contributing financially to Panda3D.  If you'd like to join them, visit [our campaign on OpenCollective](https://opencollective.com/panda3d)!
+
+## Gold Sponsors
+
+![Gold Sponsors](https://opencollective.com/panda3d/tiers/gold-sponsor.svg?avatarHeight=48&width=600)
+
+* [tcdude](https://opencollective.com/tizilogic)
+
+## Bronze Sponsors
+
+![Bronze Sponsors](https://opencollective.com/panda3d/tiers/bronze-sponsor.svg?avatarHeight=48&width=600)
+
+* [Mitchell Stokes](https://opencollective.com/mitchell-stokes)
+* [Daniel Stokes](https://opencollective.com/daniel-stokes)
+* [David Rose](https://opencollective.com/david-rose)
+* [Carnetsoft](https://cs-driving-simulator.com/)
+
+## Benefactors
+
+![Benefactors](https://opencollective.com/panda3d/tiers/benefactor.svg?avatarHeight=48&width=600)
+
+* Sam Edwards
+
+## Backers
+
+![Backers](https://opencollective.com/panda3d/tiers/backer.svg?avatarHeight=48&width=600)

+ 49 - 19
README.md

@@ -1,6 +1,8 @@
 [![Build Status](https://travis-ci.org/panda3d/panda3d.svg?branch=master)](https://travis-ci.org/panda3d/panda3d)
 [![Build Status](https://travis-ci.org/panda3d/panda3d.svg?branch=master)](https://travis-ci.org/panda3d/panda3d)
+[![OpenCollective](https://opencollective.com/panda3d/backers/badge.svg)](https://opencollective.com/panda3d)
+[![OpenCollective](https://opencollective.com/panda3d/sponsors/badge.svg)](https://opencollective.com/panda3d)
 
 
-<img src="https://avatars2.githubusercontent.com/u/590956?v=3&s=200" align="right" />
+<img src="https://avatars2.githubusercontent.com/u/590956?v=3&s=500" align="right" width="200"/>
 
 
 Panda3D
 Panda3D
 =======
 =======
@@ -8,12 +10,12 @@ Panda3D
 Panda3D is a game engine, a framework for 3D rendering and game development for
 Panda3D is a game engine, a framework for 3D rendering and game development for
 Python and C++ programs.  Panda3D is open-source and free for any purpose,
 Python and C++ programs.  Panda3D is open-source and free for any purpose,
 including commercial ventures, thanks to its
 including commercial ventures, thanks to its
-[liberal license](https://www.panda3d.org/license.php).  To learn more about
-Panda3D's capabilities, visit the [gallery](https://www.panda3d.org/gallery.php)
-and the [feature list](https://www.panda3d.org/features.php).  To learn how to
-use Panda3D, check the [documentation](https://www.panda3d.org/documentation.php)
+[liberal license](https://www.panda3d.org/license/). To learn more about
+Panda3D's capabilities, visit the [gallery](https://www.panda3d.org/gallery/)
+and the [feature list](https://www.panda3d.org/features/).  To learn how to
+use Panda3D, check the [documentation](https://www.panda3d.org/documentation/)
 resources. If you get stuck, ask for help from our active
 resources. If you get stuck, ask for help from our active
-[community](https://www.panda3d.org/community.php).
+[community](https://discourse.panda3d.org).
 
 
 Panda3D is licensed under the Modified BSD License.  See the LICENSE file for
 Panda3D is licensed under the Modified BSD License.  See the LICENSE file for
 more details.
 more details.
@@ -21,7 +23,16 @@ more details.
 Installing Panda3D
 Installing Panda3D
 ==================
 ==================
 
 
-By far, the easiest way to install the latest development build of Panda3D
+The latest Panda3D SDK can be downloaded from
+[this page](https://www.panda3d.org/download/sdk-1-10-1/).
+If you are familiar with installing Python packages, you can use
+the following comand:
+
+```bash
+pip install panda3d
+```
+
+The easiest way to install the latest development build of Panda3D
 into an existing Python installation is using the following command:
 into an existing Python installation is using the following command:
 
 
 ```bash
 ```bash
@@ -31,9 +42,7 @@ pip install --pre --extra-index-url https://archive.panda3d.org/ panda3d
 If this command fails, please make sure your version of pip is up-to-date.
 If this command fails, please make sure your version of pip is up-to-date.
 
 
 If you prefer to install the full SDK with all tools, the latest development
 If you prefer to install the full SDK with all tools, the latest development
-builds can be obtained from this page:
-
-https://www.panda3d.org/download.php?sdk&version=devel
+builds can be obtained from [this page](https://www.panda3d.org/download.php?version=devel&sdk).
 
 
 These are automatically kept up-to-date with the latest GitHub version of Panda.
 These are automatically kept up-to-date with the latest GitHub version of Panda.
 
 
@@ -55,8 +64,8 @@ depending on whether you are on a 32-bit or 64-bit system, or you can
 [click here](https://github.com/rdb/panda3d-thirdparty) for instructions on
 [click here](https://github.com/rdb/panda3d-thirdparty) for instructions on
 building them from source.
 building them from source.
 
 
-https://www.panda3d.org/download/panda3d-1.10.0/panda3d-1.10.0-tools-win64.zip
-https://www.panda3d.org/download/panda3d-1.10.0/panda3d-1.10.0-tools-win32.zip
+https://www.panda3d.org/download/panda3d-1.10.2/panda3d-1.10.2-tools-win64.zip
+https://www.panda3d.org/download/panda3d-1.10.2/panda3d-1.10.2-tools-win32.zip
 
 
 After acquiring these dependencies, you may simply build Panda3D from the
 After acquiring these dependencies, you may simply build Panda3D from the
 command prompt using the following command.  (Change `14.1` to `14` if you are
 command prompt using the following command.  (Change `14.1` to `14` if you are
@@ -96,14 +105,14 @@ python makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-g
 You will probably see some warnings saying that it's unable to find several
 You will probably see some warnings saying that it's unable to find several
 dependency packages.  You should determine which ones you want to include in
 dependency packages.  You should determine which ones you want to include in
 your build and install the respective development packages.  You may visit
 your build and install the respective development packages.  You may visit
-[this manual page](https://www.panda3d.org/manual/index.php/Dependencies)
+[this manual page](https://www.panda3d.org/manual/?title=Third-party_dependencies_and_license_info)
 for an overview of the various dependencies.
 for an overview of the various dependencies.
 
 
 If you are on Ubuntu, this command should cover the most frequently
 If you are on Ubuntu, this command should cover the most frequently
 used third-party packages:
 used third-party packages:
 
 
 ```bash
 ```bash
-sudo apt-get install build-essential pkg-config python-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev libassimp-dev libopenexr-dev
+sudo apt-get install build-essential pkg-config fakeroot python-dev libpng-dev libjpeg-dev libtiff-dev zlib1g-dev libssl-dev libx11-dev libgl1-mesa-dev libxrandr-dev libxxf86dga-dev libxcursor-dev bison flex libfreetype6-dev libvorbis-dev libeigen3-dev libopenal-dev libode-dev libbullet-dev nvidia-cg-toolkit libgtk2.0-dev libassimp-dev libopenexr-dev
 ```
 ```
 
 
 Once Panda3D has built, you can either install the .deb or .rpm package that
 Once Panda3D has built, you can either install the .deb or .rpm package that
@@ -126,7 +135,7 @@ macOS
 -----
 -----
 
 
 On macOS, you will need to download a set of precompiled thirdparty packages in order to
 On macOS, you will need to download a set of precompiled thirdparty packages in order to
-compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-mac.tar.gz).
+compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.2/panda3d-1.10.2-tools-mac.tar.gz).
 
 
 After placing the thirdparty directory inside the panda3d source directory,
 After placing the thirdparty directory inside the panda3d source directory,
 you may build Panda3D using a command like the following:
 you may build Panda3D using a command like the following:
@@ -135,9 +144,8 @@ you may build Panda3D using a command like the following:
 python makepanda/makepanda.py --everything --installer
 python makepanda/makepanda.py --everything --installer
 ```
 ```
 
 
-In order to make a universal build, pass the --universal flag.  You may also
-target a specific minimum macOS version using the --osxtarget flag followed
-by the release number, eg. 10.6 or 10.7.
+You may target a specific minimum macOS version using the --osxtarget flag
+followed by the release number, eg. 10.7 or 10.9.
 
 
 If the build was successful, makepanda will have generated a .dmg file in
 If the build was successful, makepanda will have generated a .dmg file in
 the source directory containing the installer.  Simply open it and run the
 the source directory containing the installer.  Simply open it and run the
@@ -151,7 +159,7 @@ install the requisite packages using the system package manager.  To install
 the recommended set of dependencies, you can use this command:
 the recommended set of dependencies, you can use this command:
 
 
 ```bash
 ```bash
-pkg install pkgconf png jpeg-turbo tiff freetype2 eigen squish openal opusfile libvorbis libX11 libGL ode bullet assimp openexr
+pkg install pkgconf bison png jpeg-turbo tiff freetype2 harfbuzz eigen squish openal opusfile libvorbis libX11 mesa-libs ode bullet assimp openexr
 ```
 ```
 
 
 You will also need to choose which version of Python you want to use.
 You will also need to choose which version of Python you want to use.
@@ -222,3 +230,25 @@ models that are necessary for the developers to reproduce the issue.
 
 
 If you're not sure whether you've encountered a bug, feel free to ask about
 If you're not sure whether you've encountered a bug, feel free to ask about
 it in the forums or the IRC channel first.
 it in the forums or the IRC channel first.
+
+Supporting the Project
+======================
+
+If you would like to support the project financially, visit
+[our campaign on OpenCollective](https://opencollective.com/panda3d).  Your
+contributions help us accelerate the development of Panda3D.
+
+For the list of backers, see the [BACKERS.md](BACKERS.md) file or visit the
+[Sponsors page](https://www.panda3d.org/sponsors) on our web site.  Thank you
+to everyone who has donated!
+
+<a href="https://opencollective.com/panda3d" target="_blank">
+  <img src="https://opencollective.com/panda3d/contribute/[email protected]?color=blue" width=300 />
+</a>
+
+### Gold Sponsors
+[![](https://opencollective.com/panda3d/tiers/gold-sponsor/0/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/0/website)
+[![](https://opencollective.com/panda3d/tiers/gold-sponsor/1/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/1/website)
+[![](https://opencollective.com/panda3d/tiers/gold-sponsor/2/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/2/website)
+[![](https://opencollective.com/panda3d/tiers/gold-sponsor/3/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/3/website)
+[![](https://opencollective.com/panda3d/tiers/gold-sponsor/4/avatar.svg?avatarHeight=128)](https://opencollective.com/panda3d/tiers/gold-sponsor/4/website)

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

@@ -2507,7 +2507,7 @@ class Actor(DirectObject, NodePath):
         else:
         else:
             animNames = [animName]
             animNames = [animName]
         for animName in animNames:
         for animName in animNames:
-            if animName is 'nothing':
+            if animName == 'nothing':
                 continue
                 continue
             thisAnim = ''
             thisAnim = ''
             totalEffect = 0.
             totalEffect = 0.

+ 14 - 4
direct/src/directscripts/Doxyfile.cxx

@@ -432,7 +432,7 @@ EXTRACT_STATIC         = NO
 # for Java sources.
 # for Java sources.
 # The default value is: YES.
 # The default value is: YES.
 
 
-EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_CLASSES  = NO
 
 
 # This flag is only useful for Objective-C code. When set to YES local methods,
 # This flag is only useful for Objective-C code. When set to YES local methods,
 # which are defined in the implementation section but not in the interface are
 # which are defined in the implementation section but not in the interface are
@@ -802,6 +802,11 @@ EXCLUDE                = dtool/src/parser-inc \
                          panda/src/iphone \
                          panda/src/iphone \
                          panda/src/tinydisplay \
                          panda/src/tinydisplay \
                          panda/src/movies/dr_flac.h \
                          panda/src/movies/dr_flac.h \
+                         panda/src/windisplay/winDetectDx.h \
+                         panda/src/wgldisplay/wglext.h \
+                         panda/src/glxdisplay/panda_glxext.h \
+                         pandatool/src/gtk-stats \
+                         dtool/src/dtoolbase/fakestringstream.h \
                          dtool/src/dtoolbase/pdtoa.cxx \
                          dtool/src/dtoolbase/pdtoa.cxx \
                          dtool/src/dtoolutil/panda_getopt_long.h \
                          dtool/src/dtoolutil/panda_getopt_long.h \
                          dtool/src/dtoolutil/panda_getopt_impl.h \
                          dtool/src/dtoolutil/panda_getopt_impl.h \
@@ -840,7 +845,10 @@ EXCLUDE_PATTERNS       = */Opt*-*/* \
 # Note that the wildcards are matched against the file with absolute path, so to
 # Note that the wildcards are matched against the file with absolute path, so to
 # exclude all test directories use the pattern */test/*
 # exclude all test directories use the pattern */test/*
 
 
-EXCLUDE_SYMBOLS        =
+EXCLUDE_SYMBOLS        = InterrogateFunctionWrapper::Parameter \
+                         CollisionFloorMesh::TriangleIndices \
+                         tagTOUCHINPUT \
+                         WINDOW_METRICS
 
 
 # The EXAMPLE_PATH tag can be used to specify one or more files or directories
 # The EXAMPLE_PATH tag can be used to specify one or more files or directories
 # that contain example code fragments that are included (see the \include
 # that contain example code fragments that are included (see the \include
@@ -1018,7 +1026,7 @@ ALPHABETICAL_INDEX     = YES
 # Minimum value: 1, maximum value: 20, default value: 5.
 # Minimum value: 1, maximum value: 20, default value: 5.
 # This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
 # This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
 
 
-COLS_IN_ALPHA_INDEX    = 5
+COLS_IN_ALPHA_INDEX    = 3
 
 
 # In case all classes in a project start with a common prefix, all classes will
 # In case all classes in a project start with a common prefix, all classes will
 # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
 # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
@@ -1972,6 +1980,7 @@ INCLUDE_FILE_PATTERNS  =
 
 
 PREDEFINED             = TVOLATILE= \
 PREDEFINED             = TVOLATILE= \
                          INLINE=inline \
                          INLINE=inline \
+                         ALWAYS_INLINE=inline \
                          PUBLISHED=public \
                          PUBLISHED=public \
                          protected=private \
                          protected=private \
                          INLINE_LINMATH=inline \
                          INLINE_LINMATH=inline \
@@ -1986,7 +1995,8 @@ PREDEFINED             = TVOLATILE= \
                          MAKE_SEQ(x)= \
                          MAKE_SEQ(x)= \
                          MAKE_SEQ_PROPERTY(x)= \
                          MAKE_SEQ_PROPERTY(x)= \
                          MAKE_MAP_PROPERTY(x)= \
                          MAKE_MAP_PROPERTY(x)= \
-                         MAKE_MAP_KEYS_SEQ(x)=
+                         MAKE_MAP_KEYS_SEQ(x)= \
+                         RETURNS_ALIGNED(x)=
 
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
 # tag can be used to specify a list of macro names that should be expanded. The

+ 1 - 1
direct/src/directscripts/Doxyfile.python

@@ -805,7 +805,7 @@ ALPHABETICAL_INDEX     = YES
 # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
 # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
 # in which this list will be split (can be a number in the range [1..20])
 # in which this list will be split (can be a number in the range [1..20])
 
 
-COLS_IN_ALPHA_INDEX    = 5
+COLS_IN_ALPHA_INDEX    = 2
 
 
 # In case all classes in a project start with a common prefix, all 
 # In case all classes in a project start with a common prefix, all 
 # classes will be put under the same header in the alphabetical index. 
 # classes will be put under the same header in the alphabetical index. 

+ 3 - 3
direct/src/directscripts/gendocs.py

@@ -56,9 +56,9 @@ import os, sys, parser, symbol, token, re
 #
 #
 ########################################################################
 ########################################################################
 
 
-SECHEADER = re.compile("^[A-Z][a-z]+\s*:")
-JUNKHEADER = re.compile("^((Function)|(Access))\s*:")
-IMPORTSTAR = re.compile("^from\s+([a-zA-Z0-9_.]+)\s+import\s+[*]\s*$")
+SECHEADER = re.compile("^[A-Z][a-z]+\\s*:")
+JUNKHEADER = re.compile("^((Function)|(Access))\\s*:")
+IMPORTSTAR = re.compile("^from\\s+([a-zA-Z0-9_.]+)\\s+import\\s+[*]\\s*$")
 IDENTIFIER = re.compile("[a-zA-Z0-9_]+")
 IDENTIFIER = re.compile("[a-zA-Z0-9_]+")
 FILEHEADER = re.compile(
 FILEHEADER = re.compile(
 r"""^// Filename: [a-zA-Z.]+
 r"""^// Filename: [a-zA-Z.]+

+ 5 - 5
direct/src/dist/FreezeTool.py

@@ -210,15 +210,15 @@ class CompilationEnvironment:
             if ('MAKEPANDA' in os.environ):
             if ('MAKEPANDA' in os.environ):
                 self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" %(filename)s'
                 self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" %(filename)s'
                 self.compileObjDll = self.compileObjExe
                 self.compileObjDll = self.compileObjExe
-                self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(python)s\libs"  /out:%(basename)s.exe %(basename)s.obj'
-                self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(python)s\libs"  /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
+                self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(python)s\\libs"  /out:%(basename)s.exe %(basename)s.obj'
+                self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(python)s\\libs"  /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
             else:
             else:
                 os.environ['PATH'] += ';' + self.MSVC + '\\bin' + self.suffix64 + ';' + self.MSVC + '\\Common7\\IDE;' + self.PSDK + '\\bin'
                 os.environ['PATH'] += ';' + self.MSVC + '\\bin' + self.suffix64 + ';' + self.MSVC + '\\Common7\\IDE;' + self.PSDK + '\\bin'
 
 
-                self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" /I"%(PSDK)s\include" /I"%(MSVC)s\include" %(filename)s'
+                self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" /I"%(PSDK)s\\include" /I"%(MSVC)s\\include" %(filename)s'
                 self.compileObjDll = self.compileObjExe
                 self.compileObjDll = self.compileObjExe
-                self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\libs"  /out:%(basename)s.exe %(basename)s.obj'
-                self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\libs"  /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
+                self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\\libs"  /out:%(basename)s.exe %(basename)s.obj'
+                self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\\libs"  /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
 
 
         elif self.platform.startswith('osx_'):
         elif self.platform.startswith('osx_'):
             # OSX
             # OSX

+ 2 - 1
direct/src/dist/commands.py

@@ -1347,7 +1347,8 @@ class bdist_apps(setuptools.Command):
 
 
         platforms = build_cmd.platforms
         platforms = build_cmd.platforms
         build_base = os.path.abspath(build_cmd.build_base)
         build_base = os.path.abspath(build_cmd.build_base)
-        os.makedirs(self.dist_dir, exist_ok=True)
+        if not os.path.exists(self.dist_dir):
+            os.makedirs(self.dist_dir)
         os.chdir(self.dist_dir)
         os.chdir(self.dist_dir)
 
 
         for platform in platforms:
         for platform in platforms:

+ 6 - 4
direct/src/gui/DirectScrolledFrame.py

@@ -100,8 +100,10 @@ class DirectScrolledFrame(DirectFrame):
                 simpleChildGui = self.guiDict.get(parts[-1])
                 simpleChildGui = self.guiDict.get(parts[-1])
                 if simpleChildGui:
                 if simpleChildGui:
                     simpleChildGui.destroy()
                     simpleChildGui.destroy()
-        self.verticalScroll.destroy()
-        self.horizontalScroll.destroy()
-        del self.verticalScroll
-        del self.horizontalScroll
+        if self.verticalScroll:
+            self.verticalScroll.destroy()
+        if self.horizontalScroll:
+            self.horizontalScroll.destroy()
+        self.verticalScroll = None
+        self.horizontalScroll = None
         DirectFrame.destroy(self)
         DirectFrame.destroy(self)

+ 4 - 0
direct/src/interval/cLerpNodePathInterval.I

@@ -106,6 +106,8 @@ set_end_hpr(const LQuaternion &quat) {
  * if either set_end_quat() or set_end_hpr() is also called.  This parameter
  * if either set_end_quat() or set_end_hpr() is also called.  This parameter
  * is optional; if unspecified, the value will be taken from the node's actual
  * is optional; if unspecified, the value will be taken from the node's actual
  * rotation at the time the lerp is performed.
  * rotation at the time the lerp is performed.
+ *
+ * The given quaternion needs to be normalized.
  */
  */
 INLINE void CLerpNodePathInterval::
 INLINE void CLerpNodePathInterval::
 set_start_quat(const LQuaternion &quat) {
 set_start_quat(const LQuaternion &quat) {
@@ -143,6 +145,8 @@ set_end_quat(const LVecBase3 &hpr) {
  * This replaces a previous call to set_end_hpr().  If neither set_end_quat()
  * This replaces a previous call to set_end_hpr().  If neither set_end_quat()
  * nor set_end_hpr() is called, the node's rotation will not be affected by
  * nor set_end_hpr() is called, the node's rotation will not be affected by
  * the lerp.
  * the lerp.
+ *
+ * The given quaternion needs to be normalized.
  */
  */
 INLINE void CLerpNodePathInterval::
 INLINE void CLerpNodePathInterval::
 set_end_quat(const LQuaternion &quat) {
 set_end_quat(const LQuaternion &quat) {

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

@@ -174,14 +174,14 @@ priv_step(double t) {
           setup_slerp();
           setup_slerp();
 
 
         } else if ((_flags & F_bake_in_start) != 0) {
         } else if ((_flags & F_bake_in_start) != 0) {
-          set_start_quat(transform->get_quat());
+          set_start_quat(transform->get_norm_quat());
           setup_slerp();
           setup_slerp();
 
 
         } else {
         } else {
           if (_prev_d == 1.0) {
           if (_prev_d == 1.0) {
             _start_quat = _end_quat;
             _start_quat = _end_quat;
           } else {
           } else {
-            LQuaternion prev_value = transform->get_quat();
+            LQuaternion prev_value = transform->get_norm_quat();
             _start_quat = (prev_value - _prev_d * _end_quat) / (1.0 - _prev_d);
             _start_quat = (prev_value - _prev_d * _end_quat) / (1.0 - _prev_d);
           }
           }
           setup_slerp();
           setup_slerp();

+ 1 - 1
direct/src/leveleditor/ActionMgr.py

@@ -1,4 +1,4 @@
-from pandac.PandaModules import *
+from panda3d.core import *
 from . import ObjectGlobals as OG
 from . import ObjectGlobals as OG
 
 
 class ActionMgr:
 class ActionMgr:

+ 1 - 1
direct/src/leveleditor/CurveEditor.py

@@ -2,7 +2,7 @@
 This is the module for curve edit
 This is the module for curve edit
 """
 """
 
 
-from pandac.PandaModules import *
+from panda3d.core import *
 from direct.wxwidgets.WxPandaShell import *
 from direct.wxwidgets.WxPandaShell import *
 from direct.showbase.DirectObject import *
 from direct.showbase.DirectObject import *
 from direct.directtools.DirectSelection import SelectionRay
 from direct.directtools.DirectSelection import SelectionRay

+ 1 - 1
direct/src/leveleditor/FileMgr.py

@@ -11,7 +11,7 @@ class FileMgr:
     def saveToFile(self, fileName):
     def saveToFile(self, fileName):
         try:
         try:
             f = open(fileName, 'w')
             f = open(fileName, 'w')
-            f.write("from pandac.PandaModules import *\n")
+            f.write("from panda3d.core import *\n")
             f.write("\nif hasattr(base, 'le'):\n")
             f.write("\nif hasattr(base, 'le'):\n")
             f.write("    objectMgr = base.le.objectMgr\n")
             f.write("    objectMgr = base.le.objectMgr\n")
             f.write("    animMgr = base.le.animMgr\n")
             f.write("    animMgr = base.le.animMgr\n")

+ 1 - 1
direct/src/leveleditor/LayerEditorUI.py

@@ -2,7 +2,7 @@
 Defines Layer UI
 Defines Layer UI
 """
 """
 import wx
 import wx
-from pandac.PandaModules import *
+from panda3d.core import *
 
 
 from . import ObjectGlobals as OG
 from . import ObjectGlobals as OG
 
 

+ 2 - 2
direct/src/leveleditor/LevelEditor.py

@@ -31,7 +31,7 @@ class LevelEditor(LevelEditorBase):
 
 
         # Populating uderlined data-structures
         # Populating uderlined data-structures
         self.ui = LevelEditorUI(self)
         self.ui = LevelEditorUI(self)
-        self.ui.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+        self.ui.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
         self.objectPalette.populate()
         self.objectPalette.populate()
         self.protoPalette.populate()
         self.protoPalette.populate()
 
 
@@ -42,4 +42,4 @@ class LevelEditor(LevelEditorBase):
         # When you define your own LevelEditor class inheriting LevelEditorBase
         # When you define your own LevelEditor class inheriting LevelEditorBase
         # you should call self.initialize() at the end of __init__() function
         # you should call self.initialize() at the end of __init__() function
         self.initialize()
         self.initialize()
-        self.ui.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
+        self.ui.SetCursor(wx.Cursor(wx.CURSOR_ARROW))

+ 2 - 2
direct/src/leveleditor/LevelEditorBase.py

@@ -305,7 +305,7 @@ class LevelEditorBase(DirectObject):
         if self.settingsFile is None:
         if self.settingsFile is None:
             return
             return
 
 
-        self.ui.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+        self.ui.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
         try:
         try:
             f = open(self.settingsFile, 'r')
             f = open(self.settingsFile, 'r')
             configLines = f.readlines()
             configLines = f.readlines()
@@ -342,7 +342,7 @@ class LevelEditorBase(DirectObject):
             self.ui.updateMenu()
             self.ui.updateMenu()
         except:
         except:
             pass
             pass
-        self.ui.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
+        self.ui.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
 
 
     def convertMaya(self, modelname, callBack, obj=None, isAnim=False):
     def convertMaya(self, modelname, callBack, obj=None, isAnim=False):
         if obj and isAnim:
         if obj and isAnim:

+ 1 - 1
direct/src/leveleditor/LevelEditorStart.py

@@ -1,4 +1,4 @@
-from . import LevelEditor
+from direct.leveleditor import LevelEditor
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
     base.le = LevelEditor.LevelEditor()
     base.le = LevelEditor.LevelEditor()

+ 4 - 4
direct/src/leveleditor/LevelEditorUIBase.py

@@ -2,7 +2,7 @@
 ## import os
 ## import os
 ## from wx.lib.agw import fourwaysplitter as FWS
 ## from wx.lib.agw import fourwaysplitter as FWS
 
 
-from pandac.PandaModules import *
+from panda3d.core import *
 from direct.wxwidgets.WxPandaShell import *
 from direct.wxwidgets.WxPandaShell import *
 from direct.directtools.DirectSelection import SelectionRay
 from direct.directtools.DirectSelection import SelectionRay
 
 
@@ -478,7 +478,7 @@ class LevelEditorUIBase(WxPandaShell):
         self.editor.reset()
         self.editor.reset()
 
 
     def onOpen(self, evt=None):
     def onOpen(self, evt=None):
-        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", wx.OPEN)
+        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
         if dialog.ShowModal() == wx.ID_OK:
         if dialog.ShowModal() == wx.ID_OK:
             self.editor.load(dialog.GetPath())
             self.editor.load(dialog.GetPath())
             self.editor.setTitleWithFilename(dialog.GetPath())
             self.editor.setTitleWithFilename(dialog.GetPath())
@@ -492,7 +492,7 @@ class LevelEditorUIBase(WxPandaShell):
             self.editor.save()
             self.editor.save()
 
 
     def onSaveAs(self, evt):
     def onSaveAs(self, evt):
-        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", wx.SAVE)
+        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.py", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
         result = True
         result = True
         if dialog.ShowModal() == wx.ID_OK:
         if dialog.ShowModal() == wx.ID_OK:
             self.editor.saveAs(dialog.GetPath())
             self.editor.saveAs(dialog.GetPath())
@@ -503,7 +503,7 @@ class LevelEditorUIBase(WxPandaShell):
         return result
         return result
 
 
     def onExportToMaya(self, evt):
     def onExportToMaya(self, evt):
-        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.mb", wx.SAVE)
+        dialog = wx.FileDialog(None, "Choose a file", os.getcwd(), "", "*.mb", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
         if dialog.ShowModal() == wx.ID_OK:
         if dialog.ShowModal() == wx.ID_OK:
             self.editor.exportToMaya(dialog.GetPath())
             self.editor.exportToMaya(dialog.GetPath())
         dialog.Destroy()
         dialog.Destroy()

+ 1 - 1
direct/src/leveleditor/ObjectMgrBase.py

@@ -6,7 +6,7 @@ import os, time, copy
 
 
 from direct.task import Task
 from direct.task import Task
 from direct.actor.Actor import Actor
 from direct.actor.Actor import Actor
-from pandac.PandaModules import *
+from panda3d.core import *
 from .ActionMgr import *
 from .ActionMgr import *
 from . import ObjectGlobals as OG
 from . import ObjectGlobals as OG
 
 

+ 1 - 1
direct/src/leveleditor/ObjectPaletteUI.py

@@ -69,4 +69,4 @@ class ObjectPaletteUI(wx.Panel):
         return cmp(index1, index2)
         return cmp(index1, index2)
 
 
     def getSelected(self):
     def getSelected(self):
-        return self.tree.GetItemPyData(self.tree.GetSelection())
+        return self.tree.GetItemData(self.tree.GetSelection())

+ 1 - 1
direct/src/leveleditor/ObjectPropertyUI.py

@@ -9,7 +9,7 @@ from wx.lib.embeddedimage import PyEmbeddedImage
 from wx.lib.scrolledpanel import ScrolledPanel
 from wx.lib.scrolledpanel import ScrolledPanel
 from wx.lib.agw.cubecolourdialog import *
 from wx.lib.agw.cubecolourdialog import *
 from direct.wxwidgets.WxSlider import *
 from direct.wxwidgets.WxSlider import *
-from pandac.PandaModules import *
+from panda3d.core import *
 from . import ObjectGlobals as OG
 from . import ObjectGlobals as OG
 from . import AnimGlobals as AG
 from . import AnimGlobals as AG
 
 

+ 2 - 2
direct/src/leveleditor/PaletteTreeCtrl.py

@@ -41,7 +41,7 @@ class PaletteTreeCtrl(wx.TreeCtrl):
                roots.append(key)
                roots.append(key)
         for root in roots:
         for root in roots:
             newItem = self.AppendItem(parentItem, root)
             newItem = self.AppendItem(parentItem, root)
-            self.SetItemPyData(newItem, root)
+            self.SetItemData(newItem, root)
             rootItems.append(newItem)
             rootItems.append(newItem)
             itemKeys.remove(root)
             itemKeys.remove(root)
         for rootItem in rootItems:
         for rootItem in rootItems:
@@ -108,7 +108,7 @@ class PaletteTreeCtrl(wx.TreeCtrl):
         item, cookie = self.GetFirstChild(parent)
         item, cookie = self.GetFirstChild(parent)
         while item:
         while item:
            itemName = self.GetItemText(item)
            itemName = self.GetItemText(item)
-           itemData = self.GetItemPyData(item)
+           itemData = self.GetItemData(item)
 
 
            newItem = self.AppendItem(newParent, itemName)
            newItem = self.AppendItem(newParent, itemName)
            self.SetItemPyData(newItem, itemData)
            self.SetItemPyData(newItem, itemData)

+ 1 - 1
direct/src/leveleditor/ProtoObjsUI.py

@@ -4,7 +4,7 @@ Defines ProtoObjs List UI
 import wx
 import wx
 import os
 import os
 
 
-from pandac.PandaModules import *
+from panda3d.core import *
 from .ProtoObjs import *
 from .ProtoObjs import *
 
 
 class ProtoDropTarget(wx.PyDropTarget):
 class ProtoDropTarget(wx.PyDropTarget):

+ 3 - 3
direct/src/leveleditor/ProtoPaletteUI.py

@@ -3,13 +3,13 @@ Defines ProtoPalette tree UI
 """
 """
 import wx
 import wx
 import os
 import os
-from pandac.PandaModules import *
+from panda3d.core import *
 from .PaletteTreeCtrl import *
 from .PaletteTreeCtrl import *
 
 
-class UniversalDropTarget(wx.PyDropTarget):
+class UniversalDropTarget(wx.DropTarget):
    """Implements drop target functionality to receive files, bitmaps and text"""
    """Implements drop target functionality to receive files, bitmaps and text"""
    def __init__(self, editor):
    def __init__(self, editor):
-       wx.PyDropTarget.__init__(self)
+       wx.DropTarget.__init__(self)
        self.editor = editor
        self.editor = editor
        self.do = wx.DataObjectComposite()  # the dataobject that gets filled with the appropriate data
        self.do = wx.DataObjectComposite()  # the dataobject that gets filled with the appropriate data
        self.filedo = wx.FileDataObject()
        self.filedo = wx.FileDataObject()

+ 14 - 14
direct/src/leveleditor/SceneGraphUIBase.py

@@ -2,7 +2,7 @@
 Defines Scene Graph tree UI Base
 Defines Scene Graph tree UI Base
 """
 """
 import wx
 import wx
-from pandac.PandaModules import *
+from panda3d.core import *
 from .ActionMgr import *
 from .ActionMgr import *
 
 
 from . import ObjectGlobals as OG
 from . import ObjectGlobals as OG
@@ -26,7 +26,7 @@ class SceneGraphUIBase(wx.Panel):
                   size=wx.DefaultSize, style=wx.TR_MULTIPLE|wx.TR_DEFAULT_STYLE,
                   size=wx.DefaultSize, style=wx.TR_MULTIPLE|wx.TR_DEFAULT_STYLE,
                   validator=wx.DefaultValidator, name="treeCtrl")
                   validator=wx.DefaultValidator, name="treeCtrl")
         self.root = self.tree.AddRoot('render')
         self.root = self.tree.AddRoot('render')
-        self.tree.SetItemPyData(self.root, "render")
+        self.tree.SetItemData(self.root, "render")
 
 
         self.shouldShowPandaObjChildren = False
         self.shouldShowPandaObjChildren = False
 
 
@@ -61,7 +61,7 @@ class SceneGraphUIBase(wx.Panel):
             self.tree.Delete(item)
             self.tree.Delete(item)
 
 
     def traversePandaObjects(self, parent, objNodePath):
     def traversePandaObjects(self, parent, objNodePath):
-        itemId = self.tree.GetItemPyData(parent)
+        itemId = self.tree.GetItemData(parent)
         i = 0
         i = 0
         for child in objNodePath.getChildren():
         for child in objNodePath.getChildren():
             if child.hasTag('OBJRoot'):
             if child.hasTag('OBJRoot'):
@@ -78,7 +78,7 @@ class SceneGraphUIBase(wx.Panel):
 
 
     def addPandaObjectChildren(self, parent):
     def addPandaObjectChildren(self, parent):
         # first, find Panda Object's NodePath of the item
         # first, find Panda Object's NodePath of the item
-        itemId = self.tree.GetItemPyData(parent)
+        itemId = self.tree.GetItemData(parent)
         if itemId == "render":
         if itemId == "render":
            return
            return
         obj = self.editor.objectMgr.findObjectById(itemId)
         obj = self.editor.objectMgr.findObjectById(itemId)
@@ -96,7 +96,7 @@ class SceneGraphUIBase(wx.Panel):
 
 
     def removePandaObjectChildren(self, parent):
     def removePandaObjectChildren(self, parent):
         # first, find Panda Object's NodePath of the item
         # first, find Panda Object's NodePath of the item
-        itemId = self.tree.GetItemPyData(parent)
+        itemId = self.tree.GetItemData(parent)
         if itemId == "render":
         if itemId == "render":
            return
            return
         obj = self.editor.objectMgr.findObjectById(itemId)
         obj = self.editor.objectMgr.findObjectById(itemId)
@@ -142,14 +142,14 @@ class SceneGraphUIBase(wx.Panel):
 
 
     def traverse(self, parent, itemId):
     def traverse(self, parent, itemId):
         # prevent from traversing into self
         # prevent from traversing into self
-        if itemId == self.tree.GetItemPyData(parent):
+        if itemId == self.tree.GetItemData(parent):
            return None
            return None
 
 
         # main loop - serching for an item with an itemId
         # main loop - serching for an item with an itemId
         item, cookie = self.tree.GetFirstChild(parent)
         item, cookie = self.tree.GetFirstChild(parent)
         while item:
         while item:
               # if the item was found - return it
               # if the item was found - return it
-              if itemId == self.tree.GetItemPyData(item):
+              if itemId == self.tree.GetItemData(item):
                  return item
                  return item
 
 
               # the tem was not found - checking if it has children
               # the tem was not found - checking if it has children
@@ -168,7 +168,7 @@ class SceneGraphUIBase(wx.Panel):
         item, cookie = self.tree.GetFirstChild(parent)
         item, cookie = self.tree.GetFirstChild(parent)
         while item:
         while item:
            data = self.tree.GetItemText(item)
            data = self.tree.GetItemText(item)
-           itemId = self.tree.GetItemPyData(item)
+           itemId = self.tree.GetItemData(item)
            newItem = self.tree.AppendItem(newParent, data)
            newItem = self.tree.AppendItem(newParent, data)
            self.tree.SetItemPyData(newItem, itemId)
            self.tree.SetItemPyData(newItem, itemId)
 
 
@@ -186,13 +186,13 @@ class SceneGraphUIBase(wx.Panel):
     def reParent(self, oldParent, newParent, child):
     def reParent(self, oldParent, newParent, child):
         if newParent is None:
         if newParent is None:
            newParent = self.root
            newParent = self.root
-        itemId = self.tree.GetItemPyData(oldParent)
+        itemId = self.tree.GetItemData(oldParent)
         newItem = self.tree.AppendItem(newParent, child)
         newItem = self.tree.AppendItem(newParent, child)
         self.tree.SetItemPyData(newItem, itemId)
         self.tree.SetItemPyData(newItem, itemId)
         self.reParentTree(oldParent, newItem)
         self.reParentTree(oldParent, newItem)
 
 
         obj = self.editor.objectMgr.findObjectById(itemId)
         obj = self.editor.objectMgr.findObjectById(itemId)
-        itemId = self.tree.GetItemPyData(newParent)
+        itemId = self.tree.GetItemData(newParent)
         if itemId != "render":
         if itemId != "render":
           newParentObj = self.editor.objectMgr.findObjectById(itemId)
           newParentObj = self.editor.objectMgr.findObjectById(itemId)
           self.reParentData(newParentObj[OG.OBJ_NP], obj[OG.OBJ_NP])
           self.reParentData(newParentObj[OG.OBJ_NP], obj[OG.OBJ_NP])
@@ -207,7 +207,7 @@ class SceneGraphUIBase(wx.Panel):
            self.addPandaObjectChildren(newpParent)
            self.addPandaObjectChildren(newpParent)
 
 
     def isChildOrGrandChild(self, parent, child):
     def isChildOrGrandChild(self, parent, child):
-        childId = self.tree.GetItemPyData(child)
+        childId = self.tree.GetItemData(child)
         return self.traverse(parent, childId)
         return self.traverse(parent, childId)
 
 
     def changeHierarchy(self, data, x, y):
     def changeHierarchy(self, data, x, y):
@@ -226,7 +226,7 @@ class SceneGraphUIBase(wx.Panel):
               return
               return
 
 
            # undo function setup...
            # undo function setup...
-           action = ActionChangeHierarchy(self.editor, self.tree.GetItemPyData(self.tree.GetItemParent(item)), self.tree.GetItemPyData(item), self.tree.GetItemPyData(dragToItem), data)
+           action = ActionChangeHierarchy(self.editor, self.tree.GetItemData(self.tree.GetItemParent(item)), self.tree.GetItemData(item), self.tree.GetItemData(dragToItem), data)
            self.editor.actionMgr.push(action)
            self.editor.actionMgr.push(action)
            action()
            action()
 
 
@@ -283,7 +283,7 @@ class SceneGraphUIBase(wx.Panel):
     def onSelected(self, event):
     def onSelected(self, event):
         item = event.GetItem();
         item = event.GetItem();
         if item:
         if item:
-           itemId = self.tree.GetItemPyData(item)
+           itemId = self.tree.GetItemData(item)
            if itemId:
            if itemId:
               obj = self.editor.objectMgr.findObjectById(itemId);
               obj = self.editor.objectMgr.findObjectById(itemId);
               if obj:
               if obj:
@@ -313,7 +313,7 @@ class SceneGraphUIBase(wx.Panel):
         if not item.IsOk():
         if not item.IsOk():
             return
             return
         self.currItem = item
         self.currItem = item
-        itemId = self.tree.GetItemPyData(item)
+        itemId = self.tree.GetItemData(item)
         if not itemId:
         if not itemId:
             return
             return
         self.currObj = self.editor.objectMgr.findObjectById(itemId);
         self.currObj = self.editor.objectMgr.findObjectById(itemId);

+ 1 - 1
direct/src/leveleditor/testData.py

@@ -1,4 +1,4 @@
-from pandac.PandaModules import *
+from panda3d.core import *
 
 
 if hasattr(base, 'le'):
 if hasattr(base, 'le'):
     objectMgr = base.le.objectMgr
     objectMgr = base.le.objectMgr

+ 35 - 38
direct/src/particles/ParticleEffect.py

@@ -163,44 +163,41 @@ class ParticleEffect(NodePath):
 
 
     def saveConfig(self, filename):
     def saveConfig(self, filename):
         filename = Filename(filename)
         filename = Filename(filename)
-        f = open(filename.toOsSpecific(), 'wb')
-        # Add a blank line
-        f.write('\n')
-
-        # Make sure we start with a clean slate
-        f.write('self.reset()\n')
-
-        pos = self.getPos()
-        hpr = self.getHpr()
-        scale = self.getScale()
-        f.write('self.setPos(%0.3f, %0.3f, %0.3f)\n' %
-                (pos[0], pos[1], pos[2]))
-        f.write('self.setHpr(%0.3f, %0.3f, %0.3f)\n' %
-                (hpr[0], hpr[1], hpr[2]))
-        f.write('self.setScale(%0.3f, %0.3f, %0.3f)\n' %
-                (scale[0], scale[1], scale[2]))
-
-        # Save all the particles to file
-        num = 0
-        for p in list(self.particlesDict.values()):
-            target = 'p%d' % num
-            num = num + 1
-            f.write(target + ' = Particles.Particles(\'%s\')\n' % p.getName())
-            p.printParams(f, target)
-            f.write('self.addParticles(%s)\n' % target)
-
-        # Save all the forces to file
-        num = 0
-        for fg in list(self.forceGroupDict.values()):
-            target = 'f%d' % num
-            num = num + 1
-            f.write(target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \
-                                                fg.getName())
-            fg.printParams(f, target)
-            f.write('self.addForceGroup(%s)\n' % target)
-
-        # Close the file
-        f.close()
+        with open(filename.toOsSpecific(), 'w') as f:
+          # Add a blank line
+          f.write('\n')
+
+          # Make sure we start with a clean slate
+          f.write('self.reset()\n')
+
+          pos = self.getPos()
+          hpr = self.getHpr()
+          scale = self.getScale()
+          f.write('self.setPos(%0.3f, %0.3f, %0.3f)\n' %
+                  (pos[0], pos[1], pos[2]))
+          f.write('self.setHpr(%0.3f, %0.3f, %0.3f)\n' %
+                  (hpr[0], hpr[1], hpr[2]))
+          f.write('self.setScale(%0.3f, %0.3f, %0.3f)\n' %
+                  (scale[0], scale[1], scale[2]))
+
+          # Save all the particles to file
+          num = 0
+          for p in list(self.particlesDict.values()):
+              target = 'p%d' % num
+              num = num + 1
+              f.write(target + ' = Particles.Particles(\'%s\')\n' % p.getName())
+              p.printParams(f, target)
+              f.write('self.addParticles(%s)\n' % target)
+
+          # Save all the forces to file
+          num = 0
+          for fg in list(self.forceGroupDict.values()):
+              target = 'f%d' % num
+              num = num + 1
+              f.write(target + ' = ForceGroup.ForceGroup(\'%s\')\n' % \
+                                                  fg.getName())
+              fg.printParams(f, target)
+              f.write('self.addForceGroup(%s)\n' % target)
 
 
     def loadConfig(self, filename):
     def loadConfig(self, filename):
         data = vfs.readFile(filename, 1)
         data = vfs.readFile(filename, 1)

+ 1 - 0
direct/src/particles/Particles.py

@@ -22,6 +22,7 @@ from panda3d.physics import RingEmitter
 from panda3d.physics import SphereSurfaceEmitter
 from panda3d.physics import SphereSurfaceEmitter
 from panda3d.physics import SphereVolumeEmitter
 from panda3d.physics import SphereVolumeEmitter
 from panda3d.physics import TangentRingEmitter
 from panda3d.physics import TangentRingEmitter
+from panda3d.physics import SpriteAnim
 
 
 from . import SpriteParticleRendererExt
 from . import SpriteParticleRendererExt
 
 

+ 36 - 44
direct/src/showbase/EventManager.py

@@ -19,7 +19,7 @@ class EventManager:
         Create a C++ event queue and handler
         Create a C++ event queue and handler
         """
         """
         # Make a notify category for this class (unless there already is one)
         # Make a notify category for this class (unless there already is one)
-        if (EventManager.notify == None):
+        if EventManager.notify is None:
             EventManager.notify = directNotify.newCategory("EventManager")
             EventManager.notify = directNotify.newCategory("EventManager")
 
 
         self.eventQueue = eventQueue
         self.eventQueue = eventQueue
@@ -37,8 +37,10 @@ class EventManager:
             processFunc = self.processEventPstats
             processFunc = self.processEventPstats
         else:
         else:
             processFunc = self.processEvent
             processFunc = self.processEvent
-        while (not self.eventQueue.isQueueEmpty()):
-            processFunc(self.eventQueue.dequeueEvent())
+        isEmptyFunc = self.eventQueue.isQueueEmpty
+        dequeueFunc = self.eventQueue.dequeueEvent
+        while not isEmptyFunc():
+            processFunc(dequeueFunc())
 
 
     def eventLoopTask(self, task):
     def eventLoopTask(self, task):
         """
         """
@@ -78,13 +80,13 @@ class EventManager:
         # ******** Duplicate any changes in processEventPstats *********
         # ******** Duplicate any changes in processEventPstats *********
         # **************************************************************
         # **************************************************************
         # Get the event name
         # Get the event name
-        eventName = event.getName()
+        eventName = event.name
         if eventName:
         if eventName:
             paramList = []
             paramList = []
-            for i in range(event.getNumParameters()):
-                eventParameter = event.getParameter(i)
+            for eventParameter in event.parameters:
                 eventParameterData = self.parseEventParameter(eventParameter)
                 eventParameterData = self.parseEventParameter(eventParameter)
                 paramList.append(eventParameterData)
                 paramList.append(eventParameterData)
+
             # Do not print the new frame debug, it is too noisy!
             # Do not print the new frame debug, it is too noisy!
             if (EventManager.notify.getDebug() and eventName != 'NewFrame'):
             if (EventManager.notify.getDebug() and eventName != 'NewFrame'):
                 EventManager.notify.debug('received C++ event named: ' + eventName +
                 EventManager.notify.debug('received C++ event named: ' + eventName +
@@ -94,13 +96,12 @@ class EventManager:
             # **************************************************************
             # **************************************************************
             # Send the event, we used to send it with the event
             # Send the event, we used to send it with the event
             # name as a parameter, but now you can use extraArgs for that
             # name as a parameter, but now you can use extraArgs for that
-            if paramList:
-                messenger.send(eventName, paramList)
-            else:
-                messenger.send(eventName)
+            messenger.send(eventName, paramList)
+
             # Also send the event down into C++ land
             # Also send the event down into C++ land
-            if self.eventHandler:
-                self.eventHandler.dispatchEvent(event)
+            handler = self.eventHandler
+            if handler:
+                handler.dispatchEvent(event)
 
 
         else:
         else:
             # An unnamed event from C++ is probably a bad thing
             # An unnamed event from C++ is probably a bad thing
@@ -115,13 +116,13 @@ class EventManager:
         # ******** Duplicate any changes in processEvent *********
         # ******** Duplicate any changes in processEvent *********
         # ********************************************************
         # ********************************************************
         # Get the event name
         # Get the event name
-        eventName = event.getName()
+        eventName = event.name
         if eventName:
         if eventName:
             paramList = []
             paramList = []
-            for i in range(event.getNumParameters()):
-                eventParameter = event.getParameter(i)
+            for eventParameter in event.parameters:
                 eventParameterData = self.parseEventParameter(eventParameter)
                 eventParameterData = self.parseEventParameter(eventParameter)
                 paramList.append(eventParameterData)
                 paramList.append(eventParameterData)
+
             # Do not print the new frame debug, it is too noisy!
             # Do not print the new frame debug, it is too noisy!
             if (EventManager.notify.getDebug() and eventName != 'NewFrame'):
             if (EventManager.notify.getDebug() and eventName != 'NewFrame'):
                 EventManager.notify.debug('received C++ event named: ' + eventName +
                 EventManager.notify.debug('received C++ event named: ' + eventName +
@@ -131,45 +132,36 @@ class EventManager:
             # ********************************************************
             # ********************************************************
             # ******** Duplicate any changes in processEvent *********
             # ******** Duplicate any changes in processEvent *********
             # ********************************************************
             # ********************************************************
-            if self._wantPstats:
-                name = eventName
-                hyphen = name.find('-')
-                if hyphen >= 0:
-                    name = name[0:hyphen]
-                pstatCollector = PStatCollector('App:Show code:eventManager:' + name)
-                pstatCollector.start()
-                if self.eventHandler:
-                    cppPstatCollector = PStatCollector(
-                        'App:Show code:eventManager:' + name + ':C++')
-
-            if paramList:
-                messenger.send(eventName, paramList)
-            else:
-                messenger.send(eventName)
-            # Also send the event down into C++ land
+            name = eventName
+            hyphen = name.find('-')
+            if hyphen >= 0:
+                name = name[0:hyphen]
+            pstatCollector = PStatCollector('App:Show code:eventManager:' + name)
+            pstatCollector.start()
             if self.eventHandler:
             if self.eventHandler:
-                if self._wantPstats:
-                    cppPstatCollector.start()
-                self.eventHandler.dispatchEvent(event)
-            # ********************************************************
-            # ******** Duplicate any changes in processEvent *********
-            # ********************************************************
+                cppPstatCollector = PStatCollector(
+                    'App:Show code:eventManager:' + name + ':C++')
 
 
-            if self._wantPstats:
-                if self.eventHandler:
-                    cppPstatCollector.stop()
-                pstatCollector.stop()
+            messenger.send(eventName, paramList)
+
+            # Also send the event down into C++ land
+            handler = self.eventHandler
+            if handler:
+                cppPstatCollector.start()
+                handler.dispatchEvent(event)
+                cppPstatCollector.stop()
+
+            pstatCollector.stop()
 
 
         else:
         else:
             # An unnamed event from C++ is probably a bad thing
             # An unnamed event from C++ is probably a bad thing
             EventManager.notify.warning('unnamed event in processEvent')
             EventManager.notify.warning('unnamed event in processEvent')
 
 
-
     def restart(self):
     def restart(self):
-        if self.eventQueue == None:
+        if self.eventQueue is None:
             self.eventQueue = EventQueue.getGlobalEventQueue()
             self.eventQueue = EventQueue.getGlobalEventQueue()
 
 
-        if self.eventHandler == None:
+        if self.eventHandler is None:
             if self.eventQueue == EventQueue.getGlobalEventQueue():
             if self.eventQueue == EventQueue.getGlobalEventQueue():
                 # If we are using the global event queue, then we also
                 # If we are using the global event queue, then we also
                 # want to use the global event handler.
                 # want to use the global event handler.

+ 0 - 100
direct/src/showbase/FindCtaPaths.py

@@ -1,100 +0,0 @@
-"""This module is used only by the VR Studio programmers who are using
-the ctattach tools.  It is imported before any other package, and its
-job is to figure out the correct paths to each of the packages.
-
-This module is not needed if you are not using ctattach; in this case
-all of the Panda packages will be collected under a common directory,
-which you will presumably have already on your PYTHONPATH. """
-
-__all__ = ['deCygwinify', 'getPaths']
-
-import os
-import sys
-
-def deCygwinify(path):
-    if os.name in ['nt'] and path[0] == '/':
-        # On Windows, we may need to convert from a Cygwin-style path
-        # to a native Windows path.
-
-        # Check for a case like /i/ or /p/: this converts
-        # to i:\ or p:\.
-
-        dirs = path.split('/')
-        if len(dirs) > 2 and len(dirs[1]) == 1:
-            path = '%s:\%s' % (dirs[1], '\\'.join(dirs[2:]))
-
-        else:
-            # Otherwise, prepend $PANDA_ROOT and flip the slashes.
-            pandaRoot = os.getenv('PANDA_ROOT')
-            if pandaRoot:
-                path = os.path.normpath(pandaRoot + path)
-
-    return path
-
-def getPaths():
-    """
-    Add to sys.path the appropriate director(ies) to search for the
-    various Panda projects.  Typically, these will all be in the same
-    directory (which is presumably already on sys.path), but if the VR
-    Studio ctattach tools are in use they could be scattered around in
-    several places.
-    """
-
-    ctprojs = os.getenv("CTPROJS")
-    if ctprojs:
-        # The CTPROJS environment variable is defined.  We must be
-        # using the ctattach tools.  In this case, we need to figure
-        # out the location of each of the separate trees, and put the
-        # parent directory of each one on sys.path.  In many cases,
-        # these will all be siblings, so we filter out duplicate
-        # parent directories.
-
-        print('Appending to sys.path based on $CTPROJS:')
-
-        # First, get the list of packages, then reverse the list to
-        # put it in ctattach order.  (The reversal may not matter too
-        # much these days, but let's be as correct as we can be.)
-        packages = []
-        for proj in ctprojs.split():
-            projName = proj.split(':')[0]
-            packages.append(projName)
-        packages.reverse()
-
-        # Now walk through the packages and figure out the parent of
-        # each referenced directory.
-
-        parents = []
-        for package in packages:
-            tree = os.getenv(package)
-            if not tree:
-                print("  CTPROJS contains %s, but $%s is not defined." % (package, package))
-                sys.exit(1)
-
-            tree = deCygwinify(tree)
-
-            parent, base = os.path.split(tree)
-            if base != package.lower():
-                print("  Warning: $%s refers to a directory named %s (instead of %s)" % (package, base, package.lower()))
-
-            if parent not in parents:
-                parents.append(parent)
-
-
-            # We also put tree/built/lib on sys.path by hand, because we
-            # will need to load up the generated C++ modules that got
-            # put there.  Also, we will find the output of genPyCode
-            # in $DIRECT/built/lib/pandac.
-            libdir = os.path.join(tree, 'built', 'lib')
-            if os.path.isdir(libdir):
-                if libdir not in sys.path:
-                    sys.path.append(libdir)
-
-
-        # Now the result goes onto sys.path.
-        for parent in parents:
-            print("  %s" % (parent))
-            if parent not in sys.path:
-                sys.path.append(parent)
-
-
-getPaths()

+ 42 - 20
direct/src/showbase/Loader.py

@@ -27,6 +27,42 @@ class Loader(DirectObject):
         # This indicates that this class behaves like a Future.
         # This indicates that this class behaves like a Future.
         _asyncio_future_blocking = False
         _asyncio_future_blocking = False
 
 
+        class ResultAwaiter(object):
+            """Reinvents generators because of PEP 479, sigh.  See #513."""
+
+            __slots__ = 'requestList', 'index'
+
+            def __init__(self, requestList):
+                self.requestList = requestList
+                self.index = 0
+
+            def __await__(self):
+                return self
+
+            def __anext__(self):
+                if self.index >= len(self.requestList):
+                    raise StopAsyncIteration
+                return self
+
+            def __iter__(self):
+                return self
+
+            def __next__(self):
+                i = self.index
+                request = self.requestList[i]
+                if not request.done():
+                    return request
+
+                self.index = i + 1
+
+                result = request.result()
+                if isinstance(result, PandaNode):
+                    result = NodePath(result)
+
+                exc = StopIteration(result)
+                exc.value = result
+                raise exc
+
         def __init__(self, loader, numObjects, gotList, callback, extraArgs):
         def __init__(self, loader, numObjects, gotList, callback, extraArgs):
             self._loader = loader
             self._loader = loader
             self.objects = [None] * numObjects
             self.objects = [None] * numObjects
@@ -81,16 +117,14 @@ class Loader(DirectObject):
         def __await__(self):
         def __await__(self):
             """ Returns a generator that raises StopIteration when the loading
             """ Returns a generator that raises StopIteration when the loading
             is complete.  This allows this class to be used with 'await'."""
             is complete.  This allows this class to be used with 'await'."""
+
             if self.requests:
             if self.requests:
                 self._asyncio_future_blocking = True
                 self._asyncio_future_blocking = True
-                yield self
 
 
-            # This should be a simple return, but older versions of Python
-            # don't allow return statements with arguments.
-            result = self.result()
-            exc = StopIteration(result)
-            exc.value = result
-            raise exc
+            if self.gotList:
+                return self.ResultAwaiter([self])
+            else:
+                return self.ResultAwaiter(self.requestList)
 
 
         def __aiter__(self):
         def __aiter__(self):
             """ This allows using `async for` to iterate asynchronously over
             """ This allows using `async for` to iterate asynchronously over
@@ -100,19 +134,7 @@ class Loader(DirectObject):
             requestList = self.requestList
             requestList = self.requestList
             assert requestList is not None, "Request was cancelled."
             assert requestList is not None, "Request was cancelled."
 
 
-            class AsyncIter:
-                index = 0
-                def __anext__(self):
-                    if self.index < len(requestList):
-                        i = self.index
-                        self.index = i + 1
-                        return requestList[i]
-                    else:
-                        raise StopAsyncIteration
-
-            iter = AsyncIter()
-            iter.objects = self.objects
-            return iter
+            return self.ResultAwaiter(requestList)
 
 
     # special methods
     # special methods
     def __init__(self, base):
     def __init__(self, base):

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

@@ -2513,7 +2513,7 @@ class AlphabetCounter:
         index = -1
         index = -1
         while True:
         while True:
             curChar = self._curCounter[index]
             curChar = self._curCounter[index]
-            if curChar is 'Z':
+            if curChar == 'Z':
                 nextChar = 'A'
                 nextChar = 'A'
                 carry = True
                 carry = True
             else:
             else:

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

@@ -2926,7 +2926,7 @@ class ShowBase(DirectObject.DirectObject):
 
 
         if not self.wxApp:
         if not self.wxApp:
             # Create a new base.wxApp.
             # Create a new base.wxApp.
-            self.wxApp = wx.PySimpleApp(redirect = False)
+            self.wxApp = wx.App(redirect = False)
 
 
         if ConfigVariableBool('wx-main-loop', True):
         if ConfigVariableBool('wx-main-loop', True):
             # Put wxPython in charge of the main loop.  It really
             # Put wxPython in charge of the main loop.  It really

+ 9 - 7
direct/src/showutil/TexMemWatcher.py

@@ -786,8 +786,9 @@ class TexMemWatcher(DirectObject):
             # Look for a single rectangular hole to hold this piece.
             # Look for a single rectangular hole to hold this piece.
             tp = self.findHole(tr.area, tr.w, tr.h)
             tp = self.findHole(tr.area, tr.w, tr.h)
             if tp:
             if tp:
-                texCmp = cmp(tr.w, tr.h)
-                holeCmp = cmp(tp.p[1] - tp.p[0], tp.p[3] - tp.p[2])
+                texCmp = (tr.w > tr.h) - (tr.w < tr.h)
+                holeCmp = ((tp.p[1] - tp.p[0]) > (tp.p[3] - tp.p[2])) \
+                        - ((tp.p[1] - tp.p[0]) < (tp.p[3] - tp.p[2]))
                 if texCmp != 0 and holeCmp != 0 and texCmp != holeCmp:
                 if texCmp != 0 and holeCmp != 0 and texCmp != holeCmp:
                     tp.rotated = True
                     tp.rotated = True
                 tr.placements = [tp]
                 tr.placements = [tp]
@@ -803,10 +804,11 @@ class TexMemWatcher(DirectObject):
             # in.
             # in.
             tpList = self.findHolePieces(tr.area)
             tpList = self.findHolePieces(tr.area)
             if tpList:
             if tpList:
-                texCmp = cmp(tr.w, tr.h)
+                texCmp = (tr.w > tr.h) - (tr.w < tr.h)
                 tr.placements = tpList
                 tr.placements = tpList
                 for tp in tpList:
                 for tp in tpList:
-                    holeCmp = cmp(tp.p[1] - tp.p[0], tp.p[3] - tp.p[2])
+                    holeCmp = ((tp.p[1] - tp.p[0]) > (tp.p[3] - tp.p[2])) \
+                            - ((tp.p[1] - tp.p[0]) < (tp.p[3] - tp.p[2]))
                     if texCmp != 0 and holeCmp != 0 and texCmp != holeCmp:
                     if texCmp != 0 and holeCmp != 0 and texCmp != holeCmp:
                         tp.rotated = True
                         tp.rotated = True
                     tp.setBitmasks(self.bitmasks)
                     tp.setBitmasks(self.bitmasks)
@@ -858,11 +860,11 @@ class TexMemWatcher(DirectObject):
             # we have to squish it?
             # we have to squish it?
             if tw < w:
             if tw < w:
                 # We'd have to make it taller.
                 # We'd have to make it taller.
-                nh = min(area / tw, th)
+                nh = min(area // tw, th)
                 th = nh
                 th = nh
             elif th < h:
             elif th < h:
                 # We'd have to make it narrower.
                 # We'd have to make it narrower.
-                nw = min(area / th, tw)
+                nw = min(area // th, tw)
                 tw = nw
                 tw = nw
             else:
             else:
                 # Hey, we don't have to squish it after all!  Just
                 # Hey, we don't have to squish it after all!  Just
@@ -912,7 +914,7 @@ class TexMemWatcher(DirectObject):
             tpArea = (r - l) * (t - b)
             tpArea = (r - l) * (t - b)
             if tpArea >= area:
             if tpArea >= area:
                 # we're done.
                 # we're done.
-                shorten = (tpArea - area) / (r - l)
+                shorten = (tpArea - area) // (r - l)
                 t -= shorten
                 t -= shorten
                 tp.p = (l, r, b, t)
                 tp.p = (l, r, b, t)
                 tp.area = (r - l) * (t - b)
                 tp.area = (r - l) * (t - b)

+ 15 - 16
direct/src/tkpanels/ParticlePanel.py

@@ -96,22 +96,21 @@ class ParticlePanel(AppShell):
 
 
         ## MENUBAR ENTRIES ##
         ## MENUBAR ENTRIES ##
         # FILE MENU
         # FILE MENU
-        # Get a handle on the file menu so commands can be inserted
-        # before quit item
-        fileMenu = self.menuBar.component('File-menu')
-        # MRM: Need to add load and save effects methods
-        fileMenu.insert_command(
-            fileMenu.index('Quit'),
-            label = 'Load Params',
-            command = self.loadParticleEffectFromFile)
-        fileMenu.insert_command(
-            fileMenu.index('Quit'),
-            label = 'Save Params',
-            command = self.saveParticleEffectToFile)
-        fileMenu.insert_command(
-            fileMenu.index('Quit'),
-            label = 'Print Params',
-            command = lambda s = self: s.particles.printParams())
+        # Get a handle on the file menu, and delete the Quit item that AppShell
+        # created so we can add it back after adding the other items.
+        self.menuBar.deletemenuitems('File', 0, 0)
+        self.menuBar.addmenuitem('File', 'command',
+                                 label='Load Params',
+                                 command=self.loadParticleEffectFromFile)
+        self.menuBar.addmenuitem('File', 'command',
+                                 label='Save Params',
+                                 command=self.saveParticleEffectToFile)
+        self.menuBar.addmenuitem('File', 'command',
+                                 label='Print Params',
+                                 command=lambda s=self:s.particles.printParams())
+        self.menuBar.addmenuitem('File', 'command', 'Quit this application',
+                                 label='Quit',
+                                 command=self.quit)
 
 
         # PARTICLE MANAGER MENU
         # PARTICLE MANAGER MENU
         self.menuBar.addmenu('ParticleMgr', 'ParticleMgr Operations')
         self.menuBar.addmenu('ParticleMgr', 'ParticleMgr Operations')

+ 1 - 0
direct/src/tkwidgets/Valuator.py

@@ -7,6 +7,7 @@ from direct.showbase.TkGlobal import *
 from . import WidgetPropertiesDialog
 from . import WidgetPropertiesDialog
 import Pmw
 import Pmw
 from direct.directtools.DirectUtil import getTkColorString
 from direct.directtools.DirectUtil import getTkColorString
+from panda3d.core import Vec4
 
 
 if sys.version_info >= (3, 0):
 if sys.version_info >= (3, 0):
     from tkinter.colorchooser import askcolor
     from tkinter.colorchooser import askcolor

+ 4 - 4
direct/src/wxwidgets/WxPandaShell.py

@@ -202,15 +202,15 @@ class WxPandaShell(WxAppShell):
         """A step in the WX event loop. You can either call this yourself or use as task."""
         """A step in the WX event loop. You can either call this yourself or use as task."""
         while self.evtLoop.Pending():
         while self.evtLoop.Pending():
           self.evtLoop.Dispatch()
           self.evtLoop.Dispatch()
-        self.wxApp.ProcessIdle()
+        self.evtLoop.ProcessIdle()
         if task != None: return task.cont
         if task != None: return task.cont
 
 
     def appInit(self):
     def appInit(self):
         """Overridden from WxAppShell.py."""
         """Overridden from WxAppShell.py."""
         # Create a new event loop (to overide default wxEventLoop)
         # Create a new event loop (to overide default wxEventLoop)
-        self.evtLoop = wx.EventLoop()
-        self.oldLoop = wx.EventLoop.GetActive()
-        wx.EventLoop.SetActive(self.evtLoop)
+        self.evtLoop = wx.GUIEventLoop()
+        self.oldLoop = wx.GUIEventLoop.GetActive()
+        wx.GUIEventLoop.SetActive(self.evtLoop)
         taskMgr.add(self.wxStep, "evtLoopTask")
         taskMgr.add(self.wxStep, "evtLoopTask")
 
 
     def onViewChange(self, evt, viewIdx):
     def onViewChange(self, evt, viewIdx):

+ 53 - 0
doc/ReleaseNotes

@@ -1,7 +1,60 @@
+------------------------  RELEASE 1.10.2  -----------------------
+
+This release fixes several more bugs, including a few regressions
+in 1.10.1.  Upgrading is highly recommended.
+
+* Fix regression on Windows causing freezes and instability
+* Fix a memory leak issue in Python applications
+* Fix crash reading unaligned float4 column in GeomVertexReader
+* Fixes for switching to fullscreen at runtime on Windows and Linux
+* Fix incorrect display mode listing in some Linux distributions
+* Fix threading crash on Linux when using get_keyboard_map()
+* Support "from __future__ import division" for Panda types
+* Support building with Visual Studio 2019 in makepanda
+* Work around Assimp crash when loading multiple .ply models
+* On Windows, a Python 3-compatible version of Pmw is included
+* Fix ParticlePanel spam when hovering over File menu items
+* TexMemWatcher has been fixed for Python 3
+* Prevent macOS window getting stuck after base.destroy()
+* Fix assertion setting mass before shape with Bullet debug build
+* Don't error if DirectScrolledFrame is destroyed twice
+* Fix reference count corruption accessing task.__dict__
+* Fix writing to SequenceNode frame_rate property
+* Fix collider sort not copied when copying CollisionNode
+* Add OpenCollective backer file
+
 ------------------------  RELEASE 1.10.1  -----------------------
 ------------------------  RELEASE 1.10.1  -----------------------
 
 
+This is a bugfix release intended to fix several issues in 1.10.0.
+
+* Fix crashes when gamepad is plugged in on 32-bit Windows
+* Fix deploy-ng error regarding 'exist_ok' on Python 2
+* Fix Linux install from pip not working with some mesa drivers
+* Fix compatibility issues with upcoming Python 3.8
 * Fix regression with Audio3DManager.setSoundVelocityAuto()
 * Fix regression with Audio3DManager.setSoundVelocityAuto()
+* Fix issues when awaiting loader.loadModel in Python 3.7
 * Audio3DManager accepts tuple in setSoundVelocity/setListenerVelocity
 * Audio3DManager accepts tuple in setSoundVelocity/setListenerVelocity
+* Fix lighting being disabled when only an AmbientLight is active
+* Fix an error saving from Particle Panel in Python 3
+* Depth buffer now defaults to 24-bit on macOS (fixes flickering)
+* Fix no devices being detected on Windows with threading-model
+* Implement collision tests from Capsule and Box into InvSphere
+* Fix odd behavior and occasional crash in QuatInterval
+* Fix SpriteAnim error in particle system
+* Fix ShaderGenerator error when using too many shadowing lights
+* Fix interrogate crash in Python 3 with optional wstring args
+* Fix compilation errors for x86 Android platform
+* Fix permissions of directories created by installpanda
+* Improvements to API reference documentation
+* Fix incorrect features printed out when printing an InputDevice
+* Support cross-compiling for Android platforms in makepanda
+* Work around various bugs when compiling with OS X 10.7's libc++
+* Fix wrong error sometimes being reported when loading plug-in
+* Allow getting NodePath from CullTraverserData object
+* Add config options to Assimp loader for generating normals
+* Fix multisampling in floating-point framebuffers on OpenGL
+* Parse egg files with 4-component tangents (must be 1 or -1)
+* StencilAttrib.make() write_mask argument is now optional
 
 
 ------------------------  RELEASE 1.10.0  -----------------------
 ------------------------  RELEASE 1.10.0  -----------------------
 
 

+ 0 - 44
doc/man/fltcopy.1

@@ -1,44 +0,0 @@
-.\" Automatically generated by fltcopy -write-bam
-.TH FLTCOPY 1 "27 December 2014" "1.9.0" Panda3D
-.SH NAME
-fltcopy \- copy MultiGen .flt files into a CVS source hierarchy
-.SH SYNOPSIS
-\fBfltcopy\fR [opts] file.flt [file.flt ... ]
-.SH DESCRIPTION
-fltcopy copies one or more MultiGen .flt files into a CVS source hierarchy.  Rather than copying the named files immediately into the current directory, it first scans the entire source hierarchy, identifying all the already\-existing files.  If the named file to copy matches the name of an already\-existing file in the current directory or elsewhere in the hierarchy, that file is overwritten.  Other .flt files, as well as texture files, that are externally referenced by the named .flt file(s) are similarly copied.
-.SH OPTIONS
-.TP
-.BI "\-pr " "path_replace"
-Sometimes references to other files (textures, external references) are stored with a full path that is appropriate for some other system, but does not exist here.  This option may be used to specify how those invalid paths map to correct paths.  Generally, this is of the form 'orig_prefix=replacement_prefix', which indicates a particular initial sequence of characters that should be replaced with a new sequence; e.g. '/c/home/models=/beta/fish'.  If the replacement prefix does not begin with a slash, the file will then be searched for along the search path specified by -pp.  You may use standard filename matching characters ('*', '?', etc.) in the original prefix, and '**' as a component by itself stands for any number of components.
-
-This option may be repeated as necessary; each file will be tried against each specified method, in the order in which they appear in the command line, until the file is found.  If the file is not found, the last matching prefix is used anyway.
-.TP
-.BI "\-pp " "dirname"
-Adds the indicated directory name to the list of directories to search for filenames referenced by the source file.  This is used only for relative paths, or for paths that are made relative by a -pr replacement string that doesn't begin with a leading slash.  The model-path is always implicitly searched anyway.
-.TP
-.B \-f
-Force the copy to happen without any input from the user.  If a file with the same name exists anywhere in the source hierarchy, it will be overwritten without prompting; if a file does not yet exist, it will be created in the directory named by -d or by -m, as appropriate.
-.TP
-.B \-i
-The opposite of -f, this will prompt the user before each action.  The default is only to prompt the user when an action is ambiguous or unusual.
-.TP
-.BI "\-d " "dirname"
-Copy model files that are not already present somewhere in the tree to the indicated directory.  The default is the current directory.
-.TP
-.BI "\-m " "dirname"
-Copy texture map files to the indicated directory.  The default is src/maps from the root directory.
-.TP
-.BI "\-root " "dirname"
-Specify the root of the CVS source hierarchy.  The default is to use the ppremake convention of locating the directory above the -d directory that contains a file called Package.pp.
-.TP
-.BI "\-key " "filename"
-Specify the name of the file that must exist in each directory for it to be considered part of the CVS source hierarchy.  The default is the ppremake convention, "Sources.pp".  Other likely candidates are "CVS" to search a CVS hierarchy, or "." to include all subdirectories indiscriminately.
-.TP
-.B \-nc
-Do not attempt to add newly-created files to CVS.  The default is to add them.
-.TP
-.BI "\-cvs " "cvs_binary"
-Specify how to run the cvs program for adding newly-created files.  The default is simply "cvs".
-.TP
-.B \-h
-Display this help page.

+ 1 - 1
dtool/PandaVersion.pp

@@ -7,7 +7,7 @@
 // place to put this.
 // place to put this.
 
 
 // Use spaces to separate the major, minor, and sequence numbers here.
 // Use spaces to separate the major, minor, and sequence numbers here.
-#define PANDA_VERSION 1 10 1
+#define PANDA_VERSION 1 11 0
 
 
 // This variable will be defined to false in the CVS repository, but
 // This variable will be defined to false in the CVS repository, but
 // scripts that generate source tarballs and/or binary releases for
 // scripts that generate source tarballs and/or binary releases for

+ 1 - 1
dtool/src/dtoolbase/atomicAdjustI386Impl.h

@@ -17,7 +17,7 @@
 #include "dtoolbase.h"
 #include "dtoolbase.h"
 #include "selectThreadImpl.h"
 #include "selectThreadImpl.h"
 
 
-#if (defined(__i386__) || defined(_M_IX86)) && !defined(__APPLE__)
+#if (defined(__i386__) || defined(_M_IX86)) && !defined(__APPLE__) && !defined(__ANDROID__)
 
 
 #include "numeric_types.h"
 #include "numeric_types.h"
 
 

+ 3 - 0
dtool/src/dtoolbase/epvector.h

@@ -20,6 +20,8 @@
 
 
 #include <Eigen/StdVector>
 #include <Eigen/StdVector>
 
 
+#include <initializer_list>
+
 /**
 /**
  * Unfortunately, on Windows, std::vector can't be used for classes with
  * Unfortunately, on Windows, std::vector can't be used for classes with
  * explicitly alignment requirements, due to a minor mistake in the template
  * explicitly alignment requirements, due to a minor mistake in the template
@@ -46,6 +48,7 @@ public:
   epvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator()) { }
   epvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator()) { }
   epvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator()) { }
   epvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator()) { }
   epvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator()) { }
   epvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator()) { }
+  epvector(std::initializer_list<Type> init, TypeHandle type_handle = pvector_type_handle) : base_class(std::move(init), allocator()) { }
 };
 };
 
 
 #else  // HAVE_EIGEN
 #else  // HAVE_EIGEN

+ 1 - 1
dtool/src/dtoolbase/mutexImpl.h

@@ -34,7 +34,7 @@ typedef MutexSpinlockImpl MutexImpl;
 
 
 #include "mutexWin32Impl.h"
 #include "mutexWin32Impl.h"
 typedef MutexWin32Impl MutexImpl;
 typedef MutexWin32Impl MutexImpl;
-typedef MutexWin32Impl ReMutexImpl;  // Win32 Mutexes are always reentrant.
+typedef ReMutexWin32Impl ReMutexImpl;
 #define HAVE_REMUTEXIMPL 1
 #define HAVE_REMUTEXIMPL 1
 
 
 #elif defined(THREAD_POSIX_IMPL)
 #elif defined(THREAD_POSIX_IMPL)

+ 39 - 3
dtool/src/dtoolbase/mutexWin32Impl.I

@@ -16,7 +16,11 @@
  */
  */
 INLINE MutexWin32Impl::
 INLINE MutexWin32Impl::
 ~MutexWin32Impl() {
 ~MutexWin32Impl() {
-  DeleteCriticalSection(&_lock);
+  // If the lock has been contended, and we use the Windows XP implementation,
+  // we have a handle to close.  Otherwise, this field will be null.
+  if (_lock[1] != nullptr) {
+    CloseHandle(_lock[1]);
+  }
 }
 }
 
 
 /**
 /**
@@ -24,7 +28,7 @@ INLINE MutexWin32Impl::
  */
  */
 INLINE void MutexWin32Impl::
 INLINE void MutexWin32Impl::
 lock() {
 lock() {
-  EnterCriticalSection(&_lock);
+  _funcs._lock(_lock);
 }
 }
 
 
 /**
 /**
@@ -32,13 +36,45 @@ lock() {
  */
  */
 INLINE bool MutexWin32Impl::
 INLINE bool MutexWin32Impl::
 try_lock() {
 try_lock() {
-  return (TryEnterCriticalSection(&_lock) != 0);
+  return (_funcs._try_lock(_lock) != 0);
 }
 }
 
 
 /**
 /**
  *
  *
  */
  */
 INLINE void MutexWin32Impl::
 INLINE void MutexWin32Impl::
+unlock() {
+  _funcs._unlock(_lock);
+}
+
+/**
+ *
+ */
+INLINE ReMutexWin32Impl::
+~ReMutexWin32Impl() {
+  DeleteCriticalSection(&_lock);
+}
+
+/**
+ *
+ */
+INLINE void ReMutexWin32Impl::
+lock() {
+  EnterCriticalSection(&_lock);
+}
+
+/**
+ *
+ */
+INLINE bool ReMutexWin32Impl::
+try_lock() {
+  return (TryEnterCriticalSection(&_lock) != 0);
+}
+
+/**
+ *
+ */
+INLINE void ReMutexWin32Impl::
 unlock() {
 unlock() {
   LeaveCriticalSection(&_lock);
   LeaveCriticalSection(&_lock);
 }
 }

+ 400 - 4
dtool/src/dtoolbase/mutexWin32Impl.cxx

@@ -13,16 +13,412 @@
 
 
 #include "selectThreadImpl.h"
 #include "selectThreadImpl.h"
 
 
-#ifdef WIN32_VC
+#if defined(WIN32_VC) && !defined(CPPPARSER)
 
 
 #include "mutexWin32Impl.h"
 #include "mutexWin32Impl.h"
 
 
+// The number of spins to do before suspending the thread.
+static const unsigned int spin_count = 4000;
+
+// Only compile the below nonsense if we're not compiling for a Vista minimum.
+#if _WIN32_WINNT < 0x0600
+
+// If this is true, we will use SRWLock on Windows Vista and above instead of
+// our own implementation.
+static const bool prefer_srwlock = true;
+
+// These configure our own Windows XP implementation.
+static const uintptr_t lock_bit = 0x40000000;
+
+// This gets set to spin_count if we are on a multi-core system.
+static unsigned int effective_spin_count = 0;
+
+/**
+ * Windows XP implementation of lock(), which uses a combination of a spinlock
+ * and an event.
+ */
+static void __stdcall lock_xp(volatile PVOID *lock) {
+  // In the Windows XP case, lock consists of two words: the first one is a
+  // number of waiters plus a bit to indicate that it is locked, the second
+  // one is the handle of an event that is created in case of contention.
+  // The first word can be in the following states:
+  //
+  // lock bit | waiters | meaning
+  // ---------|---------|---------
+  //   unset  |    0    | unlocked
+  //     set  |    0    | locked, nobody waiting on event
+  //     set  |   >0    | locked, at least one thread waiting on event
+  //   unset  |   >0    | handing off lock to one of waiters
+  //
+  // The last state is a little subtle: at this point, the thread that was
+  // holding the lock has stopped holding it, but is about to fire off a
+  // signal to a waiting thread, which will attempt to grab the lock.  In this
+  // case, the waiting thread has first dibs on the lock, and any new threads
+  // will still treat it as locked and wait until there are no more waiters.
+
+  // First try to acquire the lock without suspending the thread.  This only
+  // works if the waiter count is 0; this way, we give priority to threads
+  // that are already waiting for the event.
+  if (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == nullptr) {
+    // Got the lock on the first try.
+    return;
+  }
+
+  // On multi-core systems, we keep trying for the configured spin_count.
+  const unsigned int max_spins = effective_spin_count;
+  for (unsigned int spins = 0; spins < max_spins; ++spins) {
+    if (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == nullptr) {
+      // We managed to acquire the lock.
+      return;
+    }
+
+    // Emit the pause instruction.  This is NOT a thread yield.
+    YieldProcessor();
+  }
+
+  // Looks like we might have to go to sleep for a while using an event.
+  HANDLE event = lock[1];
+  if (event == nullptr) {
+    // We don't have an event yet.  Create an auto-reset event.
+    HANDLE new_event = CreateEvent(nullptr, false, false, nullptr);
+    while (new_event == nullptr) {
+      // Hmm, out of memory?  Just yield to another thread for now until the
+      // lock is either freed or until we can create an event.
+      Sleep(1);
+      if (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == 0) {
+        return;
+      }
+      new_event = CreateEvent(nullptr, false, false, nullptr);
+    }
+
+    // Push the new event.
+    event = InterlockedCompareExchangePointer(lock + 1, new_event, nullptr);
+    if (event == nullptr) {
+      // Set successfully.
+      event = new_event;
+    } else {
+      // Another thread created an event; delete ours and use that one instead.
+      CloseHandle(new_event);
+    }
+  }
+
+  // OK, now we have an event.  We need to let the unlock() function know that
+  // we are waiting.
+  while (true) {
+    uintptr_t waiters = (uintptr_t)lock[0];
+    if (waiters == 0) {
+      // It became unlocked while we were creating an event.  Quick, grab it.
+      if (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == nullptr) {
+        return;
+      }
+    }
+
+    // If the lock bit gets unset while we try this, just keep trying.  It
+    // would be dangerous to increment this while the lock bit is unset.
+    waiters |= lock_bit;
+    uintptr_t new_waiters = (uintptr_t)InterlockedCompareExchangePointer(lock, (void *)(waiters + 1), (void *)waiters);
+    if (new_waiters == waiters) {
+      // Made the change successfully.
+      break;
+    } else if (new_waiters == 0) {
+      // It just became unlocked.  Quick, grab it.
+      if (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == nullptr) {
+        return;
+      }
+    }
+    YieldProcessor();
+  }
+
+  // Sleep well, thread.
+  while (true) {
+    WaitForSingleObjectEx(event, INFINITE, FALSE);
+
+    // We were woken up.  Does that mean the lock can be ours?
+    while (true) {
+      uintptr_t waiters = (uintptr_t)lock[0];
+      if (waiters & lock_bit) {
+        // False alarm.  Go back to sleep.
+        break;
+      }
+      assert(waiters > 0);
+
+      // Grab the lock immediately, and simultaneously tell it that we are no
+      // longer waiting.
+      uintptr_t new_waiters = (uintptr_t)InterlockedCompareExchangePointer(lock, (void *)((waiters - 1) | lock_bit), (void *)waiters);
+      if (new_waiters == waiters) {
+        // The lock is ours.
+        return;
+      } else if (new_waiters & lock_bit) {
+        // Another thread beat us to it.  Go back to sleep.
+        break;
+      }
+      YieldProcessor();
+    }
+  }
+
+  // Never supposed to get here.
+  assert(false);
+}
+
+/**
+ * Windows XP implementation of try_lock().
+ */
+static BOOL __stdcall try_lock_xp(volatile PVOID *lock) {
+  return (InterlockedCompareExchangePointer(lock, (void *)lock_bit, nullptr) == nullptr);
+}
+
+/**
+ * Windows XP implementation of unlock().
+ */
+static void __stdcall unlock_xp(volatile PVOID *lock) {
+  // Clear the lock flag.
+#ifdef _WIN64
+  uintptr_t waiters = _InterlockedAnd64((volatile __int64 *)lock, ~lock_bit);
+#else
+  uintptr_t waiters = _InterlockedAnd((volatile long *)lock, ~lock_bit);
+#endif
+
+  // If this triggers, the lock wasn't held to begin with.
+  assert((waiters & lock_bit) != 0);
+
+  // Have any threads begun to sleep (or are about to) waiting for this lock?
+  if ((waiters & ~lock_bit) == 0) {
+    // No contention, nothing to do.
+    return;
+  } else {
+    // By signalling the auto-resetting event, we wake up one waiting thread.
+    HANDLE event = lock[1];
+    assert(event != nullptr);
+    SetEvent(event);
+  }
+}
+
+/**
+ * Windows XP implementation to wait for a condition variable.
+ */
+static BOOL __stdcall
+cvar_wait_xp(volatile PVOID *cvar, volatile PVOID *lock, DWORD timeout, ULONG) {
+  // Increment the number of waiters.
+#ifdef _WIN64
+  _InterlockedIncrement64((volatile __int64 *)cvar);
+#else
+  _InterlockedIncrement((volatile long *)cvar);
+#endif
+
+  // Make sure we have two events created: one auto-reset event and one
+  // manual-reset, to handle signal and broadcast, respectively.
+  if (cvar[1] == nullptr) {
+    cvar[1] = CreateEvent(nullptr, false, false, nullptr);
+  }
+  if (cvar[2] == nullptr) {
+    cvar[2] = CreateEvent(nullptr, true, false, nullptr);
+  }
+
+  // It's ok to release the external_mutex here since Win32 manual-reset
+  // events maintain state when used with SetEvent(). This avoids the "lost
+  // wakeup" bug...
+  unlock_xp(lock);
+
+  // Wait for either event to become signaled due to notify() being called or
+  // notify_all() being called.
+  int result = WaitForMultipleObjects(2, (const HANDLE *)(cvar + 1), FALSE, timeout);
+
+  // Decrement the counter.  If it reached zero, we were the last waiter.
+#ifdef _WIN64
+  bool nonzero = (_InterlockedDecrement64((volatile __int64 *)cvar) != 0);
+#else
+  bool nonzero = (_InterlockedDecrement((volatile long *)cvar) != 0);
+#endif
+  bool last_waiter = (result == WAIT_OBJECT_0 + 1 && !nonzero);
+
+  // Some thread called notify_all().
+  if (last_waiter) {
+    // We're the last waiter to be notified or to stop waiting, so reset the
+    // manual event.
+    ResetEvent(cvar[2]);
+  }
+
+  // Reacquire the <external_mutex>.
+  lock_xp(lock);
+  return TRUE;
+}
+
+/**
+ * Wakes one thread waiting for a condition variable.
+ */
+static void __stdcall
+cvar_notify_one_xp(volatile PVOID *cvar) {
+  // If there are any waiters, signal one of them to wake up by signalling the
+  // auto-reset event.
+  if ((uintptr_t)cvar[0] > 0) {
+    SetEvent(cvar[1]);
+  }
+}
+
+/**
+ * Wakes all threads waiting for a condition variable.
+ */
+static void __stdcall
+cvar_notify_all_xp(volatile PVOID *cvar) {
+  // If there are any waiters, signal the manual-reset event, which will be
+  // reset by the last thread to wake up.
+  if ((uintptr_t)cvar[0] > 0) {
+    SetEvent(cvar[2]);
+  }
+}
+
+#endif  // _WIN32_WINNT < 0x0600
+
+/**
+ * This is put initially in the _lock slot; it makes sure that the lock
+ * functions get initialized the first time someone tries to grab a lock.
+ */
+void __stdcall MutexWin32Impl::
+lock_initially(volatile PVOID *lock) {
+  MutexWin32Impl::init_lock_funcs();
+  MutexWin32Impl::_funcs._lock(lock);
+}
+
+/**
+ * This is put initially in the _try_lock slot; it makes sure that the lock
+ * functions get initialized the first time someone tries to grab a lock.
+ */
+BOOL __stdcall MutexWin32Impl::
+try_lock_initially(volatile PVOID *lock) {
+  MutexWin32Impl::init_lock_funcs();
+  return MutexWin32Impl::_funcs._try_lock(lock);
+}
+
+/**
+ * This gets put initially in the _unlock slot and should never be called,
+ * since the initial lock/try_lock implementation will replace the pointers.
+ */
+void __stdcall MutexWin32Impl::
+unlock_initially(volatile PVOID *) {
+#if !defined(NDEBUG) || defined(DEBUG_THREADS)
+  std::cerr << "Attempt to release a mutex at static init time before acquiring it!\n";
+  assert(false);
+#endif
+}
+
+/**
+ * Same as above for condition variables.
+ */
+static BOOL __stdcall
+cvar_wait_initially(volatile PVOID *cvar, volatile PVOID *lock, DWORD timeout, ULONG) {
+#if !defined(NDEBUG) || defined(DEBUG_THREADS)
+  std::cerr << "Attempt to wait for condition variable at static init time before acquiring mutex!\n";
+  assert(false);
+#endif
+  return FALSE;
+}
+
+/**
+ * Does nothing.
+ */
+static void __stdcall
+noop(volatile PVOID *) {
+}
+
+// We initially set the function pointers to functions that do initialization
+// first.
+MutexWin32Impl::LockFunctions MutexWin32Impl::_funcs = {
+  &MutexWin32Impl::lock_initially,
+  &MutexWin32Impl::try_lock_initially,
+#ifndef NDEBUG
+  &MutexWin32Impl::unlock_initially,
+#else
+  &noop,
+#endif
+
+  &cvar_wait_initially,
+#ifndef NDEBUG
+  &MutexWin32Impl::unlock_initially,
+  &MutexWin32Impl::unlock_initially,
+#else
+  &noop,
+  &noop,
+#endif
+};
+
+/**
+ * Called the first time a lock is grabbed.
+ */
+void MutexWin32Impl::
+init_lock_funcs() {
+  if (MutexWin32Impl::_funcs._lock != &MutexWin32Impl::lock_initially) {
+    // Already initialized.
+    return;
+  }
+
+#if _WIN32_WINNT >= 0x0600
+  _funcs._lock = (LockFunc)AcquireSRWLockExclusive;
+  _funcs._try_lock = (TryLockFunc)TryAcquireSRWLockExclusive;
+  _funcs._unlock = (LockFunc)ReleaseSRWLockExclusive;
+  _funcs._cvar_wait = (CondWaitFunc)SleepConditionVariableSRW;
+  _funcs._cvar_notify_one = (LockFunc)WakeConditionVariable;
+  _funcs._cvar_notify_all = (LockFunc)WakeAllConditionVariable;
+#else
+  // We don't need to be very thread safe here.  This can only ever be called
+  // at static init time, when there is still only one thread.
+  if (prefer_srwlock) {
+    HMODULE module = GetModuleHandleA("kernel32");
+    if (module != nullptr) {
+      _funcs._lock = (LockFunc)GetProcAddress(module, "AcquireSRWLockExclusive");
+      _funcs._try_lock = (TryLockFunc)GetProcAddress(module, "TryAcquireSRWLockExclusive");
+      _funcs._unlock = (LockFunc)GetProcAddress(module, "ReleaseSRWLockExclusive");
+      _funcs._cvar_wait = (CondWaitFunc)GetProcAddress(module, "SleepConditionVariableSRW");
+      _funcs._cvar_notify_one = (LockFunc)GetProcAddress(module, "WakeConditionVariable");
+      _funcs._cvar_notify_all = (LockFunc)GetProcAddress(module, "WakeAllConditionVariable");
+      if (_funcs._lock != nullptr &&
+          _funcs._try_lock != nullptr &&
+          _funcs._unlock != nullptr &&
+          _funcs._cvar_wait != nullptr &&
+          _funcs._cvar_notify_one != nullptr &&
+          _funcs._cvar_notify_all != nullptr) {
+        return;
+      }
+    }
+  }
+
+  // Fall back to our custom Event-based implementation on Windows XP.
+  _funcs._lock = &lock_xp;
+  _funcs._try_lock = &try_lock_xp;
+  _funcs._unlock = &unlock_xp;
+  _funcs._cvar_wait = &cvar_wait_xp;
+  _funcs._cvar_notify_one = &cvar_notify_one_xp;
+  _funcs._cvar_notify_all = &cvar_notify_all_xp;
+
+  // Are we on a multi-core system?  If so, enable the spinlock.
+  SYSTEM_INFO sysinfo;
+  GetSystemInfo(&sysinfo);
+  if (sysinfo.dwNumberOfProcessors > 1) {
+    effective_spin_count = spin_count;
+  } else {
+    effective_spin_count = 0;
+  }
+#endif  // _WIN32_WINNT < 0x0600
+}
+
+/**
+ * Ensures that the lock functions are initialized at static init time.  This
+ * prevents us from having to implement synchronization in our initialization.
+ */
+class LockFuncsInitializer {
+public:
+  LockFuncsInitializer() {
+    MutexWin32Impl::init_lock_funcs();
+  }
+};
+
+static LockFuncsInitializer _lock_funcs_init;
+
 /**
 /**
  *
  *
  */
  */
-MutexWin32Impl::
-MutexWin32Impl() {
-  InitializeCriticalSectionAndSpinCount(&_lock, 4000);
+ReMutexWin32Impl::
+ReMutexWin32Impl() {
+  InitializeCriticalSectionAndSpinCount(&_lock, spin_count);
 }
 }
 
 
 #endif  // WIN32_VC
 #endif  // WIN32_VC

+ 55 - 4
dtool/src/dtoolbase/mutexWin32Impl.h

@@ -24,11 +24,12 @@
 #include <windows.h>
 #include <windows.h>
 
 
 /**
 /**
- * Uses Windows native calls to implement a mutex.
+ * Uses SRWLock on Vista and above to implement a mutex.  Older versions of
+ * Windows fall back to a combination of a spinlock and an Event object.
  */
  */
 class EXPCL_DTOOL_DTOOLBASE MutexWin32Impl {
 class EXPCL_DTOOL_DTOOLBASE MutexWin32Impl {
 public:
 public:
-  MutexWin32Impl();
+  constexpr MutexWin32Impl() = default;
   MutexWin32Impl(const MutexWin32Impl &copy) = delete;
   MutexWin32Impl(const MutexWin32Impl &copy) = delete;
   INLINE ~MutexWin32Impl();
   INLINE ~MutexWin32Impl();
 
 
@@ -39,10 +40,60 @@ public:
   INLINE bool try_lock();
   INLINE bool try_lock();
   INLINE void unlock();
   INLINE void unlock();
 
 
+  static void init_lock_funcs();
+
 private:
 private:
-  CRITICAL_SECTION _lock;
+#ifndef CPPPARSER
+  // Store function pointers; these point directly to the SRWLock Win32 API
+  // functions on Vista and above, or to our own implementation on Windows XP.
+  typedef void (__stdcall *LockFunc)(volatile PVOID *lock);
+  typedef BOOL (__stdcall *TryLockFunc)(volatile PVOID *lock);
+  typedef BOOL (__stdcall *CondWaitFunc)(volatile PVOID *cvar, volatile PVOID *lock, DWORD, ULONG);
+
+  struct LockFunctions {
+    LockFunc _lock;
+    TryLockFunc _try_lock;
+    LockFunc _unlock;
+
+    CondWaitFunc _cvar_wait;
+    LockFunc _cvar_notify_one;
+    LockFunc _cvar_notify_all;
+  };
+
+  static LockFunctions _funcs;
+
+  static void __stdcall lock_initially(volatile PVOID *lock);
+  static BOOL __stdcall try_lock_initially(volatile PVOID *lock);
+  static void __stdcall unlock_initially(volatile PVOID *lock);
+#endif
+
+private:
+  // In the SRWLock implementation, only the first field is used.  On Windows
+  // XP, the first field contains a waiter count and lock bit, and the second
+  // field contains an Event handle if contention has occurred.
+  volatile PVOID _lock[2] = {nullptr, nullptr};
+
   friend class ConditionVarWin32Impl;
   friend class ConditionVarWin32Impl;
-  friend class ConditionVarFullWin32Impl;
+};
+
+/**
+ * Uses CRITICAL_SECTION to implement a recursive mutex.
+ */
+class EXPCL_DTOOL_DTOOLBASE ReMutexWin32Impl {
+public:
+  ReMutexWin32Impl();
+  ReMutexWin32Impl(const ReMutexWin32Impl &copy) = delete;
+  INLINE ~ReMutexWin32Impl();
+
+  ReMutexWin32Impl &operator = (const ReMutexWin32Impl &copy) = delete;
+
+public:
+  INLINE void lock();
+  INLINE bool try_lock();
+  INLINE void unlock();
+
+private:
+  CRITICAL_SECTION _lock;
 };
 };
 
 
 #include "mutexWin32Impl.I"
 #include "mutexWin32Impl.I"

+ 2 - 1
dtool/src/dtoolbase/pdeque.h

@@ -18,7 +18,7 @@
 #include "pallocator.h"
 #include "pallocator.h"
 #include "register_type.h"
 #include "register_type.h"
 #include <deque>
 #include <deque>
-
+#include <initializer_list>
 
 
 #if !defined(USE_STL_ALLOCATOR) || defined(CPPPARSER)
 #if !defined(USE_STL_ALLOCATOR) || defined(CPPPARSER)
 // If we're not using custom allocators, just use the standard class
 // If we're not using custom allocators, just use the standard class
@@ -40,6 +40,7 @@ public:
   pdeque(TypeHandle type_handle = pdeque_type_handle) : std::deque<Type, pallocator_array<Type> >(allocator(type_handle)) { }
   pdeque(TypeHandle type_handle = pdeque_type_handle) : std::deque<Type, pallocator_array<Type> >(allocator(type_handle)) { }
   pdeque(size_type n, TypeHandle type_handle = pdeque_type_handle) : std::deque<Type, pallocator_array<Type> >(n, Type(), allocator(type_handle)) { }
   pdeque(size_type n, TypeHandle type_handle = pdeque_type_handle) : std::deque<Type, pallocator_array<Type> >(n, Type(), allocator(type_handle)) { }
   pdeque(size_type n, const Type &value, TypeHandle type_handle = pdeque_type_handle) : std::deque<Type, pallocator_array<Type> >(n, value, allocator(type_handle)) { }
   pdeque(size_type n, const Type &value, TypeHandle type_handle = pdeque_type_handle) : std::deque<Type, pallocator_array<Type> >(n, value, allocator(type_handle)) { }
+  pdeque(std::initializer_list<Type> init, TypeHandle type_handle = pdeque_type_handle) : std::deque<Type, pallocator_array<Type> >(std::move(init), allocator(type_handle)) { }
 };
 };
 
 
 #endif  // USE_STL_ALLOCATOR
 #endif  // USE_STL_ALLOCATOR

+ 2 - 0
dtool/src/dtoolbase/plist.h

@@ -18,6 +18,7 @@
 #include "pallocator.h"
 #include "pallocator.h"
 #include "register_type.h"
 #include "register_type.h"
 #include <list>
 #include <list>
+#include <initializer_list>
 
 
 #if !defined(USE_STL_ALLOCATOR) || defined(CPPPARSER)
 #if !defined(USE_STL_ALLOCATOR) || defined(CPPPARSER)
 // If we're not using custom allocators, just use the standard class
 // If we're not using custom allocators, just use the standard class
@@ -40,6 +41,7 @@ public:
   plist(TypeHandle type_handle = plist_type_handle) : base_class(allocator(type_handle)) { }
   plist(TypeHandle type_handle = plist_type_handle) : base_class(allocator(type_handle)) { }
   plist(size_type n, TypeHandle type_handle = plist_type_handle) : base_class(n, Type(), allocator(type_handle)) { }
   plist(size_type n, TypeHandle type_handle = plist_type_handle) : base_class(n, Type(), allocator(type_handle)) { }
   plist(size_type n, const Type &value, TypeHandle type_handle = plist_type_handle) : base_class(n, value, allocator(type_handle)) { }
   plist(size_type n, const Type &value, TypeHandle type_handle = plist_type_handle) : base_class(n, value, allocator(type_handle)) { }
+  plist(std::initializer_list<Type> init, TypeHandle type_handle = plist_type_handle) : base_class(std::move(init), allocator(type_handle)) { }
 
 
   typedef typename base_class::iterator iterator;
   typedef typename base_class::iterator iterator;
   typedef typename base_class::const_iterator const_iterator;
   typedef typename base_class::const_iterator const_iterator;

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

@@ -24,6 +24,8 @@
 #include <hash_set>
 #include <hash_set>
 #endif
 #endif
 
 
+#include <initializer_list>
+
 #if !defined(USE_STL_ALLOCATOR) || defined(CPPPARSER)
 #if !defined(USE_STL_ALLOCATOR) || defined(CPPPARSER)
 // If we're not using custom allocators, just use the standard class
 // If we're not using custom allocators, just use the standard class
 // definition.
 // definition.
@@ -52,6 +54,7 @@ public:
   typedef std::set<Key, Compare, allocator> base_class;
   typedef std::set<Key, Compare, allocator> base_class;
   pset(TypeHandle type_handle = pset_type_handle) : base_class(Compare(), allocator(type_handle)) { }
   pset(TypeHandle type_handle = pset_type_handle) : base_class(Compare(), allocator(type_handle)) { }
   pset(const Compare &comp, TypeHandle type_handle = pset_type_handle) : base_class(comp, type_handle) { }
   pset(const Compare &comp, TypeHandle type_handle = pset_type_handle) : base_class(comp, type_handle) { }
+  pset(std::initializer_list<Key> init, TypeHandle type_handle = pset_type_handle) : base_class(std::move(init), allocator(type_handle)) { }
 
 
 #ifdef USE_TAU
 #ifdef USE_TAU
   std::pair<typename base_class::iterator, bool>
   std::pair<typename base_class::iterator, bool>
@@ -110,6 +113,7 @@ public:
   typedef pallocator_single<Key> allocator;
   typedef pallocator_single<Key> allocator;
   pmultiset(TypeHandle type_handle = pset_type_handle) : std::multiset<Key, Compare, allocator>(Compare(), allocator(type_handle)) { }
   pmultiset(TypeHandle type_handle = pset_type_handle) : std::multiset<Key, Compare, allocator>(Compare(), allocator(type_handle)) { }
   pmultiset(const Compare &comp, TypeHandle type_handle = pset_type_handle) : std::multiset<Key, Compare, allocator>(comp, type_handle) { }
   pmultiset(const Compare &comp, TypeHandle type_handle = pset_type_handle) : std::multiset<Key, Compare, allocator>(comp, type_handle) { }
+  pmultiset(std::initializer_list<Key> init, TypeHandle type_handle = pset_type_handle) : std::multiset<Key, Compare, allocator>(std::move(init), allocator(type_handle)) { }
 };
 };
 
 
 #ifdef HAVE_STL_HASH
 #ifdef HAVE_STL_HASH

+ 2 - 0
dtool/src/dtoolbase/pvector.h

@@ -15,6 +15,7 @@
 #define PVECTOR_H
 #define PVECTOR_H
 
 
 #include <vector>
 #include <vector>
+#include <initializer_list>
 
 
 #include "dtoolbase.h"
 #include "dtoolbase.h"
 #include "pallocator.h"
 #include "pallocator.h"
@@ -51,6 +52,7 @@ public:
   explicit pvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator(type_handle)) { }
   explicit pvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator(type_handle)) { }
   explicit pvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator(type_handle)) { }
   explicit pvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator(type_handle)) { }
   pvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator(type_handle)) { }
   pvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator(type_handle)) { }
+  pvector(std::initializer_list<Type> init, TypeHandle type_handle = pvector_type_handle) : base_class(std::move(init), allocator(type_handle)) { }
 
 
   pvector<Type> &operator =(const pvector<Type> &copy) {
   pvector<Type> &operator =(const pvector<Type> &copy) {
     base_class::operator =(copy);
     base_class::operator =(copy);

+ 0 - 10
dtool/src/dtoolbase/typeRegistry.I

@@ -36,16 +36,6 @@ ptr() {
   return _global_pointer;
   return _global_pointer;
 }
 }
 
 
-/**
- * Ensures the lock pointer has been allocated.
- */
-INLINE void TypeRegistry::
-init_lock() {
-  if (_lock == nullptr) {
-    _lock = new MutexImpl;
-  }
-}
-
 /**
 /**
  * Returns the TypeRegistryNode associated with the indicated TypeHandle.  If
  * Returns the TypeRegistryNode associated with the indicated TypeHandle.  If
  * there is no associated TypeRegistryNode, reports an error condition and
  * there is no associated TypeRegistryNode, reports an error condition and

+ 48 - 50
dtool/src/dtoolbase/typeRegistry.cxx

@@ -25,7 +25,7 @@ using std::ostream;
 using std::ostringstream;
 using std::ostringstream;
 using std::string;
 using std::string;
 
 
-MutexImpl *TypeRegistry::_lock = nullptr;
+MutexImpl TypeRegistry::_lock;
 TypeRegistry *TypeRegistry::_global_pointer = nullptr;
 TypeRegistry *TypeRegistry::_global_pointer = nullptr;
 
 
 /**
 /**
@@ -37,7 +37,7 @@ TypeRegistry *TypeRegistry::_global_pointer = nullptr;
  */
  */
 bool TypeRegistry::
 bool TypeRegistry::
 register_type(TypeHandle &type_handle, const string &name) {
 register_type(TypeHandle &type_handle, const string &name) {
-  _lock->lock();
+  _lock.lock();
 
 
   if (type_handle != TypeHandle::none()) {
   if (type_handle != TypeHandle::none()) {
     // Here's a type that was already registered.  Just make sure everything's
     // Here's a type that was already registered.  Just make sure everything's
@@ -45,7 +45,7 @@ register_type(TypeHandle &type_handle, const string &name) {
     TypeRegistryNode *rnode = look_up(type_handle, nullptr);
     TypeRegistryNode *rnode = look_up(type_handle, nullptr);
     if (&type_handle == &rnode->_ref) {
     if (&type_handle == &rnode->_ref) {
       // No problem.
       // No problem.
-      _lock->unlock();
+      _lock.unlock();
       assert(rnode->_name == name);
       assert(rnode->_name == name);
       return false;
       return false;
     }
     }
@@ -67,7 +67,7 @@ register_type(TypeHandle &type_handle, const string &name) {
     _derivations_fresh = false;
     _derivations_fresh = false;
 
 
     type_handle = new_handle;
     type_handle = new_handle;
-    _lock->unlock();
+    _lock.unlock();
     return true;
     return true;
   }
   }
   TypeRegistryNode *rnode = (*ri).second;
   TypeRegistryNode *rnode = (*ri).second;
@@ -83,7 +83,7 @@ register_type(TypeHandle &type_handle, const string &name) {
 
 
     if (type_handle == rnode->_handle) {
     if (type_handle == rnode->_handle) {
       // No problem.
       // No problem.
-      _lock->unlock();
+      _lock.unlock();
       return false;
       return false;
     }
     }
     // But wait--the type_handle has changed!  We kept a reference to the
     // But wait--the type_handle has changed!  We kept a reference to the
@@ -92,7 +92,7 @@ register_type(TypeHandle &type_handle, const string &name) {
     // time, but now it's different!  Bad juju.
     // time, but now it's different!  Bad juju.
     cerr << "Reregistering " << name << "\n";
     cerr << "Reregistering " << name << "\n";
     type_handle = rnode->_handle;
     type_handle = rnode->_handle;
-    _lock->unlock();
+    _lock.unlock();
     return false;
     return false;
   }
   }
 
 
@@ -108,7 +108,7 @@ register_type(TypeHandle &type_handle, const string &name) {
 
 
     type_handle = rnode->_handle;
     type_handle = rnode->_handle;
   }
   }
-  _lock->unlock();
+  _lock.unlock();
   return false;
   return false;
 }
 }
 
 
@@ -119,7 +119,7 @@ register_type(TypeHandle &type_handle, const string &name) {
  */
  */
 TypeHandle TypeRegistry::
 TypeHandle TypeRegistry::
 register_dynamic_type(const string &name) {
 register_dynamic_type(const string &name) {
-  _lock->lock();
+  _lock.lock();
 
 
   NameRegistry::iterator ri;
   NameRegistry::iterator ri;
   ri = _name_registry.find(name);
   ri = _name_registry.find(name);
@@ -139,14 +139,14 @@ register_dynamic_type(const string &name) {
     _name_registry[name] = rnode;
     _name_registry[name] = rnode;
     _derivations_fresh = false;
     _derivations_fresh = false;
 
 
-    _lock->unlock();
+    _lock.unlock();
     return *new_handle;
     return *new_handle;
   }
   }
 
 
   // Return the TypeHandle previously obtained.
   // Return the TypeHandle previously obtained.
   TypeRegistryNode *rnode = (*ri).second;
   TypeRegistryNode *rnode = (*ri).second;
   TypeHandle handle = rnode->_handle;
   TypeHandle handle = rnode->_handle;
-  _lock->unlock();
+  _lock.unlock();
   return handle;
   return handle;
 }
 }
 
 
@@ -157,7 +157,7 @@ register_dynamic_type(const string &name) {
  */
  */
 void TypeRegistry::
 void TypeRegistry::
 record_derivation(TypeHandle child, TypeHandle parent) {
 record_derivation(TypeHandle child, TypeHandle parent) {
-  _lock->lock();
+  _lock.lock();
 
 
   TypeRegistryNode *cnode = look_up(child, nullptr);
   TypeRegistryNode *cnode = look_up(child, nullptr);
   assert(cnode != nullptr);
   assert(cnode != nullptr);
@@ -176,7 +176,7 @@ record_derivation(TypeHandle child, TypeHandle parent) {
     _derivations_fresh = false;
     _derivations_fresh = false;
   }
   }
 
 
-  _lock->unlock();
+  _lock.unlock();
 }
 }
 
 
 /**
 /**
@@ -187,7 +187,7 @@ record_derivation(TypeHandle child, TypeHandle parent) {
  */
  */
 void TypeRegistry::
 void TypeRegistry::
 record_alternate_name(TypeHandle type, const string &name) {
 record_alternate_name(TypeHandle type, const string &name) {
-  _lock->lock();
+  _lock.lock();
 
 
   TypeRegistryNode *rnode = look_up(type, nullptr);
   TypeRegistryNode *rnode = look_up(type, nullptr);
   if (rnode != nullptr) {
   if (rnode != nullptr) {
@@ -195,7 +195,7 @@ record_alternate_name(TypeHandle type, const string &name) {
       _name_registry.insert(NameRegistry::value_type(name, rnode)).first;
       _name_registry.insert(NameRegistry::value_type(name, rnode)).first;
 
 
     if ((*ri).second != rnode) {
     if ((*ri).second != rnode) {
-      _lock->unlock();
+      _lock.unlock();
       cerr
       cerr
         << "Name " << name << " already assigned to TypeHandle "
         << "Name " << name << " already assigned to TypeHandle "
         << rnode->_name << "; cannot reassign to " << type << "\n";
         << rnode->_name << "; cannot reassign to " << type << "\n";
@@ -204,24 +204,24 @@ record_alternate_name(TypeHandle type, const string &name) {
 
 
   }
   }
 
 
-  _lock->unlock();
+  _lock.unlock();
 }
 }
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
 /**
 /**
  * Records the given Python type pointer in the type registry for the benefit
  * Records the given Python type pointer in the type registry for the benefit
- * of interrogate.
+ * of interrogate, which expects this to contain a Dtool_PyTypedObject.
  */
  */
 void TypeRegistry::
 void TypeRegistry::
 record_python_type(TypeHandle type, PyObject *python_type) {
 record_python_type(TypeHandle type, PyObject *python_type) {
-  _lock->lock();
+  _lock.lock();
 
 
   TypeRegistryNode *rnode = look_up(type, nullptr);
   TypeRegistryNode *rnode = look_up(type, nullptr);
   if (rnode != nullptr) {
   if (rnode != nullptr) {
     rnode->_python_type = python_type;
     rnode->_python_type = python_type;
   }
   }
 
 
-  _lock->unlock();
+  _lock.unlock();
 }
 }
 #endif
 #endif
 
 
@@ -231,7 +231,7 @@ record_python_type(TypeHandle type, PyObject *python_type) {
  */
  */
 TypeHandle TypeRegistry::
 TypeHandle TypeRegistry::
 find_type(const string &name) const {
 find_type(const string &name) const {
-  _lock->lock();
+  _lock.lock();
 
 
   TypeHandle handle = TypeHandle::none();
   TypeHandle handle = TypeHandle::none();
   NameRegistry::const_iterator ri;
   NameRegistry::const_iterator ri;
@@ -239,7 +239,7 @@ find_type(const string &name) const {
   if (ri != _name_registry.end()) {
   if (ri != _name_registry.end()) {
     handle = (*ri).second->_handle;
     handle = (*ri).second->_handle;
   }
   }
-  _lock->unlock();
+  _lock.unlock();
 
 
   return handle;
   return handle;
 }
 }
@@ -271,11 +271,11 @@ find_type_by_id(int id) const {
  */
  */
 string TypeRegistry::
 string TypeRegistry::
 get_name(TypeHandle type, TypedObject *object) const {
 get_name(TypeHandle type, TypedObject *object) const {
-  _lock->lock();
+  _lock.lock();
   TypeRegistryNode *rnode = look_up(type, object);
   TypeRegistryNode *rnode = look_up(type, object);
   assert(rnode != nullptr);
   assert(rnode != nullptr);
   string name = rnode->_name;
   string name = rnode->_name;
-  _lock->unlock();
+  _lock.unlock();
 
 
   return name;
   return name;
 }
 }
@@ -296,7 +296,7 @@ get_name(TypeHandle type, TypedObject *object) const {
 bool TypeRegistry::
 bool TypeRegistry::
 is_derived_from(TypeHandle child, TypeHandle base,
 is_derived_from(TypeHandle child, TypeHandle base,
                 TypedObject *child_object) {
                 TypedObject *child_object) {
-  _lock->lock();
+  _lock.lock();
 
 
   const TypeRegistryNode *child_node = look_up(child, child_object);
   const TypeRegistryNode *child_node = look_up(child, child_object);
   const TypeRegistryNode *base_node = look_up(base, nullptr);
   const TypeRegistryNode *base_node = look_up(base, nullptr);
@@ -307,7 +307,7 @@ is_derived_from(TypeHandle child, TypeHandle base,
   freshen_derivations();
   freshen_derivations();
 
 
   bool result = TypeRegistryNode::is_derived_from(child_node, base_node);
   bool result = TypeRegistryNode::is_derived_from(child_node, base_node);
-  _lock->unlock();
+  _lock.unlock();
   return result;
   return result;
 }
 }
 
 
@@ -316,9 +316,9 @@ is_derived_from(TypeHandle child, TypeHandle base,
  */
  */
 int TypeRegistry::
 int TypeRegistry::
 get_num_typehandles() {
 get_num_typehandles() {
-  _lock->lock();
+  _lock.lock();
   int num_types = (int)_handle_registry.size();
   int num_types = (int)_handle_registry.size();
-  _lock->unlock();
+  _lock.unlock();
   return num_types;
   return num_types;
 }
 }
 
 
@@ -327,12 +327,12 @@ get_num_typehandles() {
  */
  */
 TypeHandle TypeRegistry::
 TypeHandle TypeRegistry::
 get_typehandle(int n) {
 get_typehandle(int n) {
-  _lock->lock();
+  _lock.lock();
   TypeRegistryNode *rnode = nullptr;
   TypeRegistryNode *rnode = nullptr;
   if (n >= 0 && n < (int)_handle_registry.size()) {
   if (n >= 0 && n < (int)_handle_registry.size()) {
     rnode = _handle_registry[n];
     rnode = _handle_registry[n];
   }
   }
-  _lock->unlock();
+  _lock.unlock();
 
 
   if (rnode != nullptr) {
   if (rnode != nullptr) {
     return rnode->_handle;
     return rnode->_handle;
@@ -347,10 +347,10 @@ get_typehandle(int n) {
  */
  */
 int TypeRegistry::
 int TypeRegistry::
 get_num_root_classes() {
 get_num_root_classes() {
-  _lock->lock();
+  _lock.lock();
   freshen_derivations();
   freshen_derivations();
   int num_roots = (int)_root_classes.size();
   int num_roots = (int)_root_classes.size();
-  _lock->unlock();
+  _lock.unlock();
   return num_roots;
   return num_roots;
 }
 }
 
 
@@ -359,7 +359,7 @@ get_num_root_classes() {
  */
  */
 TypeHandle TypeRegistry::
 TypeHandle TypeRegistry::
 get_root_class(int n) {
 get_root_class(int n) {
-  _lock->lock();
+  _lock.lock();
   freshen_derivations();
   freshen_derivations();
   TypeHandle handle;
   TypeHandle handle;
   if (n >= 0 && n < (int)_root_classes.size()) {
   if (n >= 0 && n < (int)_root_classes.size()) {
@@ -367,7 +367,7 @@ get_root_class(int n) {
   } else {
   } else {
     handle = TypeHandle::none();
     handle = TypeHandle::none();
   }
   }
-  _lock->unlock();
+  _lock.unlock();
 
 
   return handle;
   return handle;
 }
 }
@@ -385,11 +385,11 @@ get_root_class(int n) {
  */
  */
 int TypeRegistry::
 int TypeRegistry::
 get_num_parent_classes(TypeHandle child, TypedObject *child_object) const {
 get_num_parent_classes(TypeHandle child, TypedObject *child_object) const {
-  _lock->lock();
+  _lock.lock();
   TypeRegistryNode *rnode = look_up(child, child_object);
   TypeRegistryNode *rnode = look_up(child, child_object);
   assert(rnode != nullptr);
   assert(rnode != nullptr);
   int num_parents = (int)rnode->_parent_classes.size();
   int num_parents = (int)rnode->_parent_classes.size();
-  _lock->unlock();
+  _lock.unlock();
   return num_parents;
   return num_parents;
 }
 }
 
 
@@ -399,7 +399,7 @@ get_num_parent_classes(TypeHandle child, TypedObject *child_object) const {
  */
  */
 TypeHandle TypeRegistry::
 TypeHandle TypeRegistry::
 get_parent_class(TypeHandle child, int index) const {
 get_parent_class(TypeHandle child, int index) const {
-  _lock->lock();
+  _lock.lock();
   TypeHandle handle;
   TypeHandle handle;
   TypeRegistryNode *rnode = look_up(child, nullptr);
   TypeRegistryNode *rnode = look_up(child, nullptr);
   assert(rnode != nullptr);
   assert(rnode != nullptr);
@@ -408,7 +408,7 @@ get_parent_class(TypeHandle child, int index) const {
   } else {
   } else {
     handle = TypeHandle::none();
     handle = TypeHandle::none();
   }
   }
-  _lock->unlock();
+  _lock.unlock();
   return handle;
   return handle;
 }
 }
 
 
@@ -422,11 +422,11 @@ get_parent_class(TypeHandle child, int index) const {
  */
  */
 int TypeRegistry::
 int TypeRegistry::
 get_num_child_classes(TypeHandle child, TypedObject *child_object) const {
 get_num_child_classes(TypeHandle child, TypedObject *child_object) const {
-  _lock->lock();
+  _lock.lock();
   TypeRegistryNode *rnode = look_up(child, child_object);
   TypeRegistryNode *rnode = look_up(child, child_object);
   assert(rnode != nullptr);
   assert(rnode != nullptr);
   int num_children = (int)rnode->_child_classes.size();
   int num_children = (int)rnode->_child_classes.size();
-  _lock->unlock();
+  _lock.unlock();
   return num_children;
   return num_children;
 }
 }
 
 
@@ -436,7 +436,7 @@ get_num_child_classes(TypeHandle child, TypedObject *child_object) const {
  */
  */
 TypeHandle TypeRegistry::
 TypeHandle TypeRegistry::
 get_child_class(TypeHandle child, int index) const {
 get_child_class(TypeHandle child, int index) const {
-  _lock->lock();
+  _lock.lock();
   TypeHandle handle;
   TypeHandle handle;
   TypeRegistryNode *rnode = look_up(child, nullptr);
   TypeRegistryNode *rnode = look_up(child, nullptr);
   assert(rnode != nullptr);
   assert(rnode != nullptr);
@@ -445,7 +445,7 @@ get_child_class(TypeHandle child, int index) const {
   } else {
   } else {
     handle = TypeHandle::none();
     handle = TypeHandle::none();
   }
   }
-  _lock->unlock();
+  _lock.unlock();
   return handle;
   return handle;
 }
 }
 
 
@@ -462,7 +462,7 @@ get_child_class(TypeHandle child, int index) const {
 TypeHandle TypeRegistry::
 TypeHandle TypeRegistry::
 get_parent_towards(TypeHandle child, TypeHandle base,
 get_parent_towards(TypeHandle child, TypeHandle base,
                    TypedObject *child_object) {
                    TypedObject *child_object) {
-  _lock->lock();
+  _lock.lock();
   TypeHandle handle;
   TypeHandle handle;
   const TypeRegistryNode *child_node = look_up(child, child_object);
   const TypeRegistryNode *child_node = look_up(child, child_object);
   const TypeRegistryNode *base_node = look_up(base, nullptr);
   const TypeRegistryNode *base_node = look_up(base, nullptr);
@@ -470,7 +470,7 @@ get_parent_towards(TypeHandle child, TypeHandle base,
          base_node != nullptr);
          base_node != nullptr);
   freshen_derivations();
   freshen_derivations();
   handle = TypeRegistryNode::get_parent_towards(child_node, base_node);
   handle = TypeRegistryNode::get_parent_towards(child_node, base_node);
-  _lock->unlock();
+  _lock.unlock();
   return handle;
   return handle;
 }
 }
 
 
@@ -484,8 +484,7 @@ get_parent_towards(TypeHandle child, TypeHandle base,
  */
  */
 void TypeRegistry::
 void TypeRegistry::
 reregister_types() {
 reregister_types() {
-  init_lock();
-  _lock->lock();
+  _lock.lock();
   HandleRegistry::iterator ri;
   HandleRegistry::iterator ri;
   TypeRegistry *reg = ptr();
   TypeRegistry *reg = ptr();
   for (ri = reg->_handle_registry.begin();
   for (ri = reg->_handle_registry.begin();
@@ -496,7 +495,7 @@ reregister_types() {
       cerr << "Reregistering " << rnode->_name << "\n";
       cerr << "Reregistering " << rnode->_name << "\n";
     }
     }
   }
   }
-  _lock->unlock();
+  _lock.unlock();
 }
 }
 
 
 
 
@@ -506,9 +505,9 @@ reregister_types() {
  */
  */
 void TypeRegistry::
 void TypeRegistry::
 write(ostream &out) const {
 write(ostream &out) const {
-  _lock->lock();
+  _lock.lock();
   do_write(out);
   do_write(out);
-  _lock->unlock();
+  _lock.unlock();
 }
 }
 
 
 /**
 /**
@@ -540,7 +539,6 @@ TypeRegistry() {
  */
  */
 void TypeRegistry::
 void TypeRegistry::
 init_global_pointer() {
 init_global_pointer() {
-  init_lock();
   init_memory_hook();
   init_memory_hook();
   _global_pointer = new TypeRegistry;
   _global_pointer = new TypeRegistry;
 }
 }
@@ -636,9 +634,9 @@ look_up_invalid(TypeHandle handle, TypedObject *object) const {
       // But we're lucky enough to have a TypedObject pointer handy!  Maybe we
       // But we're lucky enough to have a TypedObject pointer handy!  Maybe we
       // can use it to resolve the error.  We have to drop the lock while we
       // can use it to resolve the error.  We have to drop the lock while we
       // do this, so we don't get a recursive lock.
       // do this, so we don't get a recursive lock.
-      _lock->unlock();
+      _lock.unlock();
       handle = object->force_init_type();
       handle = object->force_init_type();
-      _lock->lock();
+      _lock.lock();
 
 
       if (handle._index == 0) {
       if (handle._index == 0) {
         // Strange.
         // Strange.

+ 1 - 3
dtool/src/dtoolbase/typeRegistry.h

@@ -101,8 +101,6 @@ private:
   void write_node(std::ostream &out, int indent_level,
   void write_node(std::ostream &out, int indent_level,
                   const TypeRegistryNode *node) const;
                   const TypeRegistryNode *node) const;
 
 
-  static INLINE void init_lock();
-
   typedef std::vector<TypeRegistryNode *> HandleRegistry;
   typedef std::vector<TypeRegistryNode *> HandleRegistry;
   HandleRegistry _handle_registry;
   HandleRegistry _handle_registry;
 
 
@@ -114,7 +112,7 @@ private:
 
 
   bool _derivations_fresh;
   bool _derivations_fresh;
 
 
-  static MutexImpl *_lock;
+  static MutexImpl _lock;
   static TypeRegistry *_global_pointer;
   static TypeRegistry *_global_pointer;
 
 
   friend class TypeHandle;
   friend class TypeHandle;

+ 3 - 0
dtool/src/dtoolutil/load_dso.cxx

@@ -132,6 +132,9 @@ void *
 load_dso(const DSearchPath &path, const Filename &filename) {
 load_dso(const DSearchPath &path, const Filename &filename) {
   Filename abspath = resolve_dso(path, filename);
   Filename abspath = resolve_dso(path, filename);
   if (!abspath.is_regular_file()) {
   if (!abspath.is_regular_file()) {
+    // Make sure the error flag is cleared, to prevent a subsequent call to
+    // load_dso_error() from returning a previously stored error.
+    dlerror();
     return nullptr;
     return nullptr;
   }
   }
   string os_specific = abspath.to_os_specific();
   string os_specific = abspath.to_os_specific();

+ 16 - 3
dtool/src/interrogate/functionRemap.cxx

@@ -397,17 +397,25 @@ get_call_str(const string &container, const vector_string &pexprs) const {
     }
     }
 
 
     // It's not possible to assign arrays in C++, we have to copy them.
     // It's not possible to assign arrays in C++, we have to copy them.
-    CPPArrayType *array_type = _parameters[_first_true_parameter]._remap->get_orig_type()->as_array_type();
+    bool paren_close = false;
+    CPPType *param_type = _parameters[_first_true_parameter]._remap->get_orig_type();
+    CPPArrayType *array_type = param_type->as_array_type();
     if (array_type != nullptr) {
     if (array_type != nullptr) {
       call << "std::copy(" << expr << ", " << expr << " + " << *array_type->_bounds << ", ";
       call << "std::copy(" << expr << ", " << expr << " + " << *array_type->_bounds << ", ";
-    } else {
+      paren_close = true;
+    }
+    else if (TypeManager::is_pointer_to_PyObject(param_type)) {
+      call << "Dtool_Assign_PyObject(" << expr << ", ";
+      paren_close = true;
+    }
+    else {
       call << expr << " = ";
       call << expr << " = ";
     }
     }
 
 
     _parameters[_first_true_parameter]._remap->pass_parameter(call,
     _parameters[_first_true_parameter]._remap->pass_parameter(call,
                     get_parameter_expr(_first_true_parameter, pexprs));
                     get_parameter_expr(_first_true_parameter, pexprs));
 
 
-    if (array_type != nullptr) {
+    if (paren_close) {
       call << ')';
       call << ')';
     }
     }
 
 
@@ -772,6 +780,11 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
     _return_value_destructor = builder.get_destructor_for(return_meat_type);
     _return_value_destructor = builder.get_destructor_for(return_meat_type);
   }
   }
 
 
+  if (_type == T_getter && TypeManager::is_pointer_to_PyObject(return_type)) {
+    _manage_reference_count = true;
+    _return_value_needs_management = true;
+  }
+
   // Check for a special meaning by name and signature.
   // Check for a special meaning by name and signature.
   size_t first_param = 0;
   size_t first_param = 0;
   if (_has_this) {
   if (_has_this) {

+ 30 - 6
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -1690,7 +1690,6 @@ write_module_class(ostream &out, Object *obj) {
             SlottedFunctionDef def;
             SlottedFunctionDef def;
             def._answer_location = true_key;
             def._answer_location = true_key;
             def._wrapper_type = slotted_def._wrapper_type;
             def._wrapper_type = slotted_def._wrapper_type;
-            def._min_version = 0x03000000;
             def._wrapper_name = func->_name + "_" + true_key;
             def._wrapper_name = func->_name + "_" + true_key;
             slots[true_key] = def;
             slots[true_key] = def;
           }
           }
@@ -1860,6 +1859,12 @@ write_module_class(ostream &out, Object *obj) {
           } else {
           } else {
             return_flags |= RF_pyobject;
             return_flags |= RF_pyobject;
           }
           }
+          bool all_nonconst = true;
+          for (FunctionRemap *remap : def._remaps) {
+            if (remap->_const_method) {
+              all_nonconst = false;
+            }
+          }
           out << "//////////////////\n";
           out << "//////////////////\n";
           out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
           out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
           out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
           out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
@@ -1872,9 +1877,18 @@ write_module_class(ostream &out, Object *obj) {
             // This is for things like __sub__, which Python likes to call on
             // This is for things like __sub__, which Python likes to call on
             // the wrong-type objects.
             // the wrong-type objects.
             out << "  DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n";
             out << "  DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n";
-            out << "  if (local_this == nullptr) {\n";
+            if (all_nonconst) {
+              out << "  if (local_this == nullptr || DtoolInstance_IS_CONST(self)) {\n";
+            } else {
+              out << "  if (local_this == nullptr) {\n";
+            }
             out << "    Py_INCREF(Py_NotImplemented);\n";
             out << "    Py_INCREF(Py_NotImplemented);\n";
             out << "    return Py_NotImplemented;\n";
             out << "    return Py_NotImplemented;\n";
+          } else if (all_nonconst) {
+            out << "  if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_"
+                << ClassName << ", (void **)&local_this, \"" << ClassName
+                << "." << methodNameFromCppName(fname, "", false) << "\")) {\n";
+            out << "    return nullptr;\n";
           } else {
           } else {
             out << "  if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
             out << "  if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
             out << "    return nullptr;\n";
             out << "    return nullptr;\n";
@@ -1883,7 +1897,7 @@ write_module_class(ostream &out, Object *obj) {
 
 
           string expected_params;
           string expected_params;
           write_function_forset(out, def._remaps, 1, 1, expected_params, 2, true, true,
           write_function_forset(out, def._remaps, 1, 1, expected_params, 2, true, true,
-                                AT_single_arg, return_flags, false);
+                                AT_single_arg, return_flags, false, !all_nonconst);
 
 
           if (rfi->second._wrapper_type != WT_one_param) {
           if (rfi->second._wrapper_type != WT_one_param) {
             out << "  Py_INCREF(Py_NotImplemented);\n";
             out << "  Py_INCREF(Py_NotImplemented);\n";
@@ -4701,7 +4715,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
 
 
         if (is_optional) {
         if (is_optional) {
           extra_convert
           extra_convert
-            << "wchar_t *" << param_name << "_str;\n"
+            << "wchar_t *" << param_name << "_str = nullptr;\n"
             << "if (" << param_name << " != nullptr) {\n"
             << "if (" << param_name << " != nullptr) {\n"
             << "#if PY_VERSION_HEX >= 0x03030000\n"
             << "#if PY_VERSION_HEX >= 0x03030000\n"
             << "  " << param_name << "_str = PyUnicode_AsWideCharString(" << param_name << ", nullptr);\n"
             << "  " << param_name << "_str = PyUnicode_AsWideCharString(" << param_name << ", nullptr);\n"
@@ -4748,7 +4762,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
         if (is_optional) {
         if (is_optional) {
           extra_convert
           extra_convert
             << "Py_ssize_t " << param_name << "_len;\n"
             << "Py_ssize_t " << param_name << "_len;\n"
-            << "wchar_t *" << param_name << "_str;\n"
+            << "wchar_t *" << param_name << "_str = nullptr;\n"
             << "std::wstring " << param_name << "_wstr;\n"
             << "std::wstring " << param_name << "_wstr;\n"
             << "if (" << param_name << " != nullptr) {\n"
             << "if (" << param_name << " != nullptr) {\n"
             << "#if PY_VERSION_HEX >= 0x03030000\n"
             << "#if PY_VERSION_HEX >= 0x03030000\n"
@@ -5980,7 +5994,11 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       indent(out, indent_level) << "}\n";
       indent(out, indent_level) << "}\n";
     }
     }
 
 
-    return_expr = manage_return_value(out, indent_level, remap, "return_value");
+    if (TypeManager::is_pointer_to_PyObject(remap->_return_type->get_orig_type())) {
+      indent(out, indent_level) << "Py_XINCREF(return_value);\n";
+    } else {
+      return_expr = manage_return_value(out, indent_level, remap, "return_value");
+    }
     return_expr = remap->_return_type->temporary_to_return(return_expr);
     return_expr = remap->_return_type->temporary_to_return(return_expr);
   }
   }
 
 
@@ -7526,6 +7544,12 @@ is_remap_legal(FunctionRemap *remap) {
     }
     }
   }
   }
 
 
+  // Don't export global operators.
+  if (!remap->_has_this &&
+      remap->_cppfunc->get_simple_name().compare(0, 9, "operator ") == 0) {
+    return false;
+  }
+
   // ok all looks ok.
   // ok all looks ok.
   return true;
   return true;
 }
 }

+ 1 - 1
dtool/src/interrogate/interfaceMakerPythonNative.h

@@ -121,7 +121,7 @@ private:
   public:
   public:
     std::string _answer_location;
     std::string _answer_location;
     WrapperType _wrapper_type;
     WrapperType _wrapper_type;
-    int _min_version;
+    int _min_version = 0;
     std::string _wrapper_name;
     std::string _wrapper_name;
     std::set<FunctionRemap*> _remaps;
     std::set<FunctionRemap*> _remaps;
     bool _keep_method;
     bool _keep_method;

+ 13 - 0
dtool/src/interrogatedb/py_panda.I

@@ -90,6 +90,19 @@ INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, i
   Py_RETURN_RICHCOMPARE(cmpval, 0, op);
   Py_RETURN_RICHCOMPARE(cmpval, 0, op);
 }
 }
 
 
+/**
+ * Utility function for assigning a PyObject pointer while managing refcounts.
+ */
+ALWAYS_INLINE void
+Dtool_Assign_PyObject(PyObject *&ptr, PyObject *value) {
+  PyObject *prev_value = ptr;
+  if (prev_value != value) {
+    Py_XINCREF(value);
+    ptr = value;
+    Py_XDECREF(prev_value);
+  }
+}
+
 /**
 /**
  * Converts the enum value to a C long.
  * Converts the enum value to a C long.
  */
  */

+ 2 - 0
dtool/src/interrogatedb/py_panda.h

@@ -238,6 +238,8 @@ EXPCL_PYPANDA PyObject *_Dtool_Return(PyObject *value);
 #define Dtool_Return(value) _Dtool_Return(value)
 #define Dtool_Return(value) _Dtool_Return(value)
 #endif
 #endif
 
 
+ALWAYS_INLINE void Dtool_Assign_PyObject(PyObject *&ptr, PyObject *value);
+
 /**
 /**
  * Wrapper around Python 3.4's enum library, which does not have a C API.
  * Wrapper around Python 3.4's enum library, which does not have a C API.
  */
  */

+ 5 - 0
dtool/src/interrogatedb/py_wrappers.cxx

@@ -47,6 +47,7 @@ static void Dtool_WrapperBase_dealloc(PyObject *self) {
   Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
   Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
   nassertv(wrap);
   nassertv(wrap);
   Py_XDECREF(wrap->_self);
   Py_XDECREF(wrap->_self);
+  Py_TYPE(self)->tp_free(self);
 }
 }
 
 
 static PyObject *Dtool_WrapperBase_repr(PyObject *self) {
 static PyObject *Dtool_WrapperBase_repr(PyObject *self) {
@@ -1091,7 +1092,11 @@ static PyObject *Dtool_GeneratorWrapper_iternext(PyObject *self) {
  */
  */
 static void
 static void
 Dtool_StaticProperty_dealloc(PyDescrObject *descr) {
 Dtool_StaticProperty_dealloc(PyDescrObject *descr) {
+#if PY_VERSION_HEX >= 0x03080000
+  PyObject_GC_UnTrack(descr);
+#else
   _PyObject_GC_UNTRACK(descr);
   _PyObject_GC_UNTRACK(descr);
+#endif
   Py_XDECREF(descr->d_type);
   Py_XDECREF(descr->d_type);
   Py_XDECREF(descr->d_name);
   Py_XDECREF(descr->d_name);
 //#if PY_MAJOR_VERSION >= 3
 //#if PY_MAJOR_VERSION >= 3

+ 46 - 0
dtool/src/parser-inc/atomic

@@ -16,4 +16,50 @@ namespace std {
   template<class T> struct atomic<T*>;
   template<class T> struct atomic<T*>;
 
 
   struct atomic_flag;
   struct atomic_flag;
+
+  using atomic_bool = atomic<bool>;
+  using atomic_char = atomic<char>;
+  using atomic_schar = atomic<signed char>;
+  using atomic_uchar = atomic<unsigned char>;
+  using atomic_short = atomic<short>;
+  using atomic_ushort = atomic<unsigned short>;
+  using atomic_int = atomic<int>;
+  using atomic_uint = atomic<unsigned int>;
+  using atomic_long = atomic<long>;
+  using atomic_ulong = atomic<unsigned long>;
+  using atomic_llong = atomic<long long>;
+  using atomic_ullong = atomic<unsigned long long>;
+  using atomic_char16_t = atomic<char16_t>;
+  using atomic_char32_t = atomic<char32_t>;
+  using atomic_wchar_t = atomic<wchar_t>;
+  using atomic_int8_t = atomic<int8_t>;
+  using atomic_uint8_t = atomic<uint8_t>;
+  using atomic_int16_t = atomic<int16_t>;
+  using atomic_uint16_t = atomic<uint16_t>;
+  using atomic_int32_t = atomic<int32_t>;
+  using atomic_uint32_t = atomic<uint32_t>;
+  using atomic_int64_t = atomic<int64_t>;
+  using atomic_uint64_t = atomic<uint64_t>;
+  using atomic_int_least8_t = atomic<int_least8_t>;
+  using atomic_uint_least8_t = atomic<uint_least8_t>;
+  using atomic_int_least16_t = atomic<int_least16_t>;
+  using atomic_uint_least16_t = atomic<uint_least16_t>;
+  using atomic_int_least32_t = atomic<int_least32_t>;
+  using atomic_uint_least32_t = atomic<uint_least32_t>;
+  using atomic_int_least64_t = atomic<int_least64_t>;
+  using atomic_uint_least64_t = atomic<uint_least64_t>;
+  using atomic_int_fast8_t = atomic<int_fast8_t>;
+  using atomic_uint_fast8_t = atomic<uint_fast8_t>;
+  using atomic_int_fast16_t = atomic<int_fast16_t>;
+  using atomic_uint_fast16_t = atomic<uint_fast16_t>;
+  using atomic_int_fast32_t = atomic<int_fast32_t>;
+  using atomic_uint_fast32_t = atomic<uint_fast32_t>;
+  using atomic_int_fast64_t = atomic<int_fast64_t>;
+  using atomic_uint_fast64_t = atomic<uint_fast64_t>;
+  using atomic_intptr_t = atomic<intptr_t>;
+  using atomic_uintptr_t = atomic<uintptr_t>;
+  using atomic_size_t = atomic<size_t>;
+  using atomic_ptrdiff_t = atomic<ptrdiff_t>;
+  using atomic_intmax_t = atomic<intmax_t>;
+  using atomic_uintmax_t = atomic<uintmax_t>;
 }
 }

+ 10 - 0
dtool/src/parser-inc/synchapi.h

@@ -0,0 +1,10 @@
+#ifndef SYNCHAPI_H
+#define SYNCHAPI_H
+
+#include "winnt.h"
+
+#define SRWLOCK_INIT RTL_SRWLOCK_INIT
+
+typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK;
+
+#endif

+ 16 - 0
dtool/src/parser-inc/winnt.h

@@ -0,0 +1,16 @@
+#ifndef WINNT_H
+#define WINNT_H
+
+typedef void *PVOID;
+
+typedef struct _RTL_SRWLOCK {
+  PVOID Ptr;
+} RTL_SRWLOCK, *PRTL_SRWLOCK;
+
+#define RTL_SRWLOCK_INIT {0}
+
+typedef struct _RTL_CONDITION_VARIABLE {
+  PVOID Ptr;
+} RTL_CONDITION_VARIABLE, *PRTL_CONDITION_VARIABLE;
+
+#endif

+ 6 - 9
dtool/src/prc/configDeclaration.cxx

@@ -21,6 +21,8 @@
 
 
 using std::string;
 using std::string;
 
 
+static MutexImpl this_prc_dir_lock;
+
 /**
 /**
  * Use the ConfigPage::make_declaration() interface to create a new
  * Use the ConfigPage::make_declaration() interface to create a new
  * declaration.
  * declaration.
@@ -139,13 +141,6 @@ set_double_word(size_t n, double value) {
  */
  */
 Filename ConfigDeclaration::
 Filename ConfigDeclaration::
 get_filename_value() const {
 get_filename_value() const {
-  // Since we are about to set THIS_PRC_DIR globally, we need to ensure that
-  // no two threads call this method at the same time.
-  // NB. MSVC doesn't guarantee that this mutex is initialized in a
-  // thread-safe manner.  But chances are that the first time this is called
-  // is at static init time, when there is no risk of data races.
-  static MutexImpl lock;
-
   string str = _string_value;
   string str = _string_value;
 
 
   // Are there any variables to be expanded?
   // Are there any variables to be expanded?
@@ -153,11 +148,13 @@ get_filename_value() const {
     Filename page_filename(_page->get_name());
     Filename page_filename(_page->get_name());
     Filename page_dirname = page_filename.get_dirname();
     Filename page_dirname = page_filename.get_dirname();
 
 
-    lock.lock();
+    // Since we are about to set THIS_PRC_DIR globally, we need to ensure that
+    // no two threads call this method at the same time.
+    this_prc_dir_lock.lock();
     ExecutionEnvironment::shadow_environment_variable("THIS_PRC_DIR", page_dirname.to_os_specific());
     ExecutionEnvironment::shadow_environment_variable("THIS_PRC_DIR", page_dirname.to_os_specific());
     str = ExecutionEnvironment::expand_string(str);
     str = ExecutionEnvironment::expand_string(str);
     ExecutionEnvironment::clear_shadow("THIS_PRC_DIR");
     ExecutionEnvironment::clear_shadow("THIS_PRC_DIR");
-    lock.unlock();
+    this_prc_dir_lock.unlock();
   }
   }
 
 
   Filename fn;
   Filename fn;

+ 6 - 1
dtool/src/prc/configPageManager.cxx

@@ -129,7 +129,12 @@ reload_implicit_pages() {
 #else
 #else
   const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(NULL, RTLD_NOW), "blobinfo");
   const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(NULL, RTLD_NOW), "blobinfo");
 #endif
 #endif
-  if (blobinfo != nullptr && (blobinfo->version == 0 || blobinfo->num_pointers < 10)) {
+  if (blobinfo == nullptr) {
+#ifndef _MSC_VER
+    // Clear the error flag.
+    dlerror();
+#endif
+  } else if (blobinfo->version == 0 || blobinfo->num_pointers < 10) {
     blobinfo = nullptr;
     blobinfo = nullptr;
   }
   }
 
 

+ 5 - 1
dtool/src/prc/configVariableBool.cxx

@@ -12,6 +12,7 @@
  */
  */
 
 
 #include "configVariableBool.h"
 #include "configVariableBool.h"
+#include "mutexImpl.h"
 
 
 /**
 /**
  * Refreshes the cached value.
  * Refreshes the cached value.
@@ -21,7 +22,10 @@ reload_value() const {
   // NB. MSVC doesn't guarantee that this mutex is initialized in a
   // NB. MSVC doesn't guarantee that this mutex is initialized in a
   // thread-safe manner.  But chances are that the first time this is called
   // thread-safe manner.  But chances are that the first time this is called
   // is at static init time, when there is no risk of data races.
   // is at static init time, when there is no risk of data races.
-  static MutexImpl lock;
+
+  // This needs to be a recursive mutex, because get_bool_word() may end up
+  // indirectly querying another bool config variable.
+  static ReMutexImpl lock;
   lock.lock();
   lock.lock();
 
 
   // We check again for cache validity since another thread may have beaten
   // We check again for cache validity since another thread may have beaten

+ 5 - 6
dtool/src/prc/configVariableFilename.cxx

@@ -13,17 +13,16 @@
 
 
 #include "configVariableFilename.h"
 #include "configVariableFilename.h"
 #include "executionEnvironment.h"
 #include "executionEnvironment.h"
+#include "mutexImpl.h"
+
+static MutexImpl filename_lock;
 
 
 /**
 /**
  * Recopies the config variable into the Filename for returning its value.
  * Recopies the config variable into the Filename for returning its value.
  */
  */
 void ConfigVariableFilename::
 void ConfigVariableFilename::
 reload_cache() {
 reload_cache() {
-  // NB. MSVC doesn't guarantee that this mutex is initialized in a
-  // thread-safe manner.  But chances are that the first time this is called
-  // is at static init time, when there is no risk of data races.
-  static MutexImpl lock;
-  lock.lock();
+  filename_lock.lock();
 
 
   // We check again for cache validity since another thread may have beaten
   // We check again for cache validity since another thread may have beaten
   // us to the punch while we were waiting for the lock.
   // us to the punch while we were waiting for the lock.
@@ -34,5 +33,5 @@ reload_cache() {
     _cache = decl->get_filename_value();
     _cache = decl->get_filename_value();
     mark_cache_valid(_local_modified);
     mark_cache_valid(_local_modified);
   }
   }
-  lock.unlock();
+  filename_lock.unlock();
 }
 }

+ 5 - 6
dtool/src/prc/configVariableString.cxx

@@ -12,17 +12,16 @@
  */
  */
 
 
 #include "configVariableString.h"
 #include "configVariableString.h"
+#include "mutexImpl.h"
+
+static MutexImpl string_lock;
 
 
 /**
 /**
  * Refreshes the variable's cached value.
  * Refreshes the variable's cached value.
  */
  */
 void ConfigVariableString::
 void ConfigVariableString::
 reload_cache() {
 reload_cache() {
-  // NB. MSVC doesn't guarantee that this mutex is initialized in a
-  // thread-safe manner.  But chances are that the first time this is called
-  // is at static init time, when there is no risk of data races.
-  static MutexImpl lock;
-  lock.lock();
+  string_lock.lock();
 
 
   // We check again for cache validity since another thread may have beaten
   // We check again for cache validity since another thread may have beaten
   // us to the punch while we were waiting for the lock.
   // us to the punch while we were waiting for the lock.
@@ -31,5 +30,5 @@ reload_cache() {
     mark_cache_valid(_local_modified);
     mark_cache_valid(_local_modified);
   }
   }
 
 
-  lock.unlock();
+  string_lock.unlock();
 }
 }

+ 15 - 4
dtool/src/prc/streamReader.cxx

@@ -26,6 +26,9 @@ get_string() {
 
 
   // First, get the length of the string
   // First, get the length of the string
   size_t size = get_uint16();
   size_t size = get_uint16();
+  if (size == 0) {
+    return string();
+  }
 
 
   char *buffer = (char *)alloca(size);
   char *buffer = (char *)alloca(size);
   _in->read(buffer, size);
   _in->read(buffer, size);
@@ -42,6 +45,9 @@ get_string32() {
 
 
   // First, get the length of the string
   // First, get the length of the string
   size_t size = get_uint32();
   size_t size = get_uint32();
+  if (size == 0) {
+    return string();
+  }
 
 
   char *buffer = (char *)PANDA_MALLOC_ARRAY(size);
   char *buffer = (char *)PANDA_MALLOC_ARRAY(size);
   _in->read(buffer, size);
   _in->read(buffer, size);
@@ -60,7 +66,7 @@ get_z_string() {
 
 
   string result;
   string result;
   int ch = _in->get();
   int ch = _in->get();
-  while (!_in->eof() && !_in->fail() && ch != '\0') {
+  while (!_in->fail() && ch != EOF && ch != '\0') {
     result += (char)ch;
     result += (char)ch;
     ch = _in->get();
     ch = _in->get();
   }
   }
@@ -76,6 +82,10 @@ string StreamReader::
 get_fixed_string(size_t size) {
 get_fixed_string(size_t size) {
   nassertr(!_in->eof() && !_in->fail(), string());
   nassertr(!_in->eof() && !_in->fail(), string());
 
 
+  if (size == 0) {
+    return string();
+  }
+
   char *buffer = (char *)alloca(size);
   char *buffer = (char *)alloca(size);
   _in->read(buffer, size);
   _in->read(buffer, size);
   size_t read_bytes = _in->gcount();
   size_t read_bytes = _in->gcount();
@@ -90,8 +100,9 @@ get_fixed_string(size_t size) {
  */
  */
 void StreamReader::
 void StreamReader::
 skip_bytes(size_t size) {
 skip_bytes(size_t size) {
-  nassertv(!_in->eof() && !_in->fail());
+  nassertv(!_in->fail());
   nassertv((int)size >= 0);
   nassertv((int)size >= 0);
+  nassertv(size == 0 || !_in->eof());
 
 
   while (size > 0) {
   while (size > 0) {
     _in->get();
     _in->get();
@@ -145,9 +156,9 @@ string StreamReader::
 readline() {
 readline() {
   string line;
   string line;
   int ch = _in->get();
   int ch = _in->get();
-  while (!_in->eof() && !_in->fail()) {
+  while (ch != EOF && !_in->fail()) {
     line += (char)ch;
     line += (char)ch;
-    if (ch == '\n') {
+    if (ch == '\n' || _in->eof()) {
       // Here's the newline character.
       // Here's the newline character.
       return line;
       return line;
     }
     }

+ 2 - 2
dtool/src/prc/streamReader_ext.cxx

@@ -45,9 +45,9 @@ readline() {
 
 
   std::string line;
   std::string line;
   int ch = in->get();
   int ch = in->get();
-  while (!in->eof() && !in->fail()) {
+  while (ch != EOF && !in->fail()) {
     line += ch;
     line += ch;
-    if (ch == '\n') {
+    if (ch == '\n' || in->eof()) {
       // Here's the newline character.
       // Here's the newline character.
       break;
       break;
     }
     }

+ 1 - 1
makepanda/config.in

@@ -31,7 +31,7 @@ fullscreen #f
 
 
 # The framebuffer-hardware flag forces it to use an accelerated driver.
 # The framebuffer-hardware flag forces it to use an accelerated driver.
 # The framebuffer-software flag forces it to use a software renderer.
 # The framebuffer-software flag forces it to use a software renderer.
-# If you don't set either, it will use whatever's available.
+# If you set both to false, it will use whatever's available.
 
 
 framebuffer-hardware #t
 framebuffer-hardware #t
 framebuffer-software #f
 framebuffer-software #f

+ 22 - 17
makepanda/installpanda.py

@@ -62,7 +62,7 @@ def WriteMimeXMLFile(fname, info):
         fhandle.write("\t<mime-type type=\"%s\">\n" % (mime))
         fhandle.write("\t<mime-type type=\"%s\">\n" % (mime))
         fhandle.write("\t\t<comment xml:lang=\"en\">%s</comment>\n" % (desc))
         fhandle.write("\t\t<comment xml:lang=\"en\">%s</comment>\n" % (desc))
         fhandle.write("\t\t<glob pattern=\"*.%s\"/>\n" % (ext))
         fhandle.write("\t\t<glob pattern=\"*.%s\"/>\n" % (ext))
-        fhandle.write("\t</mime-type>\s")
+        fhandle.write("\t</mime-type>\n")
     fhandle.write("</mime-info>\n")
     fhandle.write("</mime-info>\n")
     fhandle.close()
     fhandle.close()
 
 
@@ -155,24 +155,29 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir(
     libdir = prefix + "/" + libdir
     libdir = prefix + "/" + libdir
 
 
     # Create the directory structure that we will be putting our files in.
     # Create the directory structure that we will be putting our files in.
-    oscmd("mkdir -m 0755 -p "+destdir+prefix+"/bin")
-    oscmd("mkdir -m 0755 -p "+destdir+prefix+"/include")
-    oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/panda3d")
-    oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/mime-info")
-    oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/mime/packages")
-    oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/application-registry")
-    oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/applications")
-    oscmd("mkdir -m 0755 -p "+destdir+libdir+"/panda3d")
+    # Don't use os.makedirs or mkdir -p; neither properly set permissions for
+    # created intermediate directories.
+    MakeDirectory(destdir+prefix+"/bin", mode=0o755, recursive=True)
+    MakeDirectory(destdir+prefix+"/include", mode=0o755)
+    MakeDirectory(destdir+prefix+"/include/panda3d", mode=0o755)
+    MakeDirectory(destdir+prefix+"/share", mode=0o755)
+    MakeDirectory(destdir+prefix+"/share/panda3d", mode=0o755)
+    MakeDirectory(destdir+prefix+"/share/mime-info", mode=0o755)
+    MakeDirectory(destdir+prefix+"/share/mime", mode=0o755)
+    MakeDirectory(destdir+prefix+"/share/mime/packages", mode=0o755)
+    MakeDirectory(destdir+prefix+"/share/application-registry", mode=0o755)
+    MakeDirectory(destdir+prefix+"/share/applications", mode=0o755)
+    MakeDirectory(destdir+libdir+"/panda3d", mode=0o755, recursive=True)
 
 
     for python_version in python_versions:
     for python_version in python_versions:
-        oscmd("mkdir -m 0755 -p "+destdir+python_version["purelib"])
-        oscmd("mkdir -m 0755 -p "+destdir+python_version["platlib"]+"/panda3d")
+        MakeDirectory(destdir+python_version["purelib"], mode=0o755, recursive=True)
+        MakeDirectory(destdir+python_version["platlib"]+"/panda3d", mode=0o755, recursive=True)
 
 
     if (sys.platform.startswith("freebsd")):
     if (sys.platform.startswith("freebsd")):
-        oscmd("mkdir -m 0755 -p "+destdir+prefix+"/etc")
-        oscmd("mkdir -m 0755 -p "+destdir+"/usr/local/libdata/ldconfig")
+        MakeDirectory(destdir+prefix+"/etc", mode=0o755)
+        MakeDirectory(destdir+"/usr/local/libdata/ldconfig", mode=0o755, recursive=True)
     else:
     else:
-        oscmd("mkdir -m 0755 -p "+destdir+"/etc/ld.so.conf.d")
+        MakeDirectory(destdir+"/etc/ld.so.conf.d", mode=0o755, recursive=True)
 
 
     # Write the Config.prc file.
     # Write the Config.prc file.
     Configrc = ReadFile(outputdir+"/etc/Config.prc")
     Configrc = ReadFile(outputdir+"/etc/Config.prc")
@@ -184,7 +189,7 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir(
         WriteFile(destdir+"/etc/Config.prc", Configrc)
         WriteFile(destdir+"/etc/Config.prc", Configrc)
         oscmd("cp "+outputdir+"/etc/Confauto.prc "+destdir+"/etc/Confauto.prc")
         oscmd("cp "+outputdir+"/etc/Confauto.prc "+destdir+"/etc/Confauto.prc")
 
 
-    oscmd("cp -R "+outputdir+"/include          "+destdir+prefix+"/include/panda3d")
+    oscmd("cp -R "+outputdir+"/include/*        "+destdir+prefix+"/include/panda3d/")
     oscmd("cp -R "+outputdir+"/pandac           "+destdir+prefix+"/share/panda3d/")
     oscmd("cp -R "+outputdir+"/pandac           "+destdir+prefix+"/share/panda3d/")
     oscmd("cp -R "+outputdir+"/models           "+destdir+prefix+"/share/panda3d/")
     oscmd("cp -R "+outputdir+"/models           "+destdir+prefix+"/share/panda3d/")
     if os.path.isdir("samples"):             oscmd("cp -R samples               "+destdir+prefix+"/share/panda3d/")
     if os.path.isdir("samples"):             oscmd("cp -R samples               "+destdir+prefix+"/share/panda3d/")
@@ -237,8 +242,8 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir(
     DeleteEmptyDirs(destdir+prefix+"/include/panda3d")
     DeleteEmptyDirs(destdir+prefix+"/include/panda3d")
 
 
     # Change permissions on include directory.
     # Change permissions on include directory.
-    os.chmod(destdir + prefix + "/include", 0o755)
-    for root, dirs, files in os.walk(destdir + prefix + "/include"):
+    os.chmod(destdir + prefix + "/include/panda3d", 0o755)
+    for root, dirs, files in os.walk(destdir + prefix + "/include/panda3d"):
         for basename in dirs:
         for basename in dirs:
             os.chmod(os.path.join(root, basename), 0o755)
             os.chmod(os.path.join(root, basename), 0o755)
         for basename in files:
         for basename in files:

+ 9 - 9
makepanda/makechm.py

@@ -213,11 +213,11 @@ def makeCHM(outputfile, dirname, title, special = None):
     # Now, execute the command to compile the files.
     # Now, execute the command to compile the files.
     if "PROGRAMFILES" in os.environ and os.path.isdir("%s\\HTML Help Workshop" % os.environ["PROGRAMFILES"]):
     if "PROGRAMFILES" in os.environ and os.path.isdir("%s\\HTML Help Workshop" % os.environ["PROGRAMFILES"]):
         cmd = "\"%s\\HTML Help Workshop\\hhc.exe\" %s.hhp" % (os.environ["PROGRAMFILES"], base)
         cmd = "\"%s\\HTML Help Workshop\\hhc.exe\" %s.hhp" % (os.environ["PROGRAMFILES"], base)
-    elif os.path.isdir("C:\Program Files\HTML Help Workshop"):
+    elif os.path.isdir("C:\\Program Files\\HTML Help Workshop"):
         cmd = "\"C:\\Program Files\\HTML Help Workshop\\hhc.exe\" %s.hhp" % base
         cmd = "\"C:\\Program Files\\HTML Help Workshop\\hhc.exe\" %s.hhp" % base
     else:
     else:
         cmd = "hhc \"%s.hhp\"" % base
         cmd = "hhc \"%s.hhp\"" % base
-    print cmd
+    print(cmd)
     os.system(cmd)
     os.system(cmd)
     if not KEEPTEMP:
     if not KEEPTEMP:
         if os.path.isfile("%s.hhp" % base): os.remove("%s.hhp" % base)
         if os.path.isfile("%s.hhp" % base): os.remove("%s.hhp" % base)
@@ -225,7 +225,7 @@ def makeCHM(outputfile, dirname, title, special = None):
         if os.path.isfile("%s.hhk" % base): os.remove("%s.hhk" % base)
         if os.path.isfile("%s.hhk" % base): os.remove("%s.hhk" % base)
         if os.path.isfile("%s.chw" % base): os.remove("%s.chw" % base)
         if os.path.isfile("%s.chw" % base): os.remove("%s.chw" % base)
     if not os.path.isfile(base + ".chm"):
     if not os.path.isfile(base + ".chm"):
-        print "An error has occurred!"
+        print("An error has occurred!")
         if __name__ == "__main__":
         if __name__ == "__main__":
             exit(1)
             exit(1)
         else:
         else:
@@ -246,7 +246,7 @@ if __name__ == "__main__":
     VERSION = None
     VERSION = None
     try:
     try:
         f = file("built/include/pandaVersion.h","r")
         f = file("built/include/pandaVersion.h","r")
-        pattern = re.compile('^\s*[#]\s*define\s+PANDA_VERSION_STR\s+["]([0-9.]+)["]')
+        pattern = re.compile('^\\s*[#]\\s*define\\s+PANDA_VERSION_STR\\s+["]([0-9.]+)["]')
         for line in f:
         for line in f:
             match = pattern.match(line,0)
             match = pattern.match(line,0)
             if (match):
             if (match):
@@ -260,22 +260,22 @@ if __name__ == "__main__":
     # Now, make CHM's for both the manual and reference, if we have them.
     # Now, make CHM's for both the manual and reference, if we have them.
     for lang in ["python", "cxx"]:
     for lang in ["python", "cxx"]:
         if not os.path.isdir("manual-" + lang):
         if not os.path.isdir("manual-" + lang):
-            print "No directory named 'manual-%s' found" % lang
+            print("No directory named 'manual-%s' found" % lang)
         else:
         else:
-            print "Making CHM file for manual-%s..." % lang
+            print("Making CHM file for manual-%s..." % lang)
             if VERSION == None:
             if VERSION == None:
                 makeManualCHM("manual-%s.chm" % lang, "manual-" + lang, "Panda3D Manual")
                 makeManualCHM("manual-%s.chm" % lang, "manual-" + lang, "Panda3D Manual")
             else:
             else:
                 makeManualCHM("manual-%s-%s.chm" % (VERSION, lang), "manual-" + lang, "Panda3D %s Manual" % VERSION)
                 makeManualCHM("manual-%s-%s.chm" % (VERSION, lang), "manual-" + lang, "Panda3D %s Manual" % VERSION)
 
 
         if not os.path.isdir("reference-" + lang):
         if not os.path.isdir("reference-" + lang):
-            print "No directory named 'reference-%s' found" % lang
+            print("No directory named 'reference-%s' found" % lang)
         else:
         else:
-            print "Making CHM file for reference-%s..." % lang
+            print("Making CHM file for reference-%s..." % lang)
             if VERSION == None:
             if VERSION == None:
                 makeReferenceCHM("reference-%s.chm" % lang, "reference-" + lang, "Panda3D Reference")
                 makeReferenceCHM("reference-%s.chm" % lang, "reference-" + lang, "Panda3D Reference")
             else:
             else:
                 makeReferenceCHM("reference-%s-%s.chm" % (VERSION, lang), "reference-" + lang, "Panda3D %s Reference" % VERSION)
                 makeReferenceCHM("reference-%s-%s.chm" % (VERSION, lang), "reference-" + lang, "Panda3D %s Reference" % VERSION)
 
 
-    print "Done!"
+    print("Done!")
 
 

+ 36 - 17
makepanda/makepackage.py

@@ -5,6 +5,9 @@ from installpanda import *
 import sys
 import sys
 import os
 import os
 import shutil
 import shutil
+import glob
+import re
+import subprocess
 
 
 
 
 INSTALLER_DEB_FILE = """
 INSTALLER_DEB_FILE = """
@@ -438,7 +441,8 @@ def MakeInstallerLinux(version, debversion=None, rpmrelease=1, runtime=False,
 
 
             # Add the binaries in /usr/bin explicitly to the spec file
             # Add the binaries in /usr/bin explicitly to the spec file
             for base in os.listdir(outputdir + "/bin"):
             for base in os.listdir(outputdir + "/bin"):
-                txt += "/usr/bin/%s\n" % (base)
+                if not base.startswith("deploy-stub"):
+                    txt += "/usr/bin/%s\n" % (base)
 
 
         # Write out the spec file.
         # Write out the spec file.
         txt = txt.replace("VERSION", version)
         txt = txt.replace("VERSION", version)
@@ -473,7 +477,7 @@ def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs):
 
 
     dmg_name = "Panda3D-" + version
     dmg_name = "Panda3D-" + version
     if len(python_versions) == 1 and not python_versions[0]["version"].startswith("2."):
     if len(python_versions) == 1 and not python_versions[0]["version"].startswith("2."):
-        dmg_name += "-py" + pyver
+        dmg_name += "-py" + python_versions[0]["version"]
     dmg_name += ".dmg"
     dmg_name += ".dmg"
 
 
     if (os.path.isfile(dmg_name)): oscmd("rm -f %s" % dmg_name)
     if (os.path.isfile(dmg_name)): oscmd("rm -f %s" % dmg_name)
@@ -834,7 +838,8 @@ def MakeInstallerAndroid(version, **kwargs):
     # off any suffix (eg. libfile.so.1.0), as Android does not support them.
     # off any suffix (eg. libfile.so.1.0), as Android does not support them.
     source_dir = os.path.join(outputdir, "lib")
     source_dir = os.path.join(outputdir, "lib")
     target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"])
     target_dir = os.path.join("apkroot", "lib", SDK["ANDROID_ABI"])
-    oscmd("mkdir -p %s" % (target_dir))
+    if not os.path.exists(target_dir):
+        os.makedirs(target_dir, mode=0o755)
 
 
     # Determine the library directories we should look in.
     # Determine the library directories we should look in.
     libpath = [source_dir]
     libpath = [source_dir]
@@ -856,19 +861,26 @@ def MakeInstallerAndroid(version, **kwargs):
             # Already processed.
             # Already processed.
             return
             return
 
 
-        oscmd("cp %s %s" % (source, target))
+        shutil.copy(source, target)
 
 
         # Walk through the library dependencies.
         # Walk through the library dependencies.
-        oscmd("ldd %s | grep .so > %s/tmp/otool-libs.txt" % (target, outputdir), True)
-        for line in open(outputdir + "/tmp/otool-libs.txt", "r"):
-            line = line.strip()
-            if not line:
+        handle = subprocess.Popen(['readelf', '--dynamic', target], stdout=subprocess.PIPE)
+        for line in handle.communicate()[0].splitlines():
+            # The line will look something like:
+            # 0x0000000000000001 (NEEDED)             Shared library: [libpanda.so]
+            line = line.decode('utf-8', 'replace').strip()
+            if not line or '(NEEDED)' not in line or '[' not in line or ']' not in line:
                 continue
                 continue
-            if '.so.' in line:
-                dep = line.rpartition('.so.')[0] + '.so'
-                oscmd("patchelf --replace-needed %s %s %s" % (line, dep, target), True)
-            else:
-                dep = line
+
+            # Extract the part between square brackets.
+            idx = line.index('[')
+            dep = line[idx + 1 : line.index(']', idx)]
+
+            # Change .so.1.2 suffix to .so, as needed for loading in .apk
+            if '.so.' in dep:
+                orig_dep = dep
+                dep = dep.rpartition('.so.')[0] + '.so'
+                oscmd("patchelf --replace-needed %s %s %s" % (orig_dep, dep, target), True)
 
 
             # Find it on the LD_LIBRARY_PATH.
             # Find it on the LD_LIBRARY_PATH.
             for dir in libpath:
             for dir in libpath:
@@ -957,21 +969,28 @@ def MakeInstallerAndroid(version, **kwargs):
     aapt_cmd += " -F %s" % (apk_unaligned)
     aapt_cmd += " -F %s" % (apk_unaligned)
     aapt_cmd += " -M apkroot/AndroidManifest.xml"
     aapt_cmd += " -M apkroot/AndroidManifest.xml"
     aapt_cmd += " -A apkroot/assets -S apkroot/res"
     aapt_cmd += " -A apkroot/assets -S apkroot/res"
-    aapt_cmd += " -I $PREFIX/share/aapt/android.jar"
+    aapt_cmd += " -I %s" % (SDK["ANDROID_JAR"])
     oscmd(aapt_cmd)
     oscmd(aapt_cmd)
 
 
     # And add all the libraries to it.
     # And add all the libraries to it.
-    oscmd("cd apkroot && aapt add ../%s classes.dex" % (apk_unaligned))
+    oscmd("aapt add %s classes.dex" % (os.path.join('..', apk_unaligned)), cwd="apkroot")
     for path, dirs, files in os.walk('apkroot/lib'):
     for path, dirs, files in os.walk('apkroot/lib'):
         if files:
         if files:
             rel = os.path.relpath(path, 'apkroot')
             rel = os.path.relpath(path, 'apkroot')
-            oscmd("cd apkroot && aapt add ../%s %s/*" % (apk_unaligned, rel))
+            rel_files = [os.path.join(rel, file).replace('\\', '/') for file in files]
+            oscmd("aapt add %s %s" % (os.path.join('..', apk_unaligned), ' '.join(rel_files)), cwd="apkroot")
 
 
     # Now align the .apk, which is necessary for Android to load it.
     # Now align the .apk, which is necessary for Android to load it.
     oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned))
     oscmd("zipalign -v -p 4 %s %s" % (apk_unaligned, apk_unsigned))
 
 
     # Finally, sign it using a debug key.  This is generated if it doesn't exist.
     # Finally, sign it using a debug key.  This is generated if it doesn't exist.
-    oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned))
+    if GetHost() == 'android':
+        # Termux version of apksigner automatically generates a debug key.
+        oscmd("apksigner debug.ks %s panda3d.apk" % (apk_unsigned))
+    else:
+        if not os.path.isfile('debug.ks'):
+            oscmd("keytool -genkey -noprompt -dname CN=Panda3D,O=Panda3D,C=US -keystore debug.ks -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 1000")
+        oscmd("apksigner sign --ks debug.ks --ks-pass pass:android --min-sdk-version %s --out panda3d.apk %s" % (SDK["ANDROID_API"], apk_unsigned))
 
 
     # Clean up.
     # Clean up.
     oscmd("rm -rf apkroot")
     oscmd("rm -rf apkroot")

+ 59 - 161
makepanda/makepanda.py

@@ -63,7 +63,6 @@ MAJOR_VERSION=None
 COREAPI_VERSION=None
 COREAPI_VERSION=None
 PLUGIN_VERSION=None
 PLUGIN_VERSION=None
 OSXTARGET=None
 OSXTARGET=None
-OSX_ARCHS=[]
 HOST_URL=None
 HOST_URL=None
 global STRDXSDKVERSION, BOOUSEINTELCOMPILER
 global STRDXSDKVERSION, BOOUSEINTELCOMPILER
 STRDXSDKVERSION = 'default'
 STRDXSDKVERSION = 'default'
@@ -94,7 +93,7 @@ PkgListSet(["PYTHON", "DIRECT",                        # Python support
   "GTK2",                                              # GTK2 is used for PStats on Unix
   "GTK2",                                              # GTK2 is used for PStats on Unix
   "MFC", "WX", "FLTK",                                 # Used for web plug-in only
   "MFC", "WX", "FLTK",                                 # Used for web plug-in only
   "ROCKET",                                            # GUI libraries
   "ROCKET",                                            # GUI libraries
-  "CARBON", "COCOA",                                   # Mac OS X toolkits
+  "COCOA",                                             # Mac OS X toolkits
   "X11",                                               # Unix platform support
   "X11",                                               # Unix platform support
   "PANDATOOL", "PVIEW", "DEPLOYTOOLS",                 # Toolchain
   "PANDATOOL", "PVIEW", "DEPLOYTOOLS",                 # Toolchain
   "SKEL",                                              # Example SKEL project
   "SKEL",                                              # Example SKEL project
@@ -142,7 +141,6 @@ def usage(problem):
     print("  --host URL        (set the host url (runtime build only))")
     print("  --host URL        (set the host url (runtime build only))")
     print("  --threads N       (use the multithreaded build system. see manual)")
     print("  --threads N       (use the multithreaded build system. see manual)")
     print("  --osxtarget N     (the OS X version number to build for (OS X only))")
     print("  --osxtarget N     (the OS X version number to build for (OS X only))")
-    print("  --universal       (build universal binaries (OS X only))")
     print("  --override \"O=V\"  (override dtool_config/prc option value)")
     print("  --override \"O=V\"  (override dtool_config/prc option value)")
     print("  --static          (builds libraries for static linking)")
     print("  --static          (builds libraries for static linking)")
     print("  --target X        (experimental cross-compilation (android only))")
     print("  --target X        (experimental cross-compilation (android only))")
@@ -159,7 +157,7 @@ def usage(problem):
     print("  --everything      (enable every third-party lib)")
     print("  --everything      (enable every third-party lib)")
     print("  --directx-sdk=X   (specify version of DirectX SDK to use: jun2010, aug2009, mar2009, aug2006)")
     print("  --directx-sdk=X   (specify version of DirectX SDK to use: jun2010, aug2009, mar2009, aug2006)")
     print("  --windows-sdk=X   (specify Windows SDK version, eg. 7.0, 7.1 or 10.  Default is 7.1)")
     print("  --windows-sdk=X   (specify Windows SDK version, eg. 7.0, 7.1 or 10.  Default is 7.1)")
-    print("  --msvc-version=X  (specify Visual C++ version, eg. 10, 11, 12, 14.  Default is 14)")
+    print("  --msvc-version=X  (specify Visual C++ version, eg. 10, 11, 12, 14, 14.1, 14.2.  Default is 14)")
     print("  --use-icl         (experimental setting to use an intel compiler instead of MSVC on Windows)")
     print("  --use-icl         (experimental setting to use an intel compiler instead of MSVC on Windows)")
     print("")
     print("")
     print("The simplest way to compile panda is to just type:")
     print("The simplest way to compile panda is to just type:")
@@ -170,7 +168,7 @@ def usage(problem):
 
 
 def parseopts(args):
 def parseopts(args):
     global INSTALLER,WHEEL,RUNTESTS,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION
     global INSTALLER,WHEEL,RUNTESTS,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION
-    global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL
+    global COMPRESSOR,THREADCOUNT,OSXTARGET,HOST_URL
     global DEBVERSION,WHLVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION
     global DEBVERSION,WHLVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION
     global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER
     global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER
     global COPY_PYTHON
     global COPY_PYTHON
@@ -178,6 +176,7 @@ def parseopts(args):
     # Options for which to display a deprecation warning.
     # Options for which to display a deprecation warning.
     removedopts = [
     removedopts = [
         "use-touchinput", "no-touchinput", "no-awesomium", "no-directscripts",
         "use-touchinput", "no-touchinput", "no-awesomium", "no-directscripts",
+        "no-carbon", "universal",
         ]
         ]
 
 
     # All recognized options.
     # All recognized options.
@@ -187,14 +186,13 @@ def parseopts(args):
         "version=","lzma","no-python","threads=","outputdir=","override=",
         "version=","lzma","no-python","threads=","outputdir=","override=",
         "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=",
         "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=",
         "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl",
         "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl",
-        "universal", "target=", "arch=", "git-commit=", "no-copy-python",
+        "target=", "arch=", "git-commit=", "no-copy-python",
         ] + removedopts
         ] + removedopts
 
 
     anything = 0
     anything = 0
     optimize = ""
     optimize = ""
     target = None
     target = None
     target_arch = None
     target_arch = None
-    universal = False
     clean_build = False
     clean_build = False
     for pkg in PkgListGet():
     for pkg in PkgListGet():
         longopts.append("use-" + pkg.lower())
         longopts.append("use-" + pkg.lower())
@@ -220,7 +218,6 @@ def parseopts(args):
             elif (option=="--threads"): THREADCOUNT=int(value)
             elif (option=="--threads"): THREADCOUNT=int(value)
             elif (option=="--outputdir"): SetOutputDir(value.strip())
             elif (option=="--outputdir"): SetOutputDir(value.strip())
             elif (option=="--osxtarget"): OSXTARGET=value.strip()
             elif (option=="--osxtarget"): OSXTARGET=value.strip()
-            elif (option=="--universal"): universal = True
             elif (option=="--target"): target = value.strip()
             elif (option=="--target"): target = value.strip()
             elif (option=="--arch"): target_arch = value.strip()
             elif (option=="--arch"): target_arch = value.strip()
             elif (option=="--nocolor"): DisableColors()
             elif (option=="--nocolor"): DisableColors()
@@ -302,25 +299,6 @@ def parseopts(args):
     if target is not None or target_arch is not None:
     if target is not None or target_arch is not None:
         SetTarget(target, target_arch)
         SetTarget(target, target_arch)
 
 
-    if universal:
-        if target_arch:
-            exit("--universal is incompatible with --arch")
-
-        OSX_ARCHS.append("i386")
-        if OSXTARGET:
-            osxver = OSXTARGET
-        else:
-            maj, min = platform.mac_ver()[0].split('.')[:2]
-            osxver = int(maj), int(min)
-
-        if osxver[1] < 6:
-            OSX_ARCHS.append("ppc")
-        else:
-            OSX_ARCHS.append("x86_64")
-
-    elif HasTargetArch():
-        OSX_ARCHS.append(GetTargetArch())
-
     try:
     try:
         SetOptimize(int(optimize))
         SetOptimize(int(optimize))
         assert GetOptimize() in [1, 2, 3, 4]
         assert GetOptimize() in [1, 2, 3, 4]
@@ -432,22 +410,7 @@ elif target == 'darwin':
         maj, min = platform.mac_ver()[0].split('.')[:2]
         maj, min = platform.mac_ver()[0].split('.')[:2]
         osxver = int(maj), int(min)
         osxver = int(maj), int(min)
 
 
-    arch_tag = None
-    if not OSX_ARCHS:
-        arch_tag = GetTargetArch()
-    elif len(OSX_ARCHS) == 1:
-        arch_tag = OSX_ARCHS[0]
-    elif frozenset(OSX_ARCHS) == frozenset(('i386', 'ppc')):
-        arch_tag = 'fat'
-    elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'i386')):
-        arch_tag = 'intel'
-    elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'ppc64')):
-        arch_tag = 'fat64'
-    elif frozenset(OSX_ARCHS) == frozenset(('x86_64', 'i386', 'ppc')):
-        arch_tag = 'fat32'
-    else:
-        raise RuntimeError('No arch tag for arch combination %s' % OSX_ARCHS)
-
+    arch_tag = GetTargetArch()
     PLATFORM = 'macosx-{0}.{1}-{2}'.format(osxver[0], osxver[1], arch_tag)
     PLATFORM = 'macosx-{0}.{1}-{2}'.format(osxver[0], osxver[1], arch_tag)
 
 
 elif target == 'linux' and (os.path.isfile("/lib/libc-2.5.so") or os.path.isfile("/lib64/libc-2.5.so")) and os.path.isdir("/opt/python"):
 elif target == 'linux' and (os.path.isfile("/lib/libc-2.5.so") or os.path.isfile("/lib64/libc-2.5.so")) and os.path.isdir("/opt/python"):
@@ -622,7 +585,6 @@ if (COMPILER == "MSVC"):
     PkgDisable("GLES")
     PkgDisable("GLES")
     PkgDisable("GLES2")
     PkgDisable("GLES2")
     PkgDisable("EGL")
     PkgDisable("EGL")
-    PkgDisable("CARBON")
     PkgDisable("COCOA")
     PkgDisable("COCOA")
     DefSymbol("FLEX", "YY_NO_UNISTD_H")
     DefSymbol("FLEX", "YY_NO_UNISTD_H")
     if (PkgSkip("PYTHON")==0):
     if (PkgSkip("PYTHON")==0):
@@ -875,14 +837,10 @@ if (COMPILER == "MSVC"):
 
 
 if (COMPILER=="GCC"):
 if (COMPILER=="GCC"):
     if GetTarget() != "darwin":
     if GetTarget() != "darwin":
-        PkgDisable("CARBON")
         PkgDisable("COCOA")
         PkgDisable("COCOA")
     elif RUNTIME:
     elif RUNTIME:
         # We don't support Cocoa in the runtime yet.
         # We don't support Cocoa in the runtime yet.
         PkgDisable("COCOA")
         PkgDisable("COCOA")
-    if 'x86_64' in OSX_ARCHS:
-        # 64-bits OS X doesn't have Carbon.
-        PkgDisable("CARBON")
 
 
     #if (PkgSkip("PYTHON")==0):
     #if (PkgSkip("PYTHON")==0):
     #    IncDirectory("PYTHON", SDK["PYTHON"])
     #    IncDirectory("PYTHON", SDK["PYTHON"])
@@ -905,13 +863,14 @@ if (COMPILER=="GCC"):
     fcollada_libs = ("FColladaD", "FColladaSD", "FColladaS")
     fcollada_libs = ("FColladaD", "FColladaSD", "FColladaS")
     # WARNING! The order of the ffmpeg libraries matters!
     # WARNING! The order of the ffmpeg libraries matters!
     ffmpeg_libs = ("libavformat", "libavcodec", "libavutil")
     ffmpeg_libs = ("libavformat", "libavcodec", "libavutil")
+    assimp_libs = ("libassimp", "libassimpd")
 
 
     #         Name         pkg-config   libs, include(dir)s
     #         Name         pkg-config   libs, include(dir)s
     if (not RUNTIME):
     if (not RUNTIME):
         SmartPkgEnable("EIGEN",     "eigen3",    (), ("Eigen/Dense",), target_pkg = 'ALWAYS')
         SmartPkgEnable("EIGEN",     "eigen3",    (), ("Eigen/Dense",), target_pkg = 'ALWAYS')
         SmartPkgEnable("ARTOOLKIT", "",          ("AR"), "AR/ar.h")
         SmartPkgEnable("ARTOOLKIT", "",          ("AR"), "AR/ar.h")
         SmartPkgEnable("FCOLLADA",  "",          ChooseLib(fcollada_libs, "FCOLLADA"), ("FCollada", "FCollada/FCollada.h"))
         SmartPkgEnable("FCOLLADA",  "",          ChooseLib(fcollada_libs, "FCOLLADA"), ("FCollada", "FCollada/FCollada.h"))
-        SmartPkgEnable("ASSIMP",    "assimp",    ("assimp"), "assimp/Importer.hpp")
+        SmartPkgEnable("ASSIMP",    "assimp",    ChooseLib(assimp_libs, "ASSIMP"), "assimp/Importer.hpp")
         SmartPkgEnable("FFMPEG",    ffmpeg_libs, ffmpeg_libs, ("libavformat/avformat.h", "libavcodec/avcodec.h", "libavutil/avutil.h"))
         SmartPkgEnable("FFMPEG",    ffmpeg_libs, ffmpeg_libs, ("libavformat/avformat.h", "libavcodec/avcodec.h", "libavutil/avutil.h"))
         SmartPkgEnable("SWSCALE",   "libswscale", "libswscale", ("libswscale/swscale.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg")
         SmartPkgEnable("SWSCALE",   "libswscale", "libswscale", ("libswscale/swscale.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg")
         SmartPkgEnable("SWRESAMPLE","libswresample", "libswresample", ("libswresample/swresample.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg")
         SmartPkgEnable("SWRESAMPLE","libswresample", "libswresample", ("libswresample/swresample.h"), target_pkg = "FFMPEG", thirdparty_dir = "ffmpeg")
@@ -938,7 +897,6 @@ if (COMPILER=="GCC"):
 
 
         if not PkgSkip("FFMPEG"):
         if not PkgSkip("FFMPEG"):
             if GetTarget() == "darwin":
             if GetTarget() == "darwin":
-                LibName("FFMPEG", "-Wl,-read_only_relocs,suppress")
                 LibName("FFMPEG", "-framework VideoDecodeAcceleration")
                 LibName("FFMPEG", "-framework VideoDecodeAcceleration")
             elif os.path.isfile(GetThirdpartyDir() + "ffmpeg/lib/libavcodec.a"):
             elif os.path.isfile(GetThirdpartyDir() + "ffmpeg/lib/libavcodec.a"):
                 # Needed when linking ffmpeg statically on Linux.
                 # Needed when linking ffmpeg statically on Linux.
@@ -1351,9 +1309,11 @@ def CompileCxx(obj,src,opts):
                 cmd += " -isysroot " + SDK["MACOSX"]
                 cmd += " -isysroot " + SDK["MACOSX"]
                 cmd += " -mmacosx-version-min=%d.%d" % (OSXTARGET)
                 cmd += " -mmacosx-version-min=%d.%d" % (OSXTARGET)
 
 
-            for arch in OSX_ARCHS:
-                if 'NOARCH:' + arch.upper() not in opts:
-                    cmd += " -arch %s" % arch
+            # Use libc++ to enable C++11 features.
+            cmd += " -stdlib=libc++"
+
+            arch = GetTargetArch()
+            cmd += " -arch %s" % arch
 
 
         if "SYSROOT" in SDK:
         if "SYSROOT" in SDK:
             if GetTarget() != "android":
             if GetTarget() != "android":
@@ -1570,7 +1530,7 @@ def CompileIgate(woutd,wsrc,opts):
         # NOTE: this 1600 value is the version number for VC2010.
         # NOTE: this 1600 value is the version number for VC2010.
         cmd += ' -D_MSC_VER=1600 -D"__declspec(param)=" -D__cdecl -D_near -D_far -D__near -D__far -D__stdcall'
         cmd += ' -D_MSC_VER=1600 -D"__declspec(param)=" -D__cdecl -D_near -D_far -D__near -D__far -D__stdcall'
     if (COMPILER=="GCC"):
     if (COMPILER=="GCC"):
-        cmd += ' -D__attribute__\(x\)='
+        cmd += ' -D__attribute__\\(x\\)='
         target_arch = GetTargetArch()
         target_arch = GetTargetArch()
         if target_arch in ("x86_64", "amd64"):
         if target_arch in ("x86_64", "amd64"):
             cmd += ' -D_LP64'
             cmd += ' -D_LP64'
@@ -1675,9 +1635,9 @@ def CompileLib(lib, obj, opts):
                 cmd += " /MACHINE:" + GetTargetArch().upper()
                 cmd += " /MACHINE:" + GetTargetArch().upper()
             cmd += ' /OUT:' + BracketNameWithQuotes(lib)
             cmd += ' /OUT:' + BracketNameWithQuotes(lib)
             for x in obj: cmd += ' ' + BracketNameWithQuotes(x)
             for x in obj: cmd += ' ' + BracketNameWithQuotes(x)
-            cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\ipp\lib\ia32"'
-            cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\TBB\Lib\ia32\vc10"'
-            cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\compiler\lib\ia32"'
+            cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\ipp\\lib\\ia32"'
+            cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\TBB\\Lib\\ia32\\vc10"'
+            cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\compiler\\lib\\ia32"'
             oscmd(cmd)
             oscmd(cmd)
 
 
     if (COMPILER=="GCC"):
     if (COMPILER=="GCC"):
@@ -1783,9 +1743,9 @@ def CompileLink(dll, obj, opts):
                 cmd += " /NOD:MFC90.LIB /NOD:MFC80.LIB /NOD:LIBCMT"
                 cmd += " /NOD:MFC90.LIB /NOD:MFC80.LIB /NOD:LIBCMT"
             cmd += " /NOD:LIBCI.LIB /DEBUG"
             cmd += " /NOD:LIBCI.LIB /DEBUG"
             cmd += " /nod:libc /nod:libcmtd /nod:atlthunk /nod:atls"
             cmd += " /nod:libc /nod:libcmtd /nod:atlthunk /nod:atls"
-            cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\ipp\lib\ia32"'
-            cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\TBB\Lib\ia32\vc10"'
-            cmd += ' /LIBPATH:"C:\Program Files (x86)\Intel\Composer XE 2011 SP1\compiler\lib\ia32"'
+            cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\ipp\\lib\\ia32"'
+            cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\TBB\\Lib\\ia32\\vc10"'
+            cmd += ' /LIBPATH:"C:\\Program Files (x86)\\Intel\\Composer XE 2011 SP1\\compiler\\lib\\ia32"'
             if (GetOrigExt(dll) != ".exe"): cmd += " /DLL"
             if (GetOrigExt(dll) != ".exe"): cmd += " /DLL"
             optlevel = GetOptimizeOption(opts)
             optlevel = GetOptimizeOption(opts)
             if (optlevel==1): cmd += " /MAP /MAPINFO:EXPORTS /NOD:MSVCRT.LIB /NOD:MSVCPRT.LIB /NOD:MSVCIRT.LIB"
             if (optlevel==1): cmd += " /MAP /MAPINFO:EXPORTS /NOD:MSVCRT.LIB /NOD:MSVCPRT.LIB /NOD:MSVCIRT.LIB"
@@ -1871,9 +1831,11 @@ def CompileLink(dll, obj, opts):
                 cmd += " -isysroot " + SDK["MACOSX"] + " -Wl,-syslibroot," + SDK["MACOSX"]
                 cmd += " -isysroot " + SDK["MACOSX"] + " -Wl,-syslibroot," + SDK["MACOSX"]
                 cmd += " -mmacosx-version-min=%d.%d" % (OSXTARGET)
                 cmd += " -mmacosx-version-min=%d.%d" % (OSXTARGET)
 
 
-            for arch in OSX_ARCHS:
-                if 'NOARCH:' + arch.upper() not in opts:
-                    cmd += " -arch %s" % arch
+            # Use libc++ to enable C++11 features.
+            cmd += " -stdlib=libc++"
+
+            arch = GetTargetArch()
+            cmd += " -arch %s" % arch
 
 
         elif GetTarget() == 'android':
         elif GetTarget() == 'android':
             arch = GetTargetArch()
             arch = GetTargetArch()
@@ -2043,7 +2005,10 @@ def CompileRsrc(target, src, opts):
 
 
 def CompileJava(target, src, opts):
 def CompileJava(target, src, opts):
     """Compiles a .java file into a .class file."""
     """Compiles a .java file into a .class file."""
-    cmd = "ecj "
+    if GetHost() == 'android':
+        cmd = "ecj "
+    else:
+        cmd = "javac -bootclasspath " + BracketNameWithQuotes(SDK["ANDROID_JAR"]) + " "
 
 
     optlevel = GetOptimizeOption(opts)
     optlevel = GetOptimizeOption(opts)
     if optlevel >= 4:
     if optlevel >= 4:
@@ -2124,10 +2089,10 @@ def Package(target, inputs, opts):
         if SDK.get("MACOSX"):
         if SDK.get("MACOSX"):
             command += " -R \"%s\"" % SDK["MACOSX"]
             command += " -R \"%s\"" % SDK["MACOSX"]
 
 
-        for arch in OSX_ARCHS:
-            if arch == "x86_64":
-                arch = "amd64"
-            command += " -P osx_%s" % arch
+        arch = GetTargetArch()
+        if arch == "x86_64":
+            arch = "amd64"
+        command += " -P osx_%s" % arch
 
 
     command += " -i \"" + GetOutputDir() + "/stage\""
     command += " -i \"" + GetOutputDir() + "/stage\""
     if (P3DSUFFIX):
     if (P3DSUFFIX):
@@ -2450,7 +2415,6 @@ DTOOL_CONFIG=[
     ("HAVE_ARTOOLKIT",                 'UNDEF',                  'UNDEF'),
     ("HAVE_ARTOOLKIT",                 'UNDEF',                  'UNDEF'),
     ("HAVE_DIRECTCAM",                 'UNDEF',                  'UNDEF'),
     ("HAVE_DIRECTCAM",                 'UNDEF',                  'UNDEF'),
     ("HAVE_SQUISH",                    'UNDEF',                  'UNDEF'),
     ("HAVE_SQUISH",                    'UNDEF',                  'UNDEF'),
-    ("HAVE_CARBON",                    'UNDEF',                  'UNDEF'),
     ("HAVE_COCOA",                     'UNDEF',                  'UNDEF'),
     ("HAVE_COCOA",                     'UNDEF',                  'UNDEF'),
     ("HAVE_OPENAL_FRAMEWORK",          'UNDEF',                  'UNDEF'),
     ("HAVE_OPENAL_FRAMEWORK",          'UNDEF',                  'UNDEF'),
     ("HAVE_ROCKET_PYTHON",             '1',                      '1'),
     ("HAVE_ROCKET_PYTHON",             '1',                      '1'),
@@ -3354,7 +3318,6 @@ if not PkgSkip("EGG"):
 if GetTarget() == 'windows':
 if GetTarget() == 'windows':
     CopyAllHeaders('panda/src/wgldisplay')
     CopyAllHeaders('panda/src/wgldisplay')
 elif GetTarget() == 'darwin':
 elif GetTarget() == 'darwin':
-    CopyAllHeaders('panda/src/osxdisplay')
     CopyAllHeaders('panda/src/cocoadisplay')
     CopyAllHeaders('panda/src/cocoadisplay')
 elif GetTarget() == 'android':
 elif GetTarget() == 'android':
     CopyAllHeaders('panda/src/android')
     CopyAllHeaders('panda/src/android')
@@ -3402,7 +3365,6 @@ if (PkgSkip("PANDATOOL")==0):
     CopyAllHeaders('pandatool/src/progbase')
     CopyAllHeaders('pandatool/src/progbase')
     CopyAllHeaders('pandatool/src/eggbase')
     CopyAllHeaders('pandatool/src/eggbase')
     CopyAllHeaders('pandatool/src/bam')
     CopyAllHeaders('pandatool/src/bam')
-    CopyAllHeaders('pandatool/src/cvscopy')
     CopyAllHeaders('pandatool/src/daeegg')
     CopyAllHeaders('pandatool/src/daeegg')
     CopyAllHeaders('pandatool/src/daeprogs')
     CopyAllHeaders('pandatool/src/daeprogs')
     CopyAllHeaders('pandatool/src/dxf')
     CopyAllHeaders('pandatool/src/dxf')
@@ -4022,12 +3984,12 @@ if (not RUNTIME):
 #
 #
 
 
 if (not RUNTIME):
 if (not RUNTIME):
-  OPTS=['DIR:panda/src/display', 'BUILDING:PANDA']
+  OPTS=['DIR:panda/src/display', 'BUILDING:PANDA', 'X11']
   TargetAdd('p3display_graphicsStateGuardian.obj', opts=OPTS, input='graphicsStateGuardian.cxx')
   TargetAdd('p3display_graphicsStateGuardian.obj', opts=OPTS, input='graphicsStateGuardian.cxx')
   TargetAdd('p3display_composite1.obj', opts=OPTS, input='p3display_composite1.cxx')
   TargetAdd('p3display_composite1.obj', opts=OPTS, input='p3display_composite1.cxx')
   TargetAdd('p3display_composite2.obj', opts=OPTS, input='p3display_composite2.cxx')
   TargetAdd('p3display_composite2.obj', opts=OPTS, input='p3display_composite2.cxx')
 
 
-  OPTS=['DIR:panda/src/display']
+  OPTS=['DIR:panda/src/display', 'X11']
   IGATEFILES=GetDirectoryContents('panda/src/display', ["*.h", "*_composite*.cxx"])
   IGATEFILES=GetDirectoryContents('panda/src/display', ["*.h", "*_composite*.cxx"])
   IGATEFILES.remove("renderBuffer.h")
   IGATEFILES.remove("renderBuffer.h")
   TargetAdd('libp3display.in', opts=OPTS, input=IGATEFILES)
   TargetAdd('libp3display.in', opts=OPTS, input=IGATEFILES)
@@ -4875,26 +4837,6 @@ if (GetTarget() == 'darwin' and PkgSkip("COCOA")==0 and PkgSkip("GL")==0 and not
   TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'COCOA', 'CARBON'])
   TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'COCOA', 'CARBON'])
 
 
-#
-# DIRECTORY: panda/src/osxdisplay/
-#
-
-elif (GetTarget() == 'darwin' and PkgSkip("CARBON")==0 and PkgSkip("GL")==0 and not RUNTIME):
-  OPTS=['DIR:panda/src/osxdisplay', 'BUILDING:PANDAGL',  'GL', 'NVIDIACG', 'CGGL']
-  TargetAdd('p3osxdisplay_composite1.obj', opts=OPTS, input='p3osxdisplay_composite1.cxx')
-  TargetAdd('p3osxdisplay_osxGraphicsWindow.obj', opts=OPTS, input='osxGraphicsWindow.mm')
-  OPTS=['DIR:panda/metalibs/pandagl', 'BUILDING:PANDAGL',  'GL', 'NVIDIACG', 'CGGL']
-  TargetAdd('pandagl_pandagl.obj', opts=OPTS, input='pandagl.cxx')
-  TargetAdd('libpandagl.dll', input='pandagl_pandagl.obj')
-  TargetAdd('libpandagl.dll', input='p3glgsg_config_glgsg.obj')
-  TargetAdd('libpandagl.dll', input='p3glgsg_glgsg.obj')
-  TargetAdd('libpandagl.dll', input='p3osxdisplay_composite1.obj')
-  TargetAdd('libpandagl.dll', input='p3osxdisplay_osxGraphicsWindow.obj')
-  if (PkgSkip('PANDAFX')==0):
-    TargetAdd('libpandagl.dll', input='libpandafx.dll')
-  TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'CARBON', 'AGL', 'COCOA'])
-
 #
 #
 # DIRECTORY: panda/src/wgldisplay/
 # DIRECTORY: panda/src/wgldisplay/
 #
 #
@@ -5035,10 +4977,10 @@ if (PkgSkip("BULLET")==0 and not RUNTIME):
 #
 #
 
 
 if (PkgSkip("PHYSX")==0):
 if (PkgSkip("PHYSX")==0):
-  OPTS=['DIR:panda/src/physx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'NOARCH:PPC']
+  OPTS=['DIR:panda/src/physx', 'BUILDING:PANDAPHYSX', 'PHYSX']
   TargetAdd('p3physx_composite.obj', opts=OPTS, input='p3physx_composite.cxx')
   TargetAdd('p3physx_composite.obj', opts=OPTS, input='p3physx_composite.cxx')
 
 
-  OPTS=['DIR:panda/src/physx', 'PHYSX', 'NOARCH:PPC']
+  OPTS=['DIR:panda/src/physx', 'PHYSX']
   IGATEFILES=GetDirectoryContents('panda/src/physx', ["*.h", "*_composite*.cxx"])
   IGATEFILES=GetDirectoryContents('panda/src/physx', ["*.h", "*_composite*.cxx"])
   TargetAdd('libpandaphysx.in', opts=OPTS, input=IGATEFILES)
   TargetAdd('libpandaphysx.in', opts=OPTS, input=IGATEFILES)
   TargetAdd('libpandaphysx.in', opts=['IMOD:panda3d.physx', 'ILIB:libpandaphysx', 'SRCDIR:panda/src/physx'])
   TargetAdd('libpandaphysx.in', opts=['IMOD:panda3d.physx', 'ILIB:libpandaphysx', 'SRCDIR:panda/src/physx'])
@@ -5048,15 +4990,15 @@ if (PkgSkip("PHYSX")==0):
 #
 #
 
 
 if (PkgSkip("PHYSX")==0):
 if (PkgSkip("PHYSX")==0):
-  OPTS=['DIR:panda/metalibs/pandaphysx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'NOARCH:PPC', 'PYTHON']
+  OPTS=['DIR:panda/metalibs/pandaphysx', 'BUILDING:PANDAPHYSX', 'PHYSX', 'PYTHON']
   TargetAdd('pandaphysx_pandaphysx.obj', opts=OPTS, input='pandaphysx.cxx')
   TargetAdd('pandaphysx_pandaphysx.obj', opts=OPTS, input='pandaphysx.cxx')
 
 
   TargetAdd('libpandaphysx.dll', input='pandaphysx_pandaphysx.obj')
   TargetAdd('libpandaphysx.dll', input='pandaphysx_pandaphysx.obj')
   TargetAdd('libpandaphysx.dll', input='p3physx_composite.obj')
   TargetAdd('libpandaphysx.dll', input='p3physx_composite.obj')
   TargetAdd('libpandaphysx.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libpandaphysx.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libpandaphysx.dll', opts=['WINUSER', 'PHYSX', 'NOARCH:PPC', 'PYTHON'])
+  TargetAdd('libpandaphysx.dll', opts=['WINUSER', 'PHYSX', 'PYTHON'])
 
 
-  OPTS=['DIR:panda/metalibs/pandaphysx', 'PHYSX', 'NOARCH:PPC']
+  OPTS=['DIR:panda/metalibs/pandaphysx', 'PHYSX']
   PyTargetAdd('physx_module.obj', input='libpandaphysx.in')
   PyTargetAdd('physx_module.obj', input='libpandaphysx.in')
   PyTargetAdd('physx_module.obj', opts=OPTS)
   PyTargetAdd('physx_module.obj', opts=OPTS)
   PyTargetAdd('physx_module.obj', opts=['IMOD:panda3d.physx', 'ILIB:physx', 'IMPORT:panda3d.core'])
   PyTargetAdd('physx_module.obj', opts=['IMOD:panda3d.physx', 'ILIB:physx', 'IMPORT:panda3d.core'])
@@ -5066,7 +5008,7 @@ if (PkgSkip("PHYSX")==0):
   PyTargetAdd('physx.pyd', input='libpandaphysx.dll')
   PyTargetAdd('physx.pyd', input='libpandaphysx.dll')
   PyTargetAdd('physx.pyd', input='libp3interrogatedb.dll')
   PyTargetAdd('physx.pyd', input='libp3interrogatedb.dll')
   PyTargetAdd('physx.pyd', input=COMMON_PANDA_LIBS)
   PyTargetAdd('physx.pyd', input=COMMON_PANDA_LIBS)
-  PyTargetAdd('physx.pyd', opts=['WINUSER', 'PHYSX', 'NOARCH:PPC'])
+  PyTargetAdd('physx.pyd', opts=['WINUSER', 'PHYSX'])
 
 
 #
 #
 # DIRECTORY: panda/src/physics/
 # DIRECTORY: panda/src/physics/
@@ -5239,7 +5181,7 @@ if (GetTarget() == 'android' and PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and no
 #
 #
 
 
 if (not RUNTIME and (GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0) and PkgSkip("TINYDISPLAY")==0):
 if (not RUNTIME and (GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0) and PkgSkip("TINYDISPLAY")==0):
-  OPTS=['DIR:panda/src/tinydisplay', 'BUILDING:TINYDISPLAY']
+  OPTS=['DIR:panda/src/tinydisplay', 'BUILDING:TINYDISPLAY', 'X11']
   TargetAdd('p3tinydisplay_composite1.obj', opts=OPTS, input='p3tinydisplay_composite1.cxx')
   TargetAdd('p3tinydisplay_composite1.obj', opts=OPTS, input='p3tinydisplay_composite1.cxx')
   TargetAdd('p3tinydisplay_composite2.obj', opts=OPTS, input='p3tinydisplay_composite2.cxx')
   TargetAdd('p3tinydisplay_composite2.obj', opts=OPTS, input='p3tinydisplay_composite2.cxx')
   TargetAdd('p3tinydisplay_ztriangle_1.obj', opts=OPTS, input='ztriangle_1.cxx')
   TargetAdd('p3tinydisplay_ztriangle_1.obj', opts=OPTS, input='ztriangle_1.cxx')
@@ -5247,14 +5189,10 @@ if (not RUNTIME and (GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0)
   TargetAdd('p3tinydisplay_ztriangle_3.obj', opts=OPTS, input='ztriangle_3.cxx')
   TargetAdd('p3tinydisplay_ztriangle_3.obj', opts=OPTS, input='ztriangle_3.cxx')
   TargetAdd('p3tinydisplay_ztriangle_4.obj', opts=OPTS, input='ztriangle_4.cxx')
   TargetAdd('p3tinydisplay_ztriangle_4.obj', opts=OPTS, input='ztriangle_4.cxx')
   TargetAdd('p3tinydisplay_ztriangle_table.obj', opts=OPTS, input='ztriangle_table.cxx')
   TargetAdd('p3tinydisplay_ztriangle_table.obj', opts=OPTS, input='ztriangle_table.cxx')
-  if GetTarget() == 'darwin':
-    TargetAdd('p3tinydisplay_tinyOsxGraphicsWindow.obj', opts=OPTS, input='tinyOsxGraphicsWindow.mm')
-    TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_tinyOsxGraphicsWindow.obj')
-    TargetAdd('libp3tinydisplay.dll', opts=['CARBON', 'AGL', 'COCOA'])
-  elif GetTarget() == 'windows':
+  if GetTarget() == 'windows':
     TargetAdd('libp3tinydisplay.dll', input='libp3windisplay.dll')
     TargetAdd('libp3tinydisplay.dll', input='libp3windisplay.dll')
     TargetAdd('libp3tinydisplay.dll', opts=['WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM'])
     TargetAdd('libp3tinydisplay.dll', opts=['WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM'])
-  else:
+  elif GetTarget() != 'darwin':
     TargetAdd('libp3tinydisplay.dll', input='p3x11display_composite1.obj')
     TargetAdd('libp3tinydisplay.dll', input='p3x11display_composite1.obj')
     TargetAdd('libp3tinydisplay.dll', opts=['X11'])
     TargetAdd('libp3tinydisplay.dll', opts=['X11'])
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite1.obj')
   TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite1.obj')
@@ -5799,15 +5737,6 @@ if not PkgSkip("PANDATOOL"):
     TargetAdd('egg2bam.exe', input=COMMON_EGG2X_LIBS)
     TargetAdd('egg2bam.exe', input=COMMON_EGG2X_LIBS)
     TargetAdd('egg2bam.exe', opts=['ADVAPI',  'FFTW'])
     TargetAdd('egg2bam.exe', opts=['ADVAPI',  'FFTW'])
 
 
-#
-# DIRECTORY: pandatool/src/cvscopy/
-#
-
-if not PkgSkip("PANDATOOL"):
-  OPTS=['DIR:pandatool/src/cvscopy']
-  TargetAdd('p3cvscopy_composite1.obj', opts=OPTS, input='p3cvscopy_composite1.cxx')
-  TargetAdd('libp3cvscopy.lib', input='p3cvscopy_composite1.obj')
-
 #
 #
 # DIRECTORY: pandatool/src/daeegg/
 # DIRECTORY: pandatool/src/daeegg/
 #
 #
@@ -5825,7 +5754,7 @@ if not PkgSkip("PANDATOOL") and not PkgSkip("ASSIMP"):
   TargetAdd('p3assimp_composite1.obj', opts=OPTS, input='p3assimp_composite1.cxx')
   TargetAdd('p3assimp_composite1.obj', opts=OPTS, input='p3assimp_composite1.cxx')
   TargetAdd('libp3assimp.dll', input='p3assimp_composite1.obj')
   TargetAdd('libp3assimp.dll', input='p3assimp_composite1.obj')
   TargetAdd('libp3assimp.dll', input=COMMON_PANDA_LIBS)
   TargetAdd('libp3assimp.dll', input=COMMON_PANDA_LIBS)
-  TargetAdd('libp3assimp.dll', opts=OPTS+['ZLIB'])
+  TargetAdd('libp3assimp.dll', opts=OPTS+['ZLIB', 'ADVAPI'])
 
 
 #
 #
 # DIRECTORY: pandatool/src/daeprogs/
 # DIRECTORY: pandatool/src/daeprogs/
@@ -6072,7 +6001,7 @@ if not PkgSkip("PANDATOOL") and not PkgSkip("EGG"):
 #
 #
 
 
 if not PkgSkip("PANDATOOL"):
 if not PkgSkip("PANDATOOL"):
-  OPTS=['DIR:pandatool/src/fltprogs', 'DIR:pandatool/src/flt', 'DIR:pandatool/src/cvscopy']
+  OPTS=['DIR:pandatool/src/fltprogs', 'DIR:pandatool/src/flt']
   TargetAdd('flt-info_fltInfo.obj', opts=OPTS, input='fltInfo.cxx')
   TargetAdd('flt-info_fltInfo.obj', opts=OPTS, input='fltInfo.cxx')
   TargetAdd('flt-info.exe', input='flt-info_fltInfo.obj')
   TargetAdd('flt-info.exe', input='flt-info_fltInfo.obj')
   TargetAdd('flt-info.exe', input='libp3flt.lib')
   TargetAdd('flt-info.exe', input='libp3flt.lib')
@@ -6089,15 +6018,6 @@ if not PkgSkip("PANDATOOL"):
   TargetAdd('flt-trans.exe', input=COMMON_PANDA_LIBS)
   TargetAdd('flt-trans.exe', input=COMMON_PANDA_LIBS)
   TargetAdd('flt-trans.exe', opts=['ADVAPI'])
   TargetAdd('flt-trans.exe', opts=['ADVAPI'])
 
 
-  TargetAdd('fltcopy_fltCopy.obj', opts=OPTS, input='fltCopy.cxx')
-  TargetAdd('fltcopy.exe', input='fltcopy_fltCopy.obj')
-  TargetAdd('fltcopy.exe', input='libp3cvscopy.lib')
-  TargetAdd('fltcopy.exe', input='libp3flt.lib')
-  TargetAdd('fltcopy.exe', input='libp3progbase.lib')
-  TargetAdd('fltcopy.exe', input='libp3pandatoolbase.lib')
-  TargetAdd('fltcopy.exe', input=COMMON_PANDA_LIBS)
-  TargetAdd('fltcopy.exe', opts=['ADVAPI'])
-
   if not PkgSkip("EGG"):
   if not PkgSkip("EGG"):
     TargetAdd('egg2flt_eggToFlt.obj', opts=OPTS, input='eggToFlt.cxx')
     TargetAdd('egg2flt_eggToFlt.obj', opts=OPTS, input='eggToFlt.cxx')
     TargetAdd('egg2flt.exe', input='egg2flt_eggToFlt.obj')
     TargetAdd('egg2flt.exe', input='egg2flt_eggToFlt.obj')
@@ -6482,18 +6402,11 @@ if not PkgSkip("PANDATOOL"):
 for VER in MAYAVERSIONS:
 for VER in MAYAVERSIONS:
   VNUM = VER[4:]
   VNUM = VER[4:]
   if not PkgSkip(VER) and not PkgSkip("PANDATOOL") and not PkgSkip("EGG"):
   if not PkgSkip(VER) and not PkgSkip("PANDATOOL") and not PkgSkip("EGG"):
-    if GetTarget() == 'darwin' and int(VNUM) >= 2012:
-      ARCH_OPTS = ['NOARCH:PPC', 'NOARCH:I386']
-      if len(OSX_ARCHS) != 0 and 'x86_64' not in OSX_ARCHS:
-        continue
-    elif GetTarget() == 'darwin' and int(VNUM) >= 2009:
-      ARCH_OPTS = ['NOARCH:PPC']
-    elif GetTarget() == 'darwin':
-      ARCH_OPTS = ['NOARCH:X86_64']
-    else:
-      ARCH_OPTS = []
+    if GetTarget() == 'darwin' and int(VNUM) < 2009:
+      # No x86_64 support.
+      continue
 
 
-    OPTS=['DIR:pandatool/src/mayaprogs', 'DIR:pandatool/src/maya', 'DIR:pandatool/src/mayaegg', 'DIR:pandatool/src/cvscopy', 'BUILDING:MISC', VER] + ARCH_OPTS
+    OPTS=['DIR:pandatool/src/mayaprogs', 'DIR:pandatool/src/maya', 'DIR:pandatool/src/mayaegg', 'BUILDING:MISC', VER]
     TargetAdd('mayaeggimport'+VNUM+'_mayaeggimport.obj', opts=OPTS, input='mayaEggImport.cxx')
     TargetAdd('mayaeggimport'+VNUM+'_mayaeggimport.obj', opts=OPTS, input='mayaEggImport.cxx')
     TargetAdd('mayaeggimport'+VNUM+'.mll', input='mayaegg'+VNUM+'_loader.obj')
     TargetAdd('mayaeggimport'+VNUM+'.mll', input='mayaegg'+VNUM+'_loader.obj')
     TargetAdd('mayaeggimport'+VNUM+'.mll', input='mayaeggimport'+VNUM+'_mayaeggimport.obj')
     TargetAdd('mayaeggimport'+VNUM+'.mll', input='mayaeggimport'+VNUM+'_mayaeggimport.obj')
@@ -6501,7 +6414,7 @@ for VER in MAYAVERSIONS:
     TargetAdd('mayaeggimport'+VNUM+'.mll', input=COMMON_PANDA_LIBS)
     TargetAdd('mayaeggimport'+VNUM+'.mll', input=COMMON_PANDA_LIBS)
     #if GetTarget() == 'windows':
     #if GetTarget() == 'windows':
     #  TargetAdd('mayaeggimport'+VNUM+'.mll', input='libp3pystub.lib')
     #  TargetAdd('mayaeggimport'+VNUM+'.mll', input='libp3pystub.lib')
-    TargetAdd('mayaeggimport'+VNUM+'.mll', opts=['ADVAPI', VER]+ARCH_OPTS)
+    TargetAdd('mayaeggimport'+VNUM+'.mll', opts=['ADVAPI', VER])
 
 
     TargetAdd('mayaloader'+VNUM+'_config_mayaloader.obj', opts=OPTS, input='config_mayaloader.cxx')
     TargetAdd('mayaloader'+VNUM+'_config_mayaloader.obj', opts=OPTS, input='config_mayaloader.cxx')
     TargetAdd('libp3mayaloader'+VNUM+'.dll', input='mayaloader'+VNUM+'_config_mayaloader.obj')
     TargetAdd('libp3mayaloader'+VNUM+'.dll', input='mayaloader'+VNUM+'_config_mayaloader.obj')
@@ -6525,7 +6438,7 @@ for VER in MAYAVERSIONS:
     TargetAdd('libp3mayaloader'+VNUM+'.dll', input='libp3pandatoolbase.lib')
     TargetAdd('libp3mayaloader'+VNUM+'.dll', input='libp3pandatoolbase.lib')
     TargetAdd('libp3mayaloader'+VNUM+'.dll', input='libpandaegg.dll')
     TargetAdd('libp3mayaloader'+VNUM+'.dll', input='libpandaegg.dll')
     TargetAdd('libp3mayaloader'+VNUM+'.dll', input=COMMON_PANDA_LIBS)
     TargetAdd('libp3mayaloader'+VNUM+'.dll', input=COMMON_PANDA_LIBS)
-    TargetAdd('libp3mayaloader'+VNUM+'.dll', opts=['ADVAPI', VER]+ARCH_OPTS)
+    TargetAdd('libp3mayaloader'+VNUM+'.dll', opts=['ADVAPI', VER])
 
 
     TargetAdd('mayapview'+VNUM+'_mayaPview.obj', opts=OPTS, input='mayaPview.cxx')
     TargetAdd('mayapview'+VNUM+'_mayaPview.obj', opts=OPTS, input='mayaPview.cxx')
     TargetAdd('libmayapview'+VNUM+'.mll', input='mayapview'+VNUM+'_mayaPview.obj')
     TargetAdd('libmayapview'+VNUM+'.mll', input='mayapview'+VNUM+'_mayaPview.obj')
@@ -6536,7 +6449,7 @@ for VER in MAYAVERSIONS:
       TargetAdd('libmayapview'+VNUM+'.mll', input=COMMON_EGG2X_LIBS)
       TargetAdd('libmayapview'+VNUM+'.mll', input=COMMON_EGG2X_LIBS)
     else:
     else:
       TargetAdd('libmayapview'+VNUM+'.mll', input=COMMON_EGG2X_LIBS)
       TargetAdd('libmayapview'+VNUM+'.mll', input=COMMON_EGG2X_LIBS)
-    TargetAdd('libmayapview'+VNUM+'.mll', opts=['ADVAPI', VER]+ARCH_OPTS)
+    TargetAdd('libmayapview'+VNUM+'.mll', opts=['ADVAPI', VER])
 
 
     TargetAdd('maya2egg'+VNUM+'_mayaToEgg.obj', opts=OPTS, input='mayaToEgg.cxx')
     TargetAdd('maya2egg'+VNUM+'_mayaToEgg.obj', opts=OPTS, input='mayaToEgg.cxx')
     TargetAdd('maya2egg'+VNUM+'_bin.exe', input='maya2egg'+VNUM+'_mayaToEgg.obj')
     TargetAdd('maya2egg'+VNUM+'_bin.exe', input='maya2egg'+VNUM+'_mayaToEgg.obj')
@@ -6546,7 +6459,7 @@ for VER in MAYAVERSIONS:
       TargetAdd('maya2egg'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS)
       TargetAdd('maya2egg'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS)
     else:
     else:
       TargetAdd('maya2egg'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS)
       TargetAdd('maya2egg'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS)
-    TargetAdd('maya2egg'+VNUM+'_bin.exe', opts=['ADVAPI', VER]+ARCH_OPTS)
+    TargetAdd('maya2egg'+VNUM+'_bin.exe', opts=['ADVAPI', VER])
 
 
     TargetAdd('egg2maya'+VNUM+'_eggToMaya.obj', opts=OPTS, input='eggToMaya.cxx')
     TargetAdd('egg2maya'+VNUM+'_eggToMaya.obj', opts=OPTS, input='eggToMaya.cxx')
     TargetAdd('egg2maya'+VNUM+'_bin.exe', input='egg2maya'+VNUM+'_eggToMaya.obj')
     TargetAdd('egg2maya'+VNUM+'_bin.exe', input='egg2maya'+VNUM+'_eggToMaya.obj')
@@ -6556,38 +6469,23 @@ for VER in MAYAVERSIONS:
       TargetAdd('egg2maya'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS)
       TargetAdd('egg2maya'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS)
     else:
     else:
       TargetAdd('egg2maya'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS)
       TargetAdd('egg2maya'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS)
-    TargetAdd('egg2maya'+VNUM+'_bin.exe', opts=['ADVAPI', VER]+ARCH_OPTS)
-
-    TargetAdd('mayacopy'+VNUM+'_mayaCopy.obj', opts=OPTS, input='mayaCopy.cxx')
-    TargetAdd('mayacopy'+VNUM+'_bin.exe', input='mayacopy'+VNUM+'_mayaCopy.obj')
-    TargetAdd('mayacopy'+VNUM+'_bin.exe', input='libp3cvscopy.lib')
-    TargetAdd('mayacopy'+VNUM+'_bin.exe', input='libmaya'+VNUM+'.lib')
-    if GetTarget() == 'windows':
-      TargetAdd('mayacopy'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS)
-    else:
-      TargetAdd('mayacopy'+VNUM+'_bin.exe', input=COMMON_EGG2X_LIBS)
-    TargetAdd('mayacopy'+VNUM+'_bin.exe', opts=['ADVAPI', VER]+ARCH_OPTS)
+    TargetAdd('egg2maya'+VNUM+'_bin.exe', opts=['ADVAPI', VER])
 
 
     TargetAdd('mayasavepview'+VNUM+'_mayaSavePview.obj', opts=OPTS, input='mayaSavePview.cxx')
     TargetAdd('mayasavepview'+VNUM+'_mayaSavePview.obj', opts=OPTS, input='mayaSavePview.cxx')
     TargetAdd('libmayasavepview'+VNUM+'.mll', input='mayasavepview'+VNUM+'_mayaSavePview.obj')
     TargetAdd('libmayasavepview'+VNUM+'.mll', input='mayasavepview'+VNUM+'_mayaSavePview.obj')
-    TargetAdd('libmayasavepview'+VNUM+'.mll', opts=['ADVAPI', VER]+ARCH_OPTS)
+    TargetAdd('libmayasavepview'+VNUM+'.mll', opts=['ADVAPI', VER])
 
 
     TargetAdd('mayapath'+VNUM+'.obj', opts=OPTS, input='mayapath.cxx')
     TargetAdd('mayapath'+VNUM+'.obj', opts=OPTS, input='mayapath.cxx')
 
 
     TargetAdd('maya2egg'+VNUM+'.exe', input='mayapath'+VNUM+'.obj')
     TargetAdd('maya2egg'+VNUM+'.exe', input='mayapath'+VNUM+'.obj')
     TargetAdd('maya2egg'+VNUM+'.exe', input='libpandaexpress.dll')
     TargetAdd('maya2egg'+VNUM+'.exe', input='libpandaexpress.dll')
     TargetAdd('maya2egg'+VNUM+'.exe', input=COMMON_DTOOL_LIBS)
     TargetAdd('maya2egg'+VNUM+'.exe', input=COMMON_DTOOL_LIBS)
-    TargetAdd('maya2egg'+VNUM+'.exe', opts=['ADVAPI']+ARCH_OPTS)
+    TargetAdd('maya2egg'+VNUM+'.exe', opts=['ADVAPI'])
 
 
     TargetAdd('egg2maya'+VNUM+'.exe', input='mayapath'+VNUM+'.obj')
     TargetAdd('egg2maya'+VNUM+'.exe', input='mayapath'+VNUM+'.obj')
     TargetAdd('egg2maya'+VNUM+'.exe', input='libpandaexpress.dll')
     TargetAdd('egg2maya'+VNUM+'.exe', input='libpandaexpress.dll')
     TargetAdd('egg2maya'+VNUM+'.exe', input=COMMON_DTOOL_LIBS)
     TargetAdd('egg2maya'+VNUM+'.exe', input=COMMON_DTOOL_LIBS)
-    TargetAdd('egg2maya'+VNUM+'.exe', opts=['ADVAPI']+ARCH_OPTS)
-
-    TargetAdd('mayacopy'+VNUM+'.exe', input='mayapath'+VNUM+'.obj')
-    TargetAdd('mayacopy'+VNUM+'.exe', input='libpandaexpress.dll')
-    TargetAdd('mayacopy'+VNUM+'.exe', input=COMMON_DTOOL_LIBS)
-    TargetAdd('mayacopy'+VNUM+'.exe', opts=['ADVAPI']+ARCH_OPTS)
+    TargetAdd('egg2maya'+VNUM+'.exe', opts=['ADVAPI'])
 
 
 #
 #
 # DIRECTORY: contrib/src/ai/
 # DIRECTORY: contrib/src/ai/
@@ -6645,7 +6543,7 @@ if PkgSkip("PYTHON") == 0:
 
 
     if GetTarget() == 'linux' or GetTarget() == 'freebsd':
     if GetTarget() == 'linux' or GetTarget() == 'freebsd':
         # Setup rpath so libs can be found in the same directory as the deployed game
         # Setup rpath so libs can be found in the same directory as the deployed game
-        LibName('DEPLOYSTUB', "-Wl,-rpath,\$ORIGIN")
+        LibName('DEPLOYSTUB', "-Wl,-rpath,\\$ORIGIN")
         LibName('DEPLOYSTUB', "-Wl,-z,origin")
         LibName('DEPLOYSTUB', "-Wl,-z,origin")
         LibName('DEPLOYSTUB', "-rdynamic")
         LibName('DEPLOYSTUB', "-rdynamic")
     PyTargetAdd('deploy-stub.exe', input='deploy-stub.obj')
     PyTargetAdd('deploy-stub.exe', input='deploy-stub.obj')

+ 0 - 37
makepanda/makepanda.vcproj

@@ -1619,7 +1619,6 @@
 				<File RelativePath="..\panda\src\tinydisplay\zmath.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\zmath.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\zdither.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\zdither.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\zbuffer.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\zbuffer.cxx"></File>
-				<File RelativePath="..\panda\src\tinydisplay\tinyOsxGraphicsPipe.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinydisplay_composite2.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinydisplay_composite2.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinySDLGraphicsWindow.I"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinySDLGraphicsWindow.I"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyOffscreenGraphicsPipe.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyOffscreenGraphicsPipe.cxx"></File>
@@ -1627,13 +1626,11 @@
 				<File RelativePath="..\panda\src\tinydisplay\tinyOffscreenGraphicsPipe.I"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyOffscreenGraphicsPipe.I"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyWinGraphicsWindow.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyWinGraphicsWindow.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\image_util.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\image_util.cxx"></File>
-				<File RelativePath="..\panda\src\tinydisplay\tinyOsxGraphicsPipe.I"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyGeomMunger.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyGeomMunger.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyGeomMunger.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyGeomMunger.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyWinGraphicsPipe.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyWinGraphicsPipe.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\td_texture.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\td_texture.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyWinGraphicsWindow.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyWinGraphicsWindow.cxx"></File>
-				<File RelativePath="..\panda\src\tinydisplay\tinyOsxGraphicsWindow.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\config_tinydisplay.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\config_tinydisplay.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinySDLGraphicsPipe.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinySDLGraphicsPipe.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\ztriangle_code_3.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\ztriangle_code_3.h"></File>
@@ -1675,12 +1672,10 @@
 				<File RelativePath="..\panda\src\tinydisplay\ztriangle_code_1.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\ztriangle_code_1.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyXGraphicsWindow.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyXGraphicsWindow.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\ztriangle_2.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\ztriangle_2.cxx"></File>
-				<File RelativePath="..\panda\src\tinydisplay\tinyOsxGraphicsWindow.I"></File>
 				<File RelativePath="..\panda\src\tinydisplay\ztriangle_3.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\ztriangle_3.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\init.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\init.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\zbuffer.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\zbuffer.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\vertex.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\vertex.cxx"></File>
-				<File RelativePath="..\panda\src\tinydisplay\tinyOsxGraphicsPipe.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyWinGraphicsWindow.I"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyWinGraphicsWindow.I"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyGraphicsStateGuardian.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyGraphicsStateGuardian.cxx"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyXGraphicsPipe.h"></File>
 				<File RelativePath="..\panda\src\tinydisplay\tinyXGraphicsPipe.h"></File>
@@ -2211,7 +2206,6 @@
 				<File RelativePath="..\panda\src\express\typedReferenceCount.h"></File>
 				<File RelativePath="..\panda\src\express\typedReferenceCount.h"></File>
 				<File RelativePath="..\panda\src\express\virtualFileMountSystem.I"></File>
 				<File RelativePath="..\panda\src\express\virtualFileMountSystem.I"></File>
 				<File RelativePath="..\panda\src\express\nodePointerTo.I"></File>
 				<File RelativePath="..\panda\src\express\nodePointerTo.I"></File>
-				<File RelativePath="..\panda\src\express\profileTimer.I"></File>
 				<File RelativePath="..\panda\src\express\hashVal.I"></File>
 				<File RelativePath="..\panda\src\express\hashVal.I"></File>
 				<File RelativePath="..\panda\src\express\pta_float.cxx"></File>
 				<File RelativePath="..\panda\src\express\pta_float.cxx"></File>
 				<File RelativePath="..\panda\src\express\weakPointerTo.h"></File>
 				<File RelativePath="..\panda\src\express\weakPointerTo.h"></File>
@@ -2220,10 +2214,8 @@
 				<File RelativePath="..\panda\src\express\pointerToArrayBase.I"></File>
 				<File RelativePath="..\panda\src\express\pointerToArrayBase.I"></File>
 				<File RelativePath="..\panda\src\express\datagramIterator.I"></File>
 				<File RelativePath="..\panda\src\express\datagramIterator.I"></File>
 				<File RelativePath="..\panda\src\express\vector_uchar.h"></File>
 				<File RelativePath="..\panda\src\express\vector_uchar.h"></File>
-				<File RelativePath="..\panda\src\express\profileTimer.h"></File>
 				<File RelativePath="..\panda\src\express\datagramIterator.h"></File>
 				<File RelativePath="..\panda\src\express\datagramIterator.h"></File>
 				<File RelativePath="..\panda\src\express\threadSafePointerTo.cxx"></File>
 				<File RelativePath="..\panda\src\express\threadSafePointerTo.cxx"></File>
-				<File RelativePath="..\panda\src\express\profileTimer.cxx"></File>
 				<File RelativePath="..\panda\src\express\datagramGenerator.cxx"></File>
 				<File RelativePath="..\panda\src\express\datagramGenerator.cxx"></File>
 				<File RelativePath="..\panda\src\express\hashGeneratorBase.h"></File>
 				<File RelativePath="..\panda\src\express\hashGeneratorBase.h"></File>
 				<File RelativePath="..\panda\src\express\patchfile.cxx"></File>
 				<File RelativePath="..\panda\src\express\patchfile.cxx"></File>
@@ -3506,20 +3498,6 @@
 				<File RelativePath="..\panda\src\physx\physxConvexShapeDesc.I"></File>
 				<File RelativePath="..\panda\src\physx\physxConvexShapeDesc.I"></File>
 				<File RelativePath="..\panda\src\physx\physxMaterialDesc.h"></File>
 				<File RelativePath="..\panda\src\physx\physxMaterialDesc.h"></File>
 			</Filter>
 			</Filter>
-			<Filter Name="osxdisplay">
-				<File RelativePath="..\panda\src\osxdisplay\osxGraphicsBuffer.h"></File>
-				<File RelativePath="..\panda\src\osxdisplay\osxGraphicsWindow.I"></File>
-				<File RelativePath="..\panda\src\osxdisplay\osxGraphicsPipe.cxx"></File>
-				<File RelativePath="..\panda\src\osxdisplay\osxGraphicsBuffer.cxx"></File>
-				<File RelativePath="..\panda\src\osxdisplay\config_osxdisplay.h"></File>
-				<File RelativePath="..\panda\src\osxdisplay\resize_box.rgb.c"></File>
-				<File RelativePath="..\panda\src\osxdisplay\osxGraphicsStateGuardian.h"></File>
-				<File RelativePath="..\panda\src\osxdisplay\osxGraphicsWindow.h"></File>
-				<File RelativePath="..\panda\src\osxdisplay\config_osxdisplay.cxx"></File>
-				<File RelativePath="..\panda\src\osxdisplay\osxGraphicsPipe.h"></File>
-				<File RelativePath="..\panda\src\osxdisplay\osxdisplay_composite1.cxx"></File>
-				<File RelativePath="..\panda\src\osxdisplay\osxGraphicsStateGuardian.cxx"></File>
-			</Filter>
 			<Filter Name="net">
 			<Filter Name="net">
 				<File RelativePath="..\panda\src\net\queuedConnectionReader.cxx"></File>
 				<File RelativePath="..\panda\src\net\queuedConnectionReader.cxx"></File>
 				<File RelativePath="..\panda\src\net\datagramGeneratorNet.I"></File>
 				<File RelativePath="..\panda\src\net\datagramGeneratorNet.I"></File>
@@ -4591,17 +4569,6 @@
 				<File RelativePath="..\pandatool\src\lwoprogs\lwoScan.h"></File>
 				<File RelativePath="..\pandatool\src\lwoprogs\lwoScan.h"></File>
 				<File RelativePath="..\pandatool\src\lwoprogs\lwoScan.cxx"></File>
 				<File RelativePath="..\pandatool\src\lwoprogs\lwoScan.cxx"></File>
 			</Filter>
 			</Filter>
-			<Filter Name="cvscopy">
-				<File RelativePath="..\pandatool\src\cvscopy\cvscopy_composite1.cxx"></File>
-				<File RelativePath="..\pandatool\src\cvscopy\testCopy.cxx"></File>
-				<File RelativePath="..\pandatool\src\cvscopy\cvsSourceDirectory.h"></File>
-				<File RelativePath="..\pandatool\src\cvscopy\testCopy.h"></File>
-				<File RelativePath="..\pandatool\src\cvscopy\cvsSourceDirectory.cxx"></File>
-				<File RelativePath="..\pandatool\src\cvscopy\cvsCopy.cxx"></File>
-				<File RelativePath="..\pandatool\src\cvscopy\cvsCopy.h"></File>
-				<File RelativePath="..\pandatool\src\cvscopy\cvsSourceTree.cxx"></File>
-				<File RelativePath="..\pandatool\src\cvscopy\cvsSourceTree.h"></File>
-			</Filter>
 			<Filter Name="pandatoolbase">
 			<Filter Name="pandatoolbase">
 				<File RelativePath="..\pandatool\src\pandatoolbase\animationConvert.cxx"></File>
 				<File RelativePath="..\pandatool\src\pandatoolbase\animationConvert.cxx"></File>
 				<File RelativePath="..\pandatool\src\pandatoolbase\pathReplace.I"></File>
 				<File RelativePath="..\pandatool\src\pandatoolbase\pathReplace.I"></File>
@@ -4769,11 +4736,9 @@
 			<Filter Name="fltprogs">
 			<Filter Name="fltprogs">
 				<File RelativePath="..\pandatool\src\fltprogs\fltToEgg.cxx"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltToEgg.cxx"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\eggToFlt.cxx"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\eggToFlt.cxx"></File>
-				<File RelativePath="..\pandatool\src\fltprogs\fltCopy.h"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltTrans.h"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltTrans.h"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltInfo.h"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltInfo.h"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltInfo.cxx"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltInfo.cxx"></File>
-				<File RelativePath="..\pandatool\src\fltprogs\fltCopy.cxx"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltTrans.cxx"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltTrans.cxx"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltToEgg.h"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\fltToEgg.h"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\eggToFlt.h"></File>
 				<File RelativePath="..\pandatool\src\fltprogs\eggToFlt.h"></File>
@@ -4939,11 +4904,9 @@
 				<File RelativePath="..\pandatool\src\mayaprogs\mayaToEgg_client.h"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\mayaToEgg_client.h"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\blend_test.cxx"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\blend_test.cxx"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\mayaToEgg_server.h"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\mayaToEgg_server.h"></File>
-				<File RelativePath="..\pandatool\src\mayaprogs\mayaCopy.h"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\eggToMaya.h"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\eggToMaya.h"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\mayaSavePview.h"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\mayaSavePview.h"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\mayaToEgg_server.cxx"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\mayaToEgg_server.cxx"></File>
-				<File RelativePath="..\pandatool\src\mayaprogs\mayaCopy.cxx"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\eggToMaya.cxx"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\eggToMaya.cxx"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\normal_test.cxx"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\normal_test.cxx"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\mayaSavePview.cxx"></File>
 				<File RelativePath="..\pandatool\src\mayaprogs\mayaSavePview.cxx"></File>

+ 130 - 20
makepanda/makepandacore.py

@@ -78,6 +78,7 @@ MSVCVERSIONINFO = {
     (12,0): {"vsversion":(12,0), "vsname":"Visual Studio 2013"},
     (12,0): {"vsversion":(12,0), "vsname":"Visual Studio 2013"},
     (14,0): {"vsversion":(14,0), "vsname":"Visual Studio 2015"},
     (14,0): {"vsversion":(14,0), "vsname":"Visual Studio 2015"},
     (14,1): {"vsversion":(15,0), "vsname":"Visual Studio 2017"},
     (14,1): {"vsversion":(15,0), "vsname":"Visual Studio 2017"},
+    (14,2): {"vsversion":(16,0), "vsname":"Visual Studio 2019"},
 }
 }
 
 
 ########################################################################
 ########################################################################
@@ -564,11 +565,12 @@ def LocateBinary(binary):
         p = os.environ["PATH"]
         p = os.environ["PATH"]
 
 
     pathList = p.split(os.pathsep)
     pathList = p.split(os.pathsep)
+    suffixes = ['']
 
 
     if GetHost() == 'windows':
     if GetHost() == 'windows':
-        if not binary.endswith('.exe'):
+        if not binary.lower().endswith('.exe') and not binary.lower().endswith('.bat'):
             # Append .exe if necessary
             # Append .exe if necessary
-            binary += '.exe'
+            suffixes = ['.exe', '.bat']
 
 
         # On Windows the current directory is always implicitly
         # On Windows the current directory is always implicitly
         # searched before anything else on PATH.
         # searched before anything else on PATH.
@@ -576,8 +578,9 @@ def LocateBinary(binary):
 
 
     for path in pathList:
     for path in pathList:
         binpath = os.path.join(os.path.expanduser(path), binary)
         binpath = os.path.join(os.path.expanduser(path), binary)
-        if os.access(binpath, os.X_OK):
-            return os.path.abspath(os.path.realpath(binpath))
+        for suffix in suffixes:
+            if os.access(binpath + suffix, os.X_OK):
+                return os.path.abspath(os.path.realpath(binpath + suffix))
     return None
     return None
 
 
 ########################################################################
 ########################################################################
@@ -586,20 +589,31 @@ def LocateBinary(binary):
 ##
 ##
 ########################################################################
 ########################################################################
 
 
-def oscmd(cmd, ignoreError = False):
+def oscmd(cmd, ignoreError = False, cwd=None):
     if VERBOSE:
     if VERBOSE:
         print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor())
         print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor())
     sys.stdout.flush()
     sys.stdout.flush()
 
 
     if sys.platform == "win32":
     if sys.platform == "win32":
-        exe = cmd.split()[0]
+        if cmd[0] == '"':
+            exe = cmd[1 : cmd.index('"', 1)]
+        else:
+            exe = cmd.split()[0]
         exe_path = LocateBinary(exe)
         exe_path = LocateBinary(exe)
         if exe_path is None:
         if exe_path is None:
             exit("Cannot find "+exe+" on search path")
             exit("Cannot find "+exe+" on search path")
+
+        if cwd is not None:
+            pwd = os.getcwd()
+            os.chdir(cwd)
+
         res = os.spawnl(os.P_WAIT, exe_path, cmd)
         res = os.spawnl(os.P_WAIT, exe_path, cmd)
+
+        if cwd is not None:
+            os.chdir(pwd)
     else:
     else:
         cmd = cmd.replace(';', '\\;')
         cmd = cmd.replace(';', '\\;')
-        res = subprocess.call(cmd, shell=True)
+        res = subprocess.call(cmd, cwd=cwd, shell=True)
         sig = res & 0x7F
         sig = res & 0x7F
         if (GetVerbose() and res != 0):
         if (GetVerbose() and res != 0):
             print(ColorText("red", "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig)))
             print(ColorText("red", "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig)))
@@ -1091,9 +1105,19 @@ def GetOptimizeOption(opts):
 ##
 ##
 ########################################################################
 ########################################################################
 
 
-def MakeDirectory(path):
-    if os.path.isdir(path): return 0
-    os.mkdir(path)
+def MakeDirectory(path, mode=None, recursive=False):
+    if os.path.isdir(path):
+        return
+
+    if recursive:
+        parent = os.path.dirname(path)
+        if parent and not os.path.isdir(parent):
+            MakeDirectory(parent, mode=mode, recursive=True)
+
+    if mode is not None:
+        os.mkdir(path, mode)
+    else:
+        os.mkdir(path)
 
 
 def ReadFile(wfile):
 def ReadFile(wfile):
     try:
     try:
@@ -2463,22 +2487,46 @@ def SdkLocateAndroid():
     SDK["ANDROID_TRIPLE"] = ANDROID_TRIPLE
     SDK["ANDROID_TRIPLE"] = ANDROID_TRIPLE
 
 
     if GetHost() == 'android':
     if GetHost() == 'android':
+        # Assume we're compiling from termux.
+        prefix = os.environ.get("PREFIX", "/data/data/com.termux/files/usr")
+        SDK["ANDROID_JAR"] = prefix + "/share/aapt/android.jar"
         return
         return
 
 
+    # Find the location of the Android SDK.
+    sdk_root = os.environ.get('ANDROID_HOME')
+    if not sdk_root or not os.path.isdir(sdk_root):
+        sdk_root = os.environ.get('ANDROID_SDK_ROOT')
+
+        # Try the default installation location on Windows.
+        if not sdk_root and GetHost() == 'windows':
+            sdk_root = os.path.expanduser(os.path.join('~', 'AppData', 'Local', 'Android', 'Sdk'))
+
+        if not sdk_root:
+            exit('ANDROID_SDK_ROOT must be set when compiling for Android!')
+        elif not os.path.isdir(sdk_root):
+            exit('Cannot find %s.  Please install Android SDK and set ANDROID_SDK_ROOT or ANDROID_HOME.' % (sdk_root))
+
     # Determine the NDK installation directory.
     # Determine the NDK installation directory.
-    if 'NDK_ROOT' not in os.environ:
-        exit('NDK_ROOT must be set when compiling for Android!')
+    if os.environ.get('NDK_ROOT') or os.environ.get('ANDROID_NDK_ROOT'):
+        # We have an explicit setting from an environment variable.
+        ndk_root = os.environ.get('ANDROID_NDK_ROOT')
+        if not ndk_root or not os.path.isdir(ndk_root):
+            ndk_root = os.environ.get('NDK_ROOT')
+            if not ndk_root or not os.path.isdir(ndk_root):
+                exit("Cannot find %s.  Please install Android NDK and set ANDROID_NDK_ROOT." % (ndk_root))
+    else:
+        # Often, it's installed in the ndk-bundle subdirectory of the SDK.
+        ndk_root = os.path.join(sdk_root, 'ndk-bundle')
 
 
-    ndk_root = os.environ["NDK_ROOT"]
-    if not os.path.isdir(ndk_root):
-        exit("Cannot find %s.  Please install Android NDK and set NDK_ROOT." % (ndk_root))
+        if not os.path.isdir(os.path.join(ndk_root, 'toolchains')):
+            exit('Cannot find the Android NDK.  Install it via the SDK manager or set the ANDROID_NDK_ROOT variable if you have installed it in a different location.')
 
 
     SDK["ANDROID_NDK"] = ndk_root
     SDK["ANDROID_NDK"] = ndk_root
 
 
     # Determine the toolchain location.
     # Determine the toolchain location.
     prebuilt_dir = os.path.join(ndk_root, 'toolchains', 'llvm', 'prebuilt')
     prebuilt_dir = os.path.join(ndk_root, 'toolchains', 'llvm', 'prebuilt')
     if not os.path.isdir(prebuilt_dir):
     if not os.path.isdir(prebuilt_dir):
-        exit('Not found: %s' % (prebuilt_dir))
+        exit('Not found: %s (is the Android NDK installed?)' % (prebuilt_dir))
 
 
     host_tag = GetHost() + '-x86'
     host_tag = GetHost() + '-x86'
     if host_64:
     if host_64:
@@ -2527,7 +2575,44 @@ def SdkLocateAndroid():
     # STL that ships with Android.
     # STL that ships with Android.
     support = os.path.join(ndk_root, 'sources', 'android', 'support', 'include')
     support = os.path.join(ndk_root, 'sources', 'android', 'support', 'include')
     IncDirectory("ALWAYS", support.replace('\\', '/'))
     IncDirectory("ALWAYS", support.replace('\\', '/'))
-    LibName("ALWAYS", "-landroid_support")
+    if api < 21:
+        LibName("ALWAYS", "-landroid_support")
+
+    # Determine the location of android.jar.
+    SDK["ANDROID_JAR"] = os.path.join(sdk_root, 'platforms', 'android-%s' % (api), 'android.jar')
+    if not os.path.isfile(SDK["ANDROID_JAR"]):
+        exit("Cannot find %s.  Install platform API level %s via the SDK manager or change the targeted API level with --target=android-#" % (SDK["ANDROID_JAR"], api))
+
+    # Which build tools versions do we have?  Pick the latest.
+    versions = []
+    for version in os.listdir(os.path.join(sdk_root, "build-tools")):
+        match = re.match('([0-9]+)\\.([0-9]+)\\.([0-9]+)', version)
+        if match:
+            version_tuple = int(match.group(1)), int(match.group(2)), int(match.group(3))
+            versions.append(version_tuple)
+
+    versions.sort()
+    if versions:
+        version = versions[-1]
+        SDK["ANDROID_BUILD_TOOLS"] = os.path.join(sdk_root, "build-tools", "{0}.{1}.{2}".format(*version))
+
+    # And find the location of the Java compiler.
+    if GetHost() == "windows":
+        jdk_home = os.environ.get("JDK_HOME") or os.environ.get("JAVA_HOME")
+        if not jdk_home:
+            # Try to use the Java shipped with Android Studio.
+            studio_path = GetRegistryKey("SOFTWARE\\Android Studio", "Path", override64=False)
+            if studio_path and os.path.isdir(studio_path):
+                jdk_home = os.path.join(studio_path, "jre")
+
+        if not jdk_home or not os.path.isdir(jdk_home):
+            exit("Cannot find JDK.  Please set JDK_HOME or JAVA_HOME.")
+
+        javac = os.path.join(jdk_home, "bin", "javac.exe")
+        if not os.path.isfile(javac):
+            exit("Cannot find %s.  Install the JDK and set JDK_HOME or JAVA_HOME." % (javac))
+
+        SDK["JDK"] = jdk_home
 
 
 ########################################################################
 ########################################################################
 ##
 ##
@@ -2793,6 +2878,13 @@ def SetupBuildEnvironment(compiler):
     if GetTarget() == 'android' and GetHost() != 'android':
     if GetTarget() == 'android' and GetHost() != 'android':
         AddToPathEnv("PATH", os.path.join(SDK["ANDROID_TOOLCHAIN"], "bin"))
         AddToPathEnv("PATH", os.path.join(SDK["ANDROID_TOOLCHAIN"], "bin"))
 
 
+        if "ANDROID_BUILD_TOOLS" in SDK:
+            AddToPathEnv("PATH", SDK["ANDROID_BUILD_TOOLS"])
+
+        if "JDK" in SDK:
+            AddToPathEnv("PATH", os.path.join(SDK["JDK"], "bin"))
+            os.environ["JAVA_HOME"] = SDK["JDK"]
+
     if compiler == "MSVC":
     if compiler == "MSVC":
         # Add the visual studio tools to PATH et al.
         # Add the visual studio tools to PATH et al.
         SetupVisualStudioEnviron()
         SetupVisualStudioEnviron()
@@ -2844,8 +2936,22 @@ def SetupBuildEnvironment(compiler):
             Warn("%s failed" % (cmd))
             Warn("%s failed" % (cmd))
             SYS_LIB_DIRS += [SDK.get("SYSROOT", "") + "/usr/lib"]
             SYS_LIB_DIRS += [SDK.get("SYSROOT", "") + "/usr/lib"]
 
 
+        # The Android toolchain on Windows doesn't actually add this one.
+        if target == 'android' and GetHost() == 'windows':
+            libdir = SDK.get("SYSROOT", "") + "/usr/lib"
+            if GetTargetArch() == 'x86_64':
+                libdir += '64'
+            SYS_LIB_DIRS += [libdir]
+
         # Now extract the preprocessor's include directories.
         # Now extract the preprocessor's include directories.
-        cmd = GetCXX() + sysroot_flag + " -x c++ -v -E /dev/null"
+        cmd = GetCXX() + " -x c++ -v -E " + os.devnull
+        if "ANDROID_NDK" in SDK:
+            ndk_dir = SDK["ANDROID_NDK"].replace('\\', '/')
+            cmd += ' -isystem %s/sysroot/usr/include' % (ndk_dir)
+            cmd += ' -isystem %s/sysroot/usr/include/%s' % (ndk_dir, SDK["ANDROID_TRIPLE"])
+        else:
+            cmd += sysroot_flag
+
         null = open(os.devnull, 'w')
         null = open(os.devnull, 'w')
         handle = subprocess.Popen(cmd, stdout=null, stderr=subprocess.PIPE, shell=True)
         handle = subprocess.Popen(cmd, stdout=null, stderr=subprocess.PIPE, shell=True)
         scanning = False
         scanning = False
@@ -2858,8 +2964,12 @@ def SetupBuildEnvironment(compiler):
                     scanning = True
                     scanning = True
                 continue
                 continue
 
 
-            if not line.startswith(' /'):
-                continue
+            if sys.platform == "win32":
+                if not line.startswith(' '):
+                    continue
+            else:
+                if not line.startswith(' /'):
+                    continue
 
 
             line = line.strip()
             line = line.strip()
             if line.endswith(" (framework directory)"):
             if line.endswith(" (framework directory)"):

+ 29 - 6
makepanda/makewheel.py

@@ -104,7 +104,7 @@ MANYLINUX_LIBS = [
 
 
     # These are not mentioned in manylinux1 spec but should nonetheless always
     # These are not mentioned in manylinux1 spec but should nonetheless always
     # be excluded.
     # be excluded.
-    "linux-vdso.so.1", "linux-gate.so.1", "ld-linux.so.2",
+    "linux-vdso.so.1", "linux-gate.so.1", "ld-linux.so.2", "libdrm.so.2",
 ]
 ]
 
 
 # Binaries to never scan for dependencies on non-Windows systems.
 # Binaries to never scan for dependencies on non-Windows systems.
@@ -144,6 +144,32 @@ METADATA = {
     "classifiers": GetMetadataValue('classifiers'),
     "classifiers": GetMetadataValue('classifiers'),
 }
 }
 
 
+DESCRIPTION = """
+The Panda3D free 3D game engine
+===============================
+
+Panda3D is a powerful 3D engine written in C++, with a complete set of Python
+bindings. Unlike other engines, these bindings are automatically generated,
+meaning that they are always up-to-date and complete: all functions of the
+engine can be controlled from Python. All major Panda3D applications have been
+written in Python, this is the intended way of using the engine.
+
+Panda3D now supports automatic shader generation, which now means you can use
+normal maps, gloss maps, glow maps, HDR, cartoon shading, and the like without
+having to write any shaders.
+
+Panda3D is a modern engine supporting advanced features such as shaders,
+stencil, and render-to-texture. Panda3D is unusual in that it emphasizes a
+short learning curve, rapid development, and extreme stability and robustness.
+Panda3D is free software that runs under Windows, Linux, or macOS.
+
+The Panda3D team is very concerned with making the engine accessible to new
+users. We provide a detailed manual, a complete API reference, and a large
+collection of sample programs to help you get started. We have active forums,
+with many helpful users, and the developers are regularly online to answer
+questions.
+"""
+
 PANDA3D_TOOLS_INIT = """import os, sys
 PANDA3D_TOOLS_INIT = """import os, sys
 import panda3d
 import panda3d
 
 
@@ -526,11 +552,6 @@ def makewheel(version, output_dir, platform=None):
 
 
     # Update relevant METADATA entries
     # Update relevant METADATA entries
     METADATA['version'] = version
     METADATA['version'] = version
-    version_classifiers = [
-        "Programming Language :: Python :: {0}".format(*sys.version_info),
-        "Programming Language :: Python :: {0}.{1}".format(*sys.version_info),
-    ]
-    METADATA['classifiers'].extend(version_classifiers)
 
 
     # Build out the metadata
     # Build out the metadata
     details = METADATA["extensions"]["python.details"]
     details = METADATA["extensions"]["python.details"]
@@ -549,6 +570,8 @@ def makewheel(version, output_dir, platform=None):
         "Platform: {0}\n".format(platform),
         "Platform: {0}\n".format(platform),
     ] + ["Classifier: {0}\n".format(c) for c in METADATA['classifiers']])
     ] + ["Classifier: {0}\n".format(c) for c in METADATA['classifiers']])
 
 
+    metadata += '\n' + DESCRIPTION.strip() + '\n'
+
     # Zip it up and name it the right thing
     # Zip it up and name it the right thing
     whl = WheelFile('panda3d', version, platform)
     whl = WheelFile('panda3d', version, platform)
     whl.lib_path = [libs_dir]
     whl.lib_path = [libs_dir]

+ 0 - 1
makepanda/test_imports.py

@@ -200,7 +200,6 @@ import direct.showbase.EventManager
 import direct.showbase.EventManagerGlobal
 import direct.showbase.EventManagerGlobal
 import direct.showbase.ExceptionVarDump
 import direct.showbase.ExceptionVarDump
 import direct.showbase.Factory
 import direct.showbase.Factory
-import direct.showbase.FindCtaPaths
 import direct.showbase.Finder
 import direct.showbase.Finder
 import direct.showbase.GarbageReport
 import direct.showbase.GarbageReport
 import direct.showbase.GarbageReportScheduler
 import direct.showbase.GarbageReportScheduler

+ 2 - 9
panda/metalibs/pandagl/pandagl.cxx

@@ -16,9 +16,6 @@
 #if defined(HAVE_COCOA)
 #if defined(HAVE_COCOA)
 #include "config_cocoadisplay.h"
 #include "config_cocoadisplay.h"
 #include "cocoaGraphicsPipe.h"
 #include "cocoaGraphicsPipe.h"
-#elif defined(HAVE_CARBON)
-#include "config_osxdisplay.h"
-#include "osxGraphicsPipe.h"
 #endif
 #endif
 
 
 #ifdef HAVE_GLX
 #ifdef HAVE_GLX
@@ -26,8 +23,8 @@
 #include "glxGraphicsPipe.h"
 #include "glxGraphicsPipe.h"
 #endif
 #endif
 
 
-#if !defined(HAVE_WGL) && !defined(HAVE_COCOA) && !defined(HAVE_CARBON) && !defined(HAVE_GLX)
-#error One of HAVE_WGL, HAVE_COCOA, HAVE_CARBON or HAVE_GLX must be defined when compiling pandagl!
+#if !defined(HAVE_WGL) && !defined(HAVE_COCOA) && !defined(HAVE_GLX)
+#error One of HAVE_WGL, HAVE_COCOA or HAVE_GLX must be defined when compiling pandagl!
 #endif
 #endif
 
 
 /**
 /**
@@ -46,8 +43,6 @@ init_libpandagl() {
 
 
 #if defined(HAVE_COCOA)
 #if defined(HAVE_COCOA)
   init_libcocoadisplay();
   init_libcocoadisplay();
-#elif defined(HAVE_CARBON)
-  init_libosxdisplay();
 #endif
 #endif
 
 
 #ifdef IS_LINUX
 #ifdef IS_LINUX
@@ -67,8 +62,6 @@ get_pipe_type_pandagl() {
 
 
 #if defined(HAVE_COCOA)
 #if defined(HAVE_COCOA)
   return CocoaGraphicsPipe::get_class_type().get_index();
   return CocoaGraphicsPipe::get_class_type().get_index();
-#elif defined(HAVE_CARBON)
-  return osxGraphicsPipe::get_class_type().get_index();
 #endif
 #endif
 
 
 #ifdef HAVE_GLX
 #ifdef HAVE_GLX

+ 1 - 0
panda/src/android/android_main.cxx

@@ -24,6 +24,7 @@
 #include "config_display.h"
 #include "config_display.h"
 // #define OPENGLES_1 #include "config_androiddisplay.h"
 // #define OPENGLES_1 #include "config_androiddisplay.h"
 
 
+#include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 
 

+ 1 - 1
panda/src/bullet/bulletRigidBodyNode.cxx

@@ -120,7 +120,7 @@ do_set_mass(PN_stdfloat mass) {
   btScalar bt_mass = mass;
   btScalar bt_mass = mass;
   btVector3 bt_inertia(0.0, 0.0, 0.0);
   btVector3 bt_inertia(0.0, 0.0, 0.0);
 
 
-  if (bt_mass > 0.0) {
+  if (bt_mass > 0.0 && !_shapes.empty()) {
     _rigid->getCollisionShape()->calculateLocalInertia(bt_mass, bt_inertia);
     _rigid->getCollisionShape()->calculateLocalInertia(bt_mass, bt_inertia);
   }
   }
 
 

+ 2 - 2
panda/src/bullet/bulletVehicle.cxx

@@ -171,14 +171,14 @@ set_pitch_control(PN_stdfloat pitch) {
  * Factory method for creating wheels for this vehicle instance.
  * Factory method for creating wheels for this vehicle instance.
  */
  */
 BulletWheel BulletVehicle::
 BulletWheel BulletVehicle::
-create_wheel() {
+create_wheel(PN_stdfloat suspension_rest_length) {
   LightMutexHolder holder(BulletWorld::get_global_lock());
   LightMutexHolder holder(BulletWorld::get_global_lock());
 
 
   btVector3 pos(0.0, 0.0, 0.0);
   btVector3 pos(0.0, 0.0, 0.0);
   btVector3 direction = get_axis(_vehicle->getUpAxis());
   btVector3 direction = get_axis(_vehicle->getUpAxis());
   btVector3 axle = get_axis(_vehicle->getRightAxis());
   btVector3 axle = get_axis(_vehicle->getRightAxis());
 
 
-  btScalar suspension(0.4);
+  btScalar suspension(suspension_rest_length);
   btScalar radius(0.3);
   btScalar radius(0.3);
 
 
   btWheelInfo &info = _vehicle->addWheel(pos, direction, axle, suspension, radius, _tuning._, false);
   btWheelInfo &info = _vehicle->addWheel(pos, direction, axle, suspension, radius, _tuning._, false);

+ 1 - 1
panda/src/bullet/bulletVehicle.h

@@ -85,7 +85,7 @@ PUBLISHED:
   void apply_engine_force(PN_stdfloat force, int idx);
   void apply_engine_force(PN_stdfloat force, int idx);
 
 
   // Wheels
   // Wheels
-  BulletWheel create_wheel();
+  BulletWheel create_wheel(PN_stdfloat suspension_rest_length=0.4);
 
 
   int get_num_wheels() const;
   int get_num_wheels() const;
   BulletWheel get_wheel(int idx) const;
   BulletWheel get_wheel(int idx) const;

+ 1 - 1
panda/src/bullet/bulletWorld.h

@@ -43,7 +43,7 @@ class BulletPersistentManifold;
 class BulletShape;
 class BulletShape;
 class BulletSoftBodyWorldInfo;
 class BulletSoftBodyWorldInfo;
 
 
-extern PT(CallbackObject) bullet_contact_added_callback;
+extern EXPCL_PANDABULLET PT(CallbackObject) bullet_contact_added_callback;
 
 
 /**
 /**
  *
  *

+ 2 - 2
panda/src/chan/animControl.h

@@ -24,7 +24,7 @@
 #include "typedReferenceCount.h"
 #include "typedReferenceCount.h"
 #include "namable.h"
 #include "namable.h"
 #include "pmutex.h"
 #include "pmutex.h"
-#include "conditionVarFull.h"
+#include "conditionVar.h"
 
 
 class PartBundle;
 class PartBundle;
 class AnimChannelBase;
 class AnimChannelBase;
@@ -79,7 +79,7 @@ private:
   bool _pending;
   bool _pending;
   std::string _pending_done_event;
   std::string _pending_done_event;
   Mutex _pending_lock;  // protects the above two.
   Mutex _pending_lock;  // protects the above two.
-  ConditionVarFull _pending_cvar; // signals when _pending goes true.
+  ConditionVar _pending_cvar; // signals when _pending goes true.
 
 
   // This is a PT(PartGroup) instead of a PT(PartBundle), just because we
   // This is a PT(PartGroup) instead of a PT(PartBundle), just because we
   // can't include partBundle.h for circular reasons.  But it actually keeps a
   // can't include partBundle.h for circular reasons.  But it actually keeps a

+ 10 - 1
panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm

@@ -248,8 +248,17 @@ choose_pixel_format(const FrameBufferProperties &properties,
   attribs.push_back(aux_buffers);
   attribs.push_back(aux_buffers);
   attribs.push_back(NSOpenGLPFAColorSize);
   attribs.push_back(NSOpenGLPFAColorSize);
   attribs.push_back(properties.get_color_bits());
   attribs.push_back(properties.get_color_bits());
+
+  // Set the depth buffer bits to 24 manually when 1 is requested.
+  // This prevents getting a depth buffer of only 16 bits when requesting 1.
   attribs.push_back(NSOpenGLPFADepthSize);
   attribs.push_back(NSOpenGLPFADepthSize);
-  attribs.push_back(properties.get_depth_bits());
+  if (properties.get_depth_bits() == 1) {
+    attribs.push_back(24);
+  }
+  else {
+    attribs.push_back(properties.get_depth_bits());
+  }
+
   attribs.push_back(NSOpenGLPFAStencilSize);
   attribs.push_back(NSOpenGLPFAStencilSize);
   attribs.push_back(properties.get_stencil_bits());
   attribs.push_back(properties.get_stencil_bits());
 
 

+ 4 - 1
panda/src/cocoadisplay/cocoaGraphicsWindow.mm

@@ -711,8 +711,11 @@ close_window() {
   }
   }
 
 
   if (_window != nil) {
   if (_window != nil) {
-    [_window setReleasedWhenClosed: YES];
     [_window close];
     [_window close];
+    
+    // Process events once more so any pending NSEvents are cleared. Not doing
+    // this causes the window to stick around after calling [_window close].
+    process_events();
     _window = nil;
     _window = nil;
   }
   }
 
 

+ 0 - 1
panda/src/cocoadisplay/cocoaPandaWindowDelegate.h

@@ -34,7 +34,6 @@ class CocoaGraphicsWindow;
 - (void)windowDidBecomeKey:(NSNotification *)notification;
 - (void)windowDidBecomeKey:(NSNotification *)notification;
 - (void)windowDidResignKey:(NSNotification *)notification;
 - (void)windowDidResignKey:(NSNotification *)notification;
 - (BOOL)windowShouldClose:(id)sender;
 - (BOOL)windowShouldClose:(id)sender;
-- (void)windowWillClose:(NSNotification *)notification;
 
 
 // TODO: handle fullscreen on Lion.
 // TODO: handle fullscreen on Lion.
 
 

+ 5 - 5
panda/src/cocoadisplay/cocoaPandaWindowDelegate.mm

@@ -50,11 +50,11 @@
 }
 }
 
 
 - (BOOL) windowShouldClose:(id)sender {
 - (BOOL) windowShouldClose:(id)sender {
-  return _graphicsWindow->handle_close_request();
-}
-
-- (void) windowWillClose:(NSNotification *)notification {
-  _graphicsWindow->handle_close_event();
+  bool should_close = _graphicsWindow->handle_close_request();
+  if (should_close) {
+    _graphicsWindow->handle_close_event();
+  }
+  return should_close;
 }
 }
 
 
 @end
 @end

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

@@ -139,7 +139,6 @@ public:
   INLINE void calc_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const;
   INLINE void calc_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const;
   INLINE void rederive_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const;
   INLINE void rederive_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const;
   INLINE static LPoint3 to_3d(const LVecBase2 &point2d, const LMatrix4 &to_3d_mat);
   INLINE static LPoint3 to_3d(const LVecBase2 &point2d, const LMatrix4 &to_3d_mat);
-  LPoint3 legacy_to_3d(const LVecBase2 &point2d, int axis) const;
   bool clip_polygon(Points &new_points, const Points &source_points,
   bool clip_polygon(Points &new_points, const Points &source_points,
                     const LPlane &plane,int plane_no) const;
                     const LPlane &plane,int plane_no) const;
   bool apply_clip_plane(Points &new_points, const ClipPlaneAttrib *cpa,
   bool apply_clip_plane(Points &new_points, const ClipPlaneAttrib *cpa,

+ 1 - 0
panda/src/collide/collisionCapsule.h

@@ -154,6 +154,7 @@ private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 
 
   friend class CollisionBox;
   friend class CollisionBox;
+  friend class CollisionPolygon;
 };
 };
 
 
 #include "collisionCapsule.I"
 #include "collisionCapsule.I"

+ 115 - 0
panda/src/collide/collisionInvSphere.cxx

@@ -12,7 +12,9 @@
  */
  */
 
 
 #include "collisionInvSphere.h"
 #include "collisionInvSphere.h"
+
 #include "collisionSphere.h"
 #include "collisionSphere.h"
+#include "collisionCapsule.h"
 #include "collisionLine.h"
 #include "collisionLine.h"
 #include "collisionRay.h"
 #include "collisionRay.h"
 #include "collisionSegment.h"
 #include "collisionSegment.h"
@@ -288,6 +290,119 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
   return new_entry;
   return new_entry;
 }
 }
 
 
+/**
+ *
+ */
+PT(CollisionEntry) CollisionInvSphere::
+test_intersection_from_capsule(const CollisionEntry &entry) const {
+  const CollisionCapsule *capsule;
+  DCAST_INTO_R(capsule, entry.get_from(), nullptr);
+
+  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
+
+  LPoint3 from_a = capsule->get_point_a() * wrt_mat;
+  LPoint3 from_b = capsule->get_point_b() * wrt_mat;
+
+  LVector3 from_radius_v =
+    LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat;
+  PN_stdfloat from_radius = from_radius_v.length();
+
+  LPoint3 center = get_center();
+  PN_stdfloat radius = get_radius();
+
+  // Check which one of the points lies furthest inside the sphere.
+  PN_stdfloat dist_a = (from_a - center).length();
+  PN_stdfloat dist_b = (from_b - center).length();
+  if (dist_b > dist_a) {
+    // Store the furthest point into from_a/dist_a.
+    dist_a = dist_b;
+    from_a = from_b;
+  }
+
+  // from_a now contains the furthest point.  Is it inside?
+  if (dist_a < radius - from_radius) {
+    return nullptr;
+  }
+
+  if (collide_cat.is_debug()) {
+    collide_cat.debug()
+      << "intersection detected from " << entry.get_from_node_path()
+      << " into " << entry.get_into_node_path() << "\n";
+  }
+  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
+
+  LVector3 normal = center - from_a;
+  normal.normalize();
+  new_entry->set_surface_point(get_center() - normal * radius);
+  new_entry->set_interior_point(from_a - normal * from_radius);
+
+  if (has_effective_normal() && capsule->get_respect_effective_normal()) {
+    new_entry->set_surface_normal(get_effective_normal());
+  } else {
+    new_entry->set_surface_normal(normal);
+  }
+
+  return new_entry;
+}
+
+/**
+ * Double dispatch point for box as a FROM object
+ */
+PT(CollisionEntry) CollisionInvSphere::
+test_intersection_from_box(const CollisionEntry &entry) const {
+  const CollisionBox *box;
+  DCAST_INTO_R(box, entry.get_from(), nullptr);
+
+  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
+
+  LPoint3 center = get_center();
+  PN_stdfloat radius_sq = get_radius();
+  radius_sq *= radius_sq;
+
+  // Just figure out which box point is furthest from the center.  If it
+  // exceeds the radius, the furthest point wins.
+
+  PN_stdfloat max_dist_sq = -1.0;
+  LPoint3 deepest_vertex;
+
+  for (int i = 0; i < 8; ++i) {
+    LPoint3 point = wrt_mat.xform_point(box->get_point(i));
+
+    PN_stdfloat dist_sq = (point - center).length_squared();
+    if (dist_sq > max_dist_sq) {
+      deepest_vertex = point;
+      max_dist_sq = dist_sq;
+    }
+  }
+
+  if (max_dist_sq < radius_sq) {
+    // The point furthest away from the center is still inside the sphere.
+    // Therefore, no collision.
+    return nullptr;
+  }
+
+  if (collide_cat.is_debug()) {
+    collide_cat.debug()
+      << "intersection detected from " << entry.get_from_node_path()
+      << " into " << entry.get_into_node_path() << "\n";
+  }
+
+  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
+
+  // The interior point is just the deepest cube vertex.
+  new_entry->set_interior_point(deepest_vertex);
+
+  // Now extrapolate the surface point and normal from that.
+  LVector3 normal = center - deepest_vertex;
+  normal.normalize();
+  new_entry->set_surface_point(center - normal * get_radius());
+  new_entry->set_surface_normal(
+    (has_effective_normal() && box->get_respect_effective_normal())
+    ? get_effective_normal() : normal);
+
+  return new_entry;
+}
+
 /**
 /**
  * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
  * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
  * solid.
  * solid.

+ 4 - 0
panda/src/collide/collisionInvSphere.h

@@ -55,6 +55,10 @@ protected:
   test_intersection_from_ray(const CollisionEntry &entry) const;
   test_intersection_from_ray(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_segment(const CollisionEntry &entry) const;
   test_intersection_from_segment(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_capsule(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_box(const CollisionEntry &entry) const;
 
 
   virtual void fill_viz_geom();
   virtual void fill_viz_geom();
 
 

+ 1 - 0
panda/src/collide/collisionNode.cxx

@@ -58,6 +58,7 @@ CollisionNode::
 CollisionNode(const CollisionNode &copy) :
 CollisionNode(const CollisionNode &copy) :
   PandaNode(copy),
   PandaNode(copy),
   _from_collide_mask(copy._from_collide_mask),
   _from_collide_mask(copy._from_collide_mask),
+  _collider_sort(copy._collider_sort),
   _solids(copy._solids)
   _solids(copy._solids)
 {
 {
 }
 }

+ 138 - 31
panda/src/collide/collisionPolygon.cxx

@@ -12,9 +12,11 @@
  */
  */
 
 
 #include "collisionPolygon.h"
 #include "collisionPolygon.h"
+
 #include "collisionHandler.h"
 #include "collisionHandler.h"
 #include "collisionEntry.h"
 #include "collisionEntry.h"
 #include "collisionSphere.h"
 #include "collisionSphere.h"
+#include "collisionCapsule.h"
 #include "collisionLine.h"
 #include "collisionLine.h"
 #include "collisionRay.h"
 #include "collisionRay.h"
 #include "collisionSegment.h"
 #include "collisionSegment.h"
@@ -842,6 +844,142 @@ test_intersection_from_parabola(const CollisionEntry &entry) const {
   return new_entry;
   return new_entry;
 }
 }
 
 
+/**
+ * This is part of the double-dispatch implementation of test_intersection().
+ * It is called when the "from" object is a capsule.
+ */
+PT(CollisionEntry) CollisionPolygon::
+test_intersection_from_capsule(const CollisionEntry &entry) const {
+  if (_points.size() < 3) {
+    return nullptr;
+  }
+
+  const CollisionCapsule *capsule;
+  DCAST_INTO_R(capsule, entry.get_from(), nullptr);
+
+  const LMatrix4 &wrt_mat = entry.get_wrt_mat();
+  LMatrix4 plane_mat = wrt_mat * _to_2d_mat;
+
+  LPoint3 from_a = capsule->get_point_a() * plane_mat;
+  LPoint3 from_b = capsule->get_point_b() * plane_mat;
+
+  LVector3 from_radius_v =
+    LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat;
+  PN_stdfloat from_radius_sq = from_radius_v.length_squared();
+
+  // Check if the capsule is colliding with the plane at all.
+  // Are the points on the same side of the plane?
+  if ((from_a[1] > 0) == (from_b[1] > 0)) {
+    // Yes, so calculate the distance of the closest point.
+    PN_stdfloat dist = min(cabs(from_a[1]), cabs(from_b[1]));
+    if (dist * dist > from_radius_sq) {
+      return nullptr;
+    }
+  }
+
+  // Order from_a and from_b so that from_a has the deepest point.
+  bool swapped = (from_a[1] < from_b[1]);
+  if (swapped) {
+    std::swap(from_a, from_b);
+  }
+
+  LPoint3 surface_point, interior_point;
+
+  // Is the projection of from_a onto the plane inside the polygon?
+  LPoint2 from_a_proj(from_a[0], from_a[2]);
+  if (point_is_inside(from_a_proj, _points)) {
+    // Yes, and we already checked the vertical separation earlier on, so we
+    // know that the capsule is touching the polygon near from_a.
+    LPoint3 deepest = (swapped ? capsule->get_point_b() : capsule->get_point_a()) * wrt_mat;
+    PN_stdfloat from_radius = csqrt(from_radius_sq);
+    surface_point = get_plane().project(deepest);
+    interior_point = deepest - get_normal() * from_radius;
+  }
+  else {
+    LVector3 from_direction = from_b - from_a;
+
+    // Find the point in the capsule's inner segment with the closest distance
+    // to the polygon's edges.  We effectively test a sphere around that point.
+    PN_stdfloat min_dist_sq = make_inf((PN_stdfloat)0);
+    LPoint3 poly_point;
+    LPoint3 line_point;
+
+    LPoint2 last_point = _points.back()._p;
+    for (const PointDef &pd : _points) {
+      LVector2 dir = last_point - pd._p;
+      last_point = pd._p;
+
+      double t1, t2;
+      CollisionCapsule::calc_closest_segment_points(t1, t2,
+          LPoint3(pd._p[0], 0, pd._p[1]), LVector3(dir[0], 0, dir[1]),
+          from_a, from_direction);
+
+      LPoint3 point1(pd._p[0] + dir[0] * t1, 0, pd._p[1] + dir[1] * t1);
+      LPoint3 point2 = from_a + from_direction * t2;
+      PN_stdfloat dist_sq = (point2 - point1).length_squared();
+      if (dist_sq < min_dist_sq) {
+        min_dist_sq = dist_sq;
+        poly_point = point1;
+        line_point = point2;
+      }
+    }
+
+    // Project the closest point on the segment onto the polygon.  Is this point
+    // inside the polygon?
+    LPoint2 line_point_proj(line_point[0], line_point[2]);
+    if (point_is_inside(line_point_proj, _points)) {
+      // Yes, and we already checked the vertical separation earlier on, so we
+      // know that the capsule is touching the polygon here.
+      LMatrix4 to_3d_mat;
+      rederive_to_3d_mat(to_3d_mat);
+
+      surface_point = to_3d(line_point_proj, to_3d_mat);
+
+      LPoint3 interior;
+      if (IS_NEARLY_EQUAL(from_a[1], from_b[1])) {
+        // It's parallel to the polygon; we can use any point on the segment we
+        // want, so we might as well use the point we determined to be closest.
+        interior = line_point;
+      } else {
+        // Use the deepest point.  FIXME: we need something better.  This
+        // pushes the capsule out way too much.
+        interior = from_a;
+      }
+      interior[1] += csqrt(from_radius_sq);
+      interior_point = interior * to_3d_mat;
+    }
+    else if (min_dist_sq < from_radius_sq) {
+      // No, but it is colliding with an edge.
+      LMatrix4 to_3d_mat;
+      rederive_to_3d_mat(to_3d_mat);
+
+      surface_point = poly_point * to_3d_mat;
+
+      // Make sure we calculate an interior point that lies below the polygon.
+      LVector3 dir = line_point * to_3d_mat - surface_point;
+      dir.normalize();
+      interior_point = surface_point - dir * (csqrt(from_radius_sq) - csqrt(min_dist_sq));
+    }
+    else {
+      // It is outside the polygon altogether.
+      return nullptr;
+    }
+  }
+
+  if (collide_cat.is_debug()) {
+    collide_cat.debug()
+      << "intersection detected from " << entry.get_from_node_path()
+      << " into " << entry.get_into_node_path() << "\n";
+  }
+  PT(CollisionEntry) new_entry = new CollisionEntry(entry);
+  LVector3 normal = (has_effective_normal() && capsule->get_respect_effective_normal()) ? get_effective_normal() : get_normal();
+  new_entry->set_surface_normal(normal);
+  new_entry->set_surface_point(surface_point);
+  new_entry->set_interior_point(interior_point);
+
+  return new_entry;
+}
+
 /**
 /**
  * This is part of the double-dispatch implementation of test_intersection().
  * This is part of the double-dispatch implementation of test_intersection().
  * It is called when the "from" object is a box.
  * It is called when the "from" object is a box.
@@ -1335,37 +1473,6 @@ setup_points(const LPoint3 *begin, const LPoint3 *end) {
   compute_vectors(_points);
   compute_vectors(_points);
 }
 }
 
 
-/**
- * Converts the indicated point to 3-d space according to the way
- * CollisionPolygons used to be stored in bam files prior to 4.9.
- */
-LPoint3 CollisionPolygon::
-legacy_to_3d(const LVecBase2 &point2d, int axis) const {
-  nassertr(!point2d.is_nan(), LPoint3(0.0f, 0.0f, 0.0f));
-
-  LVector3 normal = get_normal();
-  PN_stdfloat D = get_plane()[3];
-
-  nassertr(!normal.is_nan(), LPoint3(0.0f, 0.0f, 0.0f));
-  nassertr(!cnan(D), LPoint3(0.0f, 0.0f, 0.0f));
-
-  switch (axis) {
-  case 0:  // AT_x:
-    return LPoint3(-(normal[1]*point2d[0] + normal[2]*point2d[1] + D)/normal[0],                    point2d[0], point2d[1]);
-
-  case 1:  // AT_y:
-    return LPoint3(point2d[0],
-                    -(normal[0]*point2d[0] + normal[2]*point2d[1] + D)/normal[1],                    point2d[1]);
-
-  case 2:  // AT_z:
-    return LPoint3(point2d[0], point2d[1],
-                    -(normal[0]*point2d[0] + normal[1]*point2d[1] + D)/normal[2]);
-  }
-
-  nassertr(false, LPoint3(0.0f, 0.0f, 0.0f));
-  return LPoint3(0.0f, 0.0f, 0.0f);
-}
-
 /**
 /**
  * Clips the source_points of the polygon by the indicated clipping plane, and
  * Clips the source_points of the polygon by the indicated clipping plane, and
  * modifies new_points to reflect the new set of clipped points (but does not
  * modifies new_points to reflect the new set of clipped points (but does not

+ 2 - 1
panda/src/collide/collisionPolygon.h

@@ -93,6 +93,8 @@ protected:
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_parabola(const CollisionEntry &entry) const;
   test_intersection_from_parabola(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
+  test_intersection_from_capsule(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
   test_intersection_from_box(const CollisionEntry &entry) const;
   test_intersection_from_box(const CollisionEntry &entry) const;
 
 
   virtual void fill_viz_geom();
   virtual void fill_viz_geom();
@@ -131,7 +133,6 @@ private:
   INLINE void calc_to_3d_mat(LMatrix4 &to_3d_mat) const;
   INLINE void calc_to_3d_mat(LMatrix4 &to_3d_mat) const;
   INLINE void rederive_to_3d_mat(LMatrix4 &to_3d_mat) const;
   INLINE void rederive_to_3d_mat(LMatrix4 &to_3d_mat) const;
   INLINE static LPoint3 to_3d(const LVecBase2 &point2d, const LMatrix4 &to_3d_mat);
   INLINE static LPoint3 to_3d(const LVecBase2 &point2d, const LMatrix4 &to_3d_mat);
-  LPoint3 legacy_to_3d(const LVecBase2 &point2d, int axis) const;
 
 
   bool clip_polygon(Points &new_points, const Points &source_points,
   bool clip_polygon(Points &new_points, const Points &source_points,
                     const LPlane &plane) const;
                     const LPlane &plane) const;

+ 3 - 1
panda/src/device/evdevInputDevice.cxx

@@ -82,8 +82,10 @@ static const struct DeviceMapping {
   {0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller},
   {0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller},
   // Jess Tech Colour Rumble Pad
   // Jess Tech Colour Rumble Pad
   {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0},
   {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0},
-  // Trust GXT 24
+  // SPEED Link SL-6535-SBK-01
   {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, 0},
   {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, 0},
+  // 8bitdo N30 Pro Controller
+  {0x2dc8, 0x9001, InputDevice::DeviceClass::gamepad, QB_rstick_from_z},
   // 3Dconnexion Space Traveller 3D Mouse
   // 3Dconnexion Space Traveller 3D Mouse
   {0x046d, 0xc623, InputDevice::DeviceClass::spatial_mouse, 0},
   {0x046d, 0xc623, InputDevice::DeviceClass::spatial_mouse, 0},
   // 3Dconnexion Space Pilot 3D Mouse
   // 3Dconnexion Space Pilot 3D Mouse

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