Преглед на файлове

Merge branch 'master' into gsoc/2020

# Conflicts:
#	makepanda/makepanda.py
Maxwell Dreytser преди 3 години
родител
ревизия
560e57f84e
променени са 100 файла, в които са добавени 1296 реда и са изтрити 1991 реда
  1. 90 72
      .github/workflows/ci.yml
  2. 1 0
      .gitignore
  3. 534 0
      .pylintrc
  4. 0 60
      .travis.yml
  5. 8 4
      BACKERS.md
  6. 45 20
      CMakeLists.txt
  7. 1 1
      CONTRIBUTING.md
  8. 12 19
      README.md
  9. 4 0
      cmake/macros/AddFlexTarget.cmake
  10. 15 5
      cmake/macros/PackageConfig.cmake
  11. 24 0
      cmake/modules/FindFFMPEG.cmake
  12. 12 0
      cmake/modules/FindGTK3.cmake
  13. 1 1
      contrib/src/ai/aiBehaviors.h
  14. 4 0
      contrib/src/ai/aiCharacter.cxx
  15. 4 0
      contrib/src/ai/aiCharacter.h
  16. 7 0
      contrib/src/contribbase/contribsymbols.h
  17. 10 2
      contrib/src/rplight/pssmCameraRig.cxx
  18. 21 21
      contrib/src/rplight/rpLight.I
  19. 3 8
      contrib/src/sceneeditor/SideWindow.py
  20. 0 1
      contrib/src/sceneeditor/collisionWindow.py
  21. 3 8
      contrib/src/sceneeditor/controllerWindow.py
  22. 1 7
      contrib/src/sceneeditor/dataHolder.py
  23. 3 7
      contrib/src/sceneeditor/lightingPanel.py
  24. 1 1
      contrib/src/sceneeditor/quad.py
  25. 2 12
      contrib/src/sceneeditor/sceneEditor.py
  26. 2 8
      contrib/src/sceneeditor/seAnimPanel.py
  27. 2 9
      contrib/src/sceneeditor/seBlendAnimPanel.py
  28. 1 0
      contrib/src/sceneeditor/seCameraControl.py
  29. 2 6
      contrib/src/sceneeditor/seColorEntry.py
  30. 0 1
      contrib/src/sceneeditor/seFileSaver.py
  31. 1 0
      contrib/src/sceneeditor/seGrid.py
  32. 0 1
      contrib/src/sceneeditor/seLights.py
  33. 3 2
      contrib/src/sceneeditor/seManipulation.py
  34. 6 10
      contrib/src/sceneeditor/seMopathRecorder.py
  35. 3 8
      contrib/src/sceneeditor/seParticlePanel.py
  36. 0 1
      contrib/src/sceneeditor/seParticles.py
  37. 3 8
      contrib/src/sceneeditor/sePlacer.py
  38. 3 8
      contrib/src/sceneeditor/seSceneGraphExplorer.py
  39. 1 1
      contrib/src/sceneeditor/seSelection.py
  40. 2 2
      contrib/src/sceneeditor/seSession.py
  41. 4 8
      contrib/src/sceneeditor/seTree.py
  42. 0 0
      contrib/src/speedtree/config_speedtree.cxx
  43. 1 1
      contrib/src/speedtree/config_speedtree.h
  44. 0 0
      contrib/src/speedtree/loaderFileTypeSrt.cxx
  45. 1 1
      contrib/src/speedtree/loaderFileTypeSrt.h
  46. 0 0
      contrib/src/speedtree/loaderFileTypeStf.cxx
  47. 1 1
      contrib/src/speedtree/loaderFileTypeStf.h
  48. 0 0
      contrib/src/speedtree/pandaspeedtree_composite1.cxx
  49. 0 0
      contrib/src/speedtree/speedTreeNode.I
  50. 2 12
      contrib/src/speedtree/speedTreeNode.cxx
  51. 1 2
      contrib/src/speedtree/speedTreeNode.h
  52. 0 0
      contrib/src/speedtree/speedtree_api.cxx
  53. 0 0
      contrib/src/speedtree/speedtree_api.h
  54. 0 0
      contrib/src/speedtree/stBasicTerrain.I
  55. 0 0
      contrib/src/speedtree/stBasicTerrain.cxx
  56. 1 1
      contrib/src/speedtree/stBasicTerrain.h
  57. 0 0
      contrib/src/speedtree/stTerrain.I
  58. 0 0
      contrib/src/speedtree/stTerrain.cxx
  59. 1 1
      contrib/src/speedtree/stTerrain.h
  60. 0 0
      contrib/src/speedtree/stTransform.I
  61. 0 0
      contrib/src/speedtree/stTransform.cxx
  62. 1 1
      contrib/src/speedtree/stTransform.h
  63. 0 0
      contrib/src/speedtree/stTree.I
  64. 0 0
      contrib/src/speedtree/stTree.cxx
  65. 1 1
      contrib/src/speedtree/stTree.h
  66. 15 1
      direct/CMakeLists.txt
  67. 80 77
      direct/src/actor/Actor.py
  68. 3 7
      direct/src/actor/DistributedActor.py
  69. 3 0
      direct/src/actor/__init__.py
  70. 39 43
      direct/src/cluster/ClusterClient.py
  71. 0 1
      direct/src/cluster/ClusterConfig.py
  72. 10 19
      direct/src/cluster/ClusterMsgs.py
  73. 33 34
      direct/src/cluster/ClusterServer.py
  74. 2 148
      direct/src/controls/BattleWalker.py
  75. 9 9
      direct/src/controls/ControlManager.py
  76. 2 0
      direct/src/controls/DevWalker.py
  77. 10 8
      direct/src/controls/GravityWalker.py
  78. 2 0
      direct/src/controls/InputState.py
  79. 7 5
      direct/src/controls/NonPhysicsWalker.py
  80. 0 5
      direct/src/controls/ObserverWalker.py
  81. 98 135
      direct/src/controls/PhysicsWalker.py
  82. 1 0
      direct/src/controls/TwoDWalker.py
  83. 1 3
      direct/src/dcparser/dcClass_ext.cxx
  84. 1 1
      direct/src/dcparser/dcPacker.cxx
  85. 5 1
      direct/src/dcparser/dcPacker_ext.cxx
  86. 3 16
      direct/src/directbase/TestStart.py
  87. 6 18
      direct/src/directbase/ThreeUpStart.py
  88. 17 16
      direct/src/directdevices/DirectDeviceManager.py
  89. 3 6
      direct/src/directdevices/DirectFastrak.py
  90. 18 19
      direct/src/directdevices/DirectJoybox.py
  91. 12 13
      direct/src/directdevices/DirectRadamec.py
  92. 3 3
      direct/src/directnotify/DirectNotify.py
  93. 11 26
      direct/src/directnotify/Logger.py
  94. 11 12
      direct/src/directnotify/Notifier.py
  95. 1 1
      direct/src/directnotify/RotatingLog.py
  96. 1 0
      direct/src/directscripts/Doxyfile.cxx
  97. 22 17
      direct/src/directscripts/eggcacher.py
  98. 2 2
      direct/src/directscripts/extract_docs.py
  99. 0 986
      direct/src/directscripts/gendocs.py
  100. 16 16
      direct/src/directtools/DirectCameraControl.py

+ 90 - 72
.github/workflows/ci.yml

@@ -11,16 +11,16 @@ jobs:
 
       matrix:
         profile:
-        - ubuntu-xenial-standard-unity-makefile
+        - ubuntu-bionic-standard-unity-makefile
         - ubuntu-bionic-coverage-ninja
         - macos-eigen-coverage-unity-xcode
         - macos-nometa-standard-makefile
-#       - windows-standard-unity-msvc # FIXME when GH Actions runners upgrade CMake to >=3.16.1
+        - windows-standard-unity-msvc
         - windows-nopython-nometa-standard-msvc
 
         include:
-        - profile: ubuntu-xenial-standard-unity-makefile
-          os: ubuntu-16.04
+        - profile: ubuntu-bionic-standard-unity-makefile
+          os: ubuntu-18.04
           config: Standard
           unity: YES
           generator: Unix Makefiles
@@ -40,7 +40,7 @@ jobs:
           eigen: NO
 
         - profile: macos-eigen-coverage-unity-xcode
-          os: macOS-latest
+          os: macOS-10.15
           config: Coverage
           unity: YES
           generator: Xcode
@@ -50,7 +50,7 @@ jobs:
           eigen: YES
 
         - profile: macos-nometa-standard-makefile
-          os: macOS-latest
+          os: macOS-10.15
           config: Standard
           unity: NO
           generator: Unix Makefiles
@@ -60,20 +60,20 @@ jobs:
           eigen: NO
 
         - profile: windows-standard-unity-msvc
-          os: windows-2019
+          os: windows-2022
           config: Standard
           unity: YES
-          generator: Visual Studio 16 2019
+          generator: Visual Studio 17 2022
           compiler: Default
           metalibs: YES
           python: YES
           eigen: NO
 
         - profile: windows-nopython-nometa-standard-msvc
-          os: windows-2019
+          os: windows-2022
           config: Standard
           unity: NO
-          generator: Visual Studio 16 2019
+          generator: Visual Studio 17 2022
           compiler: Default
           metalibs: NO
           python: NO
@@ -92,12 +92,15 @@ jobs:
     - name: Install dependencies (macOS)
       if: runner.os == 'macOS'
       run: |
-        curl -O https://www.panda3d.org/download/panda3d-1.10.5/panda3d-1.10.5-tools-mac.tar.gz
-        tar -xf panda3d-1.10.5-tools-mac.tar.gz
+        curl -O https://www.panda3d.org/download/panda3d-1.10.10/panda3d-1.10.10-tools-mac.tar.gz
+        tar -xf panda3d-1.10.10-tools-mac.tar.gz
+        mv panda3d-1.10.10/thirdparty thirdparty
+        rmdir panda3d-1.10.10
 
-        brew install ccache
+        # Temporary hack so that pzip can run, since we are about to remove Cg anyway.
+        install_name_tool -id "$(pwd)/thirdparty/darwin-libs-a/nvidiacg/lib/libCg.dylib" thirdparty/darwin-libs-a/nvidiacg/lib/libCg.dylib
 
-        echo "##[set-env name=thirdpartyOption;]-D THIRDPARTY_DIRECTORY=../panda3d-1.10.5/thirdparty" -DHAVE_CG=OFF
+        brew install ccache
 
     - name: Install dependencies (Ubuntu)
       if: startsWith(matrix.os, 'ubuntu')
@@ -120,20 +123,19 @@ jobs:
       if: runner.os == 'Windows'
       uses: actions/cache@v1
       with:
-        path: thirdparty-tools
-        key: ci-cmake-${{ runner.OS }}-thirdparty-v1.10.5-r1
+        path: thirdparty
+        key: ci-cmake-${{ runner.OS }}-thirdparty-v1.10.10-r1
     - name: Install dependencies (Windows)
       if: runner.os == 'Windows'
       shell: powershell
       run: |
-        if (!(Test-Path thirdparty-tools/panda3d-1.10.5)) {
+        if (!(Test-Path thirdparty/win-libs-vc14-x64)) {
           $wc = New-Object System.Net.WebClient
-          $wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.5/panda3d-1.10.5-tools-win64.zip", "thirdparty-tools.zip")
+          $wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.10/panda3d-1.10.10-tools-win64.zip", "thirdparty-tools.zip")
           Expand-Archive -Path thirdparty-tools.zip
+          Move-Item -Path thirdparty-tools/panda3d-1.10.10/thirdparty -Destination .
         }
 
-        echo "##[set-env name=thirdpartyOption;]-D THIRDPARTY_DIRECTORY=../thirdparty-tools/panda3d-1.10.5/thirdparty"
-
     - name: ccache (non-Windows)
       if: runner.os != 'Windows'
       uses: actions/cache@v1
@@ -146,15 +148,15 @@ jobs:
       env:
         CMAKE_GENERATOR: "${{ matrix.generator }}"
       run: >
-        mkdir build
+        mkdir -p build
 
         cd build
 
         if ${{ matrix.compiler == 'Clang' }}; then
-          if [[ "$CMAKE_GENERATOR" == *Studio*2019* ]]; then
-            export CMAKE_GENERATOR_TOOLSET=ClangCL thirdpartyOption="$thirdpartyOption -DHAVE_HARFBUZZ=NO"
+          if [[ "$CMAKE_GENERATOR" =~ Studio.+20(19|22) ]]; then
+            export CMAKE_GENERATOR_TOOLSET=ClangCL
           elif [[ "$CMAKE_GENERATOR" == *Studio* ]]; then
-            export CMAKE_GENERATOR_TOOLSET=LLVM thirdpartyOption="$thirdpartyOption -DHAVE_HARFBUZZ=NO"
+            export CMAKE_GENERATOR_TOOLSET=LLVM
           else
             export CC=clang CXX=clang++
           fi
@@ -162,7 +164,8 @@ jobs:
 
         if ${{ runner.os != 'Windows' }}; then
           compilerLauncher=$(echo -DCMAKE_C{,XX}_COMPILER_LAUNCHER=ccache)
-          echo "##[set-env name=CCACHE_DIR;]$(dirname $PWD)/ccache"
+          export CCACHE_DIR="$(dirname $PWD)/ccache"
+          echo "CCACHE_DIR=$(dirname $PWD)/ccache" >> $GITHUB_ENV
         fi
 
         cmake
@@ -172,7 +175,6 @@ jobs:
         -D BUILD_METALIBS=${{ matrix.metalibs }}
         -D HAVE_PYTHON=${{ matrix.python }}
         -D HAVE_EIGEN=${{ matrix.eigen }}
-        ${thirdpartyOption:-}
         ..
 
     - name: Build (no Python)
@@ -182,25 +184,25 @@ jobs:
       run: cmake --build . --config ${{ matrix.config }} --parallel 4
       # END A
 
-    - name: Setup Python (Python 3.5)
+    - name: Setup Python (Python 3.6)
       if: contains(matrix.python, 'YES')
-      uses: actions/setup-python@v1
+      uses: actions/setup-python@v2
       with:
-        python-version: 3.5
-    - name: Configure (Python 3.5)
+        python-version: 3.6
+    - name: Configure (Python 3.6)
       if: contains(matrix.python, 'YES')
       working-directory: build
       shell: bash
       run: >
-        cmake -DWANT_PYTHON_VERSION=3.5
-        -DPython_FIND_REGISTRY=NEVER -DPython_ROOT=$pythonLocation .
-    - name: Build (Python 3.5)
+        cmake -DWANT_PYTHON_VERSION=3.6
+        -DPython_FIND_REGISTRY=NEVER -DPython_ROOT="$pythonLocation" .
+    - name: Build (Python 3.6)
       if: contains(matrix.python, 'YES')
       # BEGIN A
       working-directory: build
       run: cmake --build . --config ${{ matrix.config }} --parallel 4
       # END A
-    - name: Test (Python 3.5)
+    - name: Test (Python 3.6)
       # BEGIN B
       if: contains(matrix.python, 'YES')
       working-directory: build
@@ -209,29 +211,30 @@ jobs:
         PYTHONPATH: ${{ matrix.config }}
       run: |
         PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
-        $PYTHON_EXECUTABLE -m pip install pytest
-        $PYTHON_EXECUTABLE -m pytest ../tests
+        $PYTHON_EXECUTABLE -m pip install pytest pytest-cov
+        export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
+        $PYTHON_EXECUTABLE -m pytest ../tests --cov=.
       # END B
 
-    - name: Setup Python (Python 3.6)
+    - name: Setup Python (Python 3.7)
       if: contains(matrix.python, 'YES')
-      uses: actions/setup-python@v1
+      uses: actions/setup-python@v2
       with:
-        python-version: 3.6
-    - name: Configure (Python 3.6)
+        python-version: 3.7
+    - name: Configure (Python 3.7)
       if: contains(matrix.python, 'YES')
       working-directory: build
       shell: bash
       run: >
-        cmake -DWANT_PYTHON_VERSION=3.6
-        -DPython_FIND_REGISTRY=NEVER -DPython_ROOT=$pythonLocation .
-    - name: Build (Python 3.6)
+        cmake -DWANT_PYTHON_VERSION=3.7
+        -DPython_FIND_REGISTRY=NEVER -DPython_ROOT="$pythonLocation" .
+    - name: Build (Python 3.7)
       if: contains(matrix.python, 'YES')
       # BEGIN A
       working-directory: build
       run: cmake --build . --config ${{ matrix.config }} --parallel 4
       # END A
-    - name: Test (Python 3.6)
+    - name: Test (Python 3.7)
       # BEGIN B
       if: contains(matrix.python, 'YES')
       working-directory: build
@@ -245,25 +248,25 @@ jobs:
         $PYTHON_EXECUTABLE -m pytest ../tests --cov=.
       # END B
 
-    - name: Setup Python (Python 3.7)
+    - name: Setup Python (Python 3.8)
       if: contains(matrix.python, 'YES')
-      uses: actions/setup-python@v1
+      uses: actions/setup-python@v2
       with:
-        python-version: 3.7
-    - name: Configure (Python 3.7)
+        python-version: 3.8
+    - name: Configure (Python 3.8)
       if: contains(matrix.python, 'YES')
       working-directory: build
       shell: bash
       run: >
-        cmake -DWANT_PYTHON_VERSION=3.7
-        -DPython_FIND_REGISTRY=NEVER -DPython_ROOT=$pythonLocation .
-    - name: Build (Python 3.7)
+        cmake -DWANT_PYTHON_VERSION=3.8
+        -DPython_FIND_REGISTRY=NEVER -DPython_ROOT="$pythonLocation" .
+    - name: Build (Python 3.8)
       if: contains(matrix.python, 'YES')
       # BEGIN A
       working-directory: build
       run: cmake --build . --config ${{ matrix.config }} --parallel 4
       # END A
-    - name: Test (Python 3.7)
+    - name: Test (Python 3.8)
       # BEGIN B
       if: contains(matrix.python, 'YES')
       working-directory: build
@@ -277,25 +280,25 @@ jobs:
         $PYTHON_EXECUTABLE -m pytest ../tests --cov=.
       # END B
 
-    - name: Setup Python (Python 3.8)
+    - name: Setup Python (Python 3.9)
       if: contains(matrix.python, 'YES')
-      uses: actions/setup-python@v1
+      uses: actions/setup-python@v2
       with:
-        python-version: 3.8
-    - name: Configure (Python 3.8)
+        python-version: 3.9
+    - name: Configure (Python 3.9)
       if: contains(matrix.python, 'YES')
       working-directory: build
       shell: bash
       run: >
-        cmake -DWANT_PYTHON_VERSION=3.8
-        -DPython_FIND_REGISTRY=NEVER -DPython_ROOT=$pythonLocation .
-    - name: Build (Python 3.8)
+        cmake -DWANT_PYTHON_VERSION=3.9
+        -DPython_FIND_REGISTRY=NEVER -DPython_ROOT="$pythonLocation" .
+    - name: Build (Python 3.9)
       if: contains(matrix.python, 'YES')
       # BEGIN A
       working-directory: build
       run: cmake --build . --config ${{ matrix.config }} --parallel 4
       # END A
-    - name: Test (Python 3.8)
+    - name: Test (Python 3.9)
       # BEGIN B
       if: contains(matrix.python, 'YES')
       working-directory: build
@@ -330,12 +333,12 @@ jobs:
     if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
     strategy:
       matrix:
-        os: [ubuntu-16.04, windows-2016, macOS-latest]
+        os: [ubuntu-18.04, windows-2016, macOS-11]
     runs-on: ${{ matrix.os }}
     steps:
     - uses: actions/checkout@v1
     - name: Install dependencies (Ubuntu)
-      if: matrix.os == 'ubuntu-16.04'
+      if: matrix.os == 'ubuntu-18.04'
       run: |
         sudo apt-get update
         sudo apt-get install build-essential bison flex libfreetype6-dev libgl1-mesa-dev libjpeg-dev libode-dev libopenal-dev libpng-dev libssl-dev libvorbis-dev libx11-dev libxcursor-dev libxrandr-dev nvidia-cg-toolkit zlib1g-dev
@@ -344,36 +347,51 @@ jobs:
       shell: powershell
       run: |
         $wc = New-Object System.Net.WebClient
-        $wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.5/panda3d-1.10.5-tools-win64.zip", "thirdparty-tools.zip")
+        $wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-win64.zip", "thirdparty-tools.zip")
         Expand-Archive -Path thirdparty-tools.zip
-        Move-Item -Path thirdparty-tools/panda3d-1.10.5/thirdparty -Destination .
+        Move-Item -Path thirdparty-tools/panda3d-1.10.11/thirdparty -Destination .
     - name: Get thirdparty packages (macOS)
       if: runner.os == 'macOS'
       run: |
-        curl -O https://www.panda3d.org/download/panda3d-1.10.5/panda3d-1.10.5-tools-mac.tar.gz
-        tar -xf panda3d-1.10.5-tools-mac.tar.gz
-        mv panda3d-1.10.5/thirdparty thirdparty
-        rmdir panda3d-1.10.5
+        curl -O https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-mac.tar.gz
+        tar -xf panda3d-1.10.11-tools-mac.tar.gz
+        mv panda3d-1.10.11/thirdparty thirdparty
+        rmdir panda3d-1.10.11
         (cd thirdparty/darwin-libs-a && rm -rf rocket)
+    - name: Set up Python 3.9
+      uses: actions/setup-python@v2
+      with:
+        python-version: 3.9
+    - name: Build Python 3.9
+      shell: bash
+      run: |
+        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4
+    - name: Test Python 3.9
+      shell: bash
+      run: |
+        python -m pip install pytest
+        PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
     - name: Set up Python 3.8
-      uses: actions/setup-python@v1
+      uses: actions/setup-python@v2
       with:
         python-version: 3.8
     - name: Build Python 3.8
+      shell: bash
       run: |
-        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir=$pythonLocation/include --python-libdir=$pythonLocation/lib --verbose --threads=4
+        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4
     - name: Test Python 3.8
       shell: bash
       run: |
         python -m pip install pytest
         PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
     - name: Set up Python 3.7
-      uses: actions/setup-python@v1
+      uses: actions/setup-python@v2
       with:
         python-version: 3.7
     - name: Build Python 3.7
+      shell: bash
       run: |
-        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir=$pythonLocation/include --python-libdir=$pythonLocation/lib --verbose --threads=4
+        python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4
     - name: Test Python 3.7
       shell: bash
       run: |

+ 1 - 0
.gitignore

@@ -3,6 +3,7 @@
 /thirdparty/
 /targetroot/
 /dstroot/
+/sdks/
 
 # Core dumps and traces
 core

+ 534 - 0
.pylintrc

@@ -0,0 +1,534 @@
+[MASTER]
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code.
+extension-pkg-whitelist=panda3d
+
+# Specify a score threshold to be exceeded before program exits with error.
+fail-under=10.0
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=thirdparty
+
+# Add files or directories matching the regex patterns to the blacklist. The
+# regex matches against base names, not paths.
+ignore-patterns=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
+# number of processors available to use.
+jobs=1
+
+# Control the amount of potential inferred values when inferring a single
+# object. This can help the performance when dealing with large functions or
+# complex, nested conditions.
+limit-inference-results=100
+
+# List of plugins (as comma separated values of python module names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# When enabled, pylint would attempt to guess common misconfiguration and emit
+# user-friendly hints instead of false-positive error messages.
+suggestion-mode=yes
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
+confidence=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once). You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use "--disable=all --enable=classes
+# --disable=W".
+disable=attribute-defined-outside-init,
+        broad-except,
+        comparison-with-callable,
+        dangerous-default-value,
+        global-statement,
+        import-outside-toplevel,
+        invalid-name,
+        line-too-long,
+        misplaced-comparison-constant,
+        missing-class-docstring,
+        missing-function-docstring,
+        missing-module-docstring,
+        protected-access,
+        r,
+        raise-missing-from,
+        redefined-builtin,
+        redefined-outer-name,
+        too-many-lines,
+        unused-argument,
+        unused-variable,
+        unused-wildcard-import,
+        using-constant-test,
+        wildcard-import,
+        wrong-import-order,
+        wrong-import-position
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+enable=c-extension-no-member
+
+
+[REPORTS]
+
+# Python expression which should return a score less than or equal to 10. You
+# have access to the variables 'error', 'warning', 'refactor', and 'convention'
+# which contain the number of messages in each category, as well as 'statement'
+# which is the total number of statements analyzed. This score is used by the
+# global evaluation report (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details.
+#msg-template=
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio). You can also give a reporter class, e.g.
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Tells whether to display a full report or only the messages.
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
+
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=5
+
+# Complete name of functions that never returns. When checking for
+# inconsistent-return-statements if a never returning function is called then
+# it will be considered as an explicit return statement and no message will be
+# printed.
+never-returning-functions=sys.exit
+
+
+[FORMAT]
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=LF
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# String used as indentation unit. This is usually "    " (4 spaces) or "\t" (1
+# tab).
+indent-string='    '
+
+# Maximum number of characters on a single line.
+max-line-length=100
+
+# Maximum number of lines in a module.
+max-module-lines=1000
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[SPELLING]
+
+# Limits count of emitted suggestions for spelling mistakes.
+max-spelling-suggestions=4
+
+# Spelling dictionary name. Available dictionaries: none. To make it work,
+# install the python-enchant package.
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains the private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to the private dictionary (see the
+# --spelling-private-dict-file option) instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# Tells whether to warn about missing members when the owner of the attribute
+# is inferred to be None.
+ignore-none=yes
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local,direct.showbase.PythonUtil.ScratchPad
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis). It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+# List of decorators that change the signature of a decorated function.
+signature-mutators=
+
+
+[SIMILARITIES]
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+
+[VARIABLES]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid defining new builtins when possible.
+additional-builtins=base,simbase,__dev__,onScreenDebug,globalClock,render,hidden,cluster
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,
+          _cb
+
+# A regular expression matching the name of dummy variables (i.e. expected to
+# not be used).
+dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore.
+ignored-argument-names=_.*|^ignored_|^unused_
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,
+      XXX,
+      TODO
+
+# Regular expression of note tags to take in consideration.
+#notes-rgx=
+
+
+[BASIC]
+
+# Naming style matching correct argument names.
+argument-naming-style=snake_case
+
+# Regular expression matching correct argument names. Overrides argument-
+# naming-style.
+#argument-rgx=
+
+# Naming style matching correct attribute names.
+attr-naming-style=snake_case
+
+# Regular expression matching correct attribute names. Overrides attr-naming-
+# style.
+#attr-rgx=
+
+# Bad variable names which should always be refused, separated by a comma.
+bad-names=foo,
+          bar,
+          baz,
+          toto,
+          tutu,
+          tata
+
+# Bad variable names regexes, separated by a comma. If names match any regex,
+# they will always be refused
+bad-names-rgxs=
+
+# Naming style matching correct class attribute names.
+class-attribute-naming-style=any
+
+# Regular expression matching correct class attribute names. Overrides class-
+# attribute-naming-style.
+#class-attribute-rgx=
+
+# Naming style matching correct class names.
+class-naming-style=PascalCase
+
+# Regular expression matching correct class names. Overrides class-naming-
+# style.
+#class-rgx=
+
+# Naming style matching correct constant names.
+const-naming-style=UPPER_CASE
+
+# Regular expression matching correct constant names. Overrides const-naming-
+# style.
+#const-rgx=
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+# Naming style matching correct function names.
+function-naming-style=snake_case
+
+# Regular expression matching correct function names. Overrides function-
+# naming-style.
+#function-rgx=
+
+# Good variable names which should always be accepted, separated by a comma.
+good-names=i,
+           j,
+           k,
+           ex,
+           Run,
+           _
+
+# Good variable names regexes, separated by a comma. If names match any regex,
+# they will always be accepted
+good-names-rgxs=
+
+# Include a hint for the correct naming format with invalid-name.
+include-naming-hint=no
+
+# Naming style matching correct inline iteration names.
+inlinevar-naming-style=any
+
+# Regular expression matching correct inline iteration names. Overrides
+# inlinevar-naming-style.
+#inlinevar-rgx=
+
+# Naming style matching correct method names.
+method-naming-style=snake_case
+
+# Regular expression matching correct method names. Overrides method-naming-
+# style.
+#method-rgx=
+
+# Naming style matching correct module names.
+module-naming-style=snake_case
+
+# Regular expression matching correct module names. Overrides module-naming-
+# style.
+#module-rgx=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+# These decorators are taken in consideration only for invalid-name.
+property-classes=abc.abstractproperty
+
+# Naming style matching correct variable names.
+variable-naming-style=snake_case
+
+# Regular expression matching correct variable names. Overrides variable-
+# naming-style.
+#variable-rgx=
+
+
+[LOGGING]
+
+# The type of string formatting that logging methods do. `old` means using %
+# formatting, `new` is for `{}` formatting.
+logging-format-style=old
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format.
+logging-modules=logging
+
+
+[STRING]
+
+# This flag controls whether inconsistent-quotes generates a warning when the
+# character used as a quote delimiter is used inconsistently within a module.
+check-quote-consistency=no
+
+# This flag controls whether the implicit-str-concat should generate a warning
+# on implicit string concatenation in sequences defined over several lines.
+check-str-concat-over-line-jumps=no
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method.
+max-args=5
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Maximum number of boolean expressions in an if statement (see R0916).
+max-bool-expr=5
+
+# Maximum number of branch for function / method body.
+max-branches=12
+
+# Maximum number of locals for function / method body.
+max-locals=15
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+# Maximum number of return / yield for function / method body.
+max-returns=6
+
+# Maximum number of statements in function / method body.
+max-statements=50
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+
+[IMPORTS]
+
+# List of modules that can be imported at any level, not just the top level
+# one.
+allow-any-import-level=
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Deprecated modules which should not be used, separated by a comma.
+deprecated-modules=optparse,tkinter.tix
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled).
+ext-import-graph=
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled).
+import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled).
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+# Couples of modules and preferred modules, separated by a comma.
+preferred-modules=
+
+
+[CLASSES]
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+                      __new__,
+                      setUp,
+                      __post_init__
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,
+                  _fields,
+                  _replace,
+                  _source,
+                  _make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=cls
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "BaseException, Exception".
+overgeneral-exceptions=BaseException,
+                       Exception

+ 0 - 60
.travis.yml

@@ -1,60 +0,0 @@
-language: cpp
-sudo: false
-matrix:
-  include:
-    - compiler: clang
-      env: PYTHONV=python3 FLAGS=--installer
-    - compiler: clang
-      env: PYTHONV=python3 FLAGS=--override=STDFLOAT_DOUBLE=1
-    - compiler: gcc
-      env: PYTHONV=python3 FLAGS=--optimize=4
-      before_install:
-        - export CC=gcc-4.7
-        - export CXX=g++-4.7
-    - compiler: clang
-      env: PYTHONV=python3 FLAGS=--no-python SKIP_TESTS=1
-addons:
-  apt:
-    sources:
-    - ubuntu-toolchain-r-test
-    packages:
-    - gcc-4.7
-    - g++-4.7
-    - bison
-    - flex
-    - libfreetype6-dev
-    - libgl1-mesa-dev
-    - libjpeg-dev
-    - libode-dev
-    - libopenal-dev
-    - libpng-dev
-    - libssl-dev
-    - libvorbis-dev
-    - libx11-dev
-    - libxcursor-dev
-    - libxrandr-dev
-    - nvidia-cg-toolkit
-    - python-dev
-    - python3-dev
-    - python-virtualenv
-    - zlib1g-dev
-    - fakeroot
-install:
-    - virtualenv --python=$PYTHONV venv && source venv/bin/activate
-    - $PYTHONV -m pip install pytest
-script:
-    - $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT $FLAGS --threads 4
-    - test -n "$SKIP_TESTS" || LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV -m pytest -v tests
-notifications:
-  irc:
-    channels:
-      - secure: "jfwHT9RHAVOGRGTMY8TpYKJI6rq8nFoIj41Y0soZdJQNWtSSFEK9AyzZeMY+2dHga7cR/X+/0NWZ2ehhedTnd9FvlzOnMWWC3K0I/b3XWbEdVEqIZnggFkKGqs82Gy3omguRC63yWupeJCcSCckIhoWbLzWy6xV8lF5WC80iXi8="
-    on_success: change
-    on_failure: always
-    use_notice: true
-    skip_join: false
-  webhooks:
-    urls:
-      - https://www.panda3d.org/webhooks/travis-ci.php
-    on_success: change
-    on_failure: always

+ 8 - 4
BACKERS.md

@@ -10,12 +10,12 @@ This is a list of all the people who are contributing financially to Panda3D.  I
 
 ## Bronze Sponsors
 
-![Bronze Sponsors](https://opencollective.com/panda3d/tiers/bronze-sponsor.svg?avatarHeight=48&width=600)
+[<img src="https://www.panda3d.org/wp-content/uploads/2021/02/changecrab_logo.png" alt="ChangeCrab" height="48">](https://changecrab.com/) ![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/)
+* [ChangeCrab](https://changecrab.com)
 
 ## Benefactors
 
@@ -23,14 +23,18 @@ This is a list of all the people who are contributing financially to Panda3D.  I
 
 * Sam Edwards
 * Max Voss
-* Will Nielsen
+* Hawkheart
+* Dan Mlodecki
 
 ## Enthusiasts
 
-![Benefactors](https://opencollective.com/panda3d/tiers/enthusiast.svg?avatarHeight=48&width=600)
+![Enthusiasts](https://opencollective.com/panda3d/tiers/enthusiast.svg?avatarHeight=48&width=600)
 
 * Eric Thomson
 * Kyle Roach
+* Brian Lach
+* C0MPU73R
+* Maxwell Dreytser
 
 ## Backers
 

+ 45 - 20
CMakeLists.txt

@@ -32,23 +32,32 @@ a CMake < 3.9. Making a guess if this is a multi-config generator.")
   endif()
 endif()
 
-# Define the type of build we are setting up.
-set(_configs Standard Release RelWithDebInfo Debug MinSizeRel)
-if(CMAKE_CXX_COMPILER_ID MATCHES "(AppleClang|Clang|GCC)")
-  list(APPEND _configs Coverage)
-endif()
-
+# Set the default CMAKE_BUILD_TYPE before calling project().
 if(IS_MULTICONFIG)
   message(STATUS "Using multi-configuration generator")
 else()
-  # Set the default CMAKE_BUILD_TYPE before calling project().
   if(NOT CMAKE_BUILD_TYPE)
     set(CMAKE_BUILD_TYPE Standard CACHE STRING "Choose the type of build." FORCE)
     message(STATUS "Using default build type ${CMAKE_BUILD_TYPE}")
   else()
     message(STATUS "Using build type ${CMAKE_BUILD_TYPE}")
   endif()
-  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${_configs})
+endif()
+
+# Set defaults for macOS, must be before project().
+if(APPLE)
+  set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum macOS version to target")
+  set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
+
+  if(CMAKE_VERSION VERSION_LESS "3.19" AND NOT CMAKE_OSX_SYSROOT)
+    # Older CMake chose SDK based on deployment target, against Apple's recommendations.
+    # However, we need to use the latest to be able to target arm64.
+    if(IS_DIRECTORY "/Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk")
+      set(CMAKE_OSX_SYSROOT "/Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk" CACHE STRING "")
+    elseif(IS_DIRECTORY "/Library/Developer/CommandLineTools/SDKs/MacOSX11.0.sdk")
+      set(CMAKE_OSX_SYSROOT "/Library/Developer/CommandLineTools/SDKs/MacOSX11.0.sdk" CACHE STRING "")
+    endif()
+  endif()
 endif()
 
 # Figure out the version
@@ -59,6 +68,18 @@ project(Panda3D VERSION ${_version})
 unset(_version)
 unset(_s)
 
+# Determine the possible build types.  Must be *after* calling project().
+set(_configs Standard Release RelWithDebInfo Debug MinSizeRel)
+if(CMAKE_CXX_COMPILER_ID MATCHES "(AppleClang|Clang|GCC)")
+  list(APPEND _configs Coverage)
+endif()
+
+if(IS_MULTICONFIG)
+  set(CMAKE_CONFIGURATION_TYPES "${_configs}" CACHE STRING "" FORCE)
+else()
+  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${_configs})
+endif()
+
 enable_testing()
 
 string(REPLACE "$(EFFECTIVE_PLATFORM_NAME)" "" PANDA_CFG_INTDIR "${CMAKE_CFG_INTDIR}")
@@ -131,26 +152,30 @@ if(BUILD_MODELS)
     "${CMAKE_CURRENT_SOURCE_DIR}/models/"
     "${PROJECT_BINARY_DIR}/${PANDA_CFG_INTDIR}/models"
     *.egg)
-  run_pzip(dmodels
-    "${CMAKE_CURRENT_SOURCE_DIR}/dmodels/src/"
-    "${PROJECT_BINARY_DIR}/${PANDA_CFG_INTDIR}/models"
-    *.egg)
 
   add_custom_command(TARGET models
                      POST_BUILD
                      COMMAND ${CMAKE_COMMAND}
-                             -DSOURCE="${CMAKE_CURRENT_SOURCE_DIR}/models/maps/"
-                             -DDESTINATION="${PANDA_OUTPUT_DIR}/models/maps"
+                             -DSOURCE="${CMAKE_CURRENT_SOURCE_DIR}/models/audio/"
+                             -DDESTINATION="${PANDA_OUTPUT_DIR}/models/audio"
                              -P ${PROJECT_SOURCE_DIR}/cmake/scripts/CopyPattern.cmake
-                     COMMENT "Copying models/maps")
-  add_custom_command(TARGET dmodels
+                     COMMENT "Copying models/audio")
+
+  add_custom_command(TARGET models
                      POST_BUILD
                      COMMAND ${CMAKE_COMMAND}
-                             -DSOURCE="${CMAKE_CURRENT_SOURCE_DIR}/dmodels/src/"
-                             -DDESTINATION="${PANDA_OUTPUT_DIR}/models"
-                             -DFILES_MATCHING="PATTERN;*.rgb;PATTERN;*.png;PATTERN;*.jpg;PATTERN;*.wav"
+                             -DSOURCE="${CMAKE_CURRENT_SOURCE_DIR}/models/icons/"
+                             -DDESTINATION="${PANDA_OUTPUT_DIR}/models/icons"
                              -P ${PROJECT_SOURCE_DIR}/cmake/scripts/CopyPattern.cmake
-                     COMMENT "Copying dmodels' assets")
+                     COMMENT "Copying models/icons")
+
+  add_custom_command(TARGET models
+                     POST_BUILD
+                     COMMAND ${CMAKE_COMMAND}
+                             -DSOURCE="${CMAKE_CURRENT_SOURCE_DIR}/models/maps/"
+                             -DDESTINATION="${PANDA_OUTPUT_DIR}/models/maps"
+                             -P ${PROJECT_SOURCE_DIR}/cmake/scripts/CopyPattern.cmake
+                     COMMENT "Copying models/maps")
 
   install(DIRECTORY "${PANDA_OUTPUT_DIR}/models"
     COMPONENT Models DESTINATION ${CMAKE_INSTALL_DATADIR}/panda3d)

+ 1 - 1
CONTRIBUTING.md

@@ -18,7 +18,7 @@ It is important for acceptance that the change is implemented in a way that fits
 the general design principles of the Panda3D API, and fits well with the general
 priorities of the team.  Therefore, prior discussion with other developers is
 critical.  Issues can be used to facilitate this, but we also invite you to
-visit the #development channel on Discord (or #panda3d-devel on FreeNode IRC).
+visit the #development channel on Discord (or #panda3d-devel on Libera Chat).
 
 We also recommend that you familiarize yourself with the established coding
 style and design patterns of Panda3D, to reduce the amount of changes that have

+ 12 - 19
README.md

@@ -1,4 +1,4 @@
-[![Build Status](https://travis-ci.org/panda3d/panda3d.svg?branch=master)](https://travis-ci.org/panda3d/panda3d)
+[![Build Status](https://github.com/panda3d/panda3d/workflows/Continuous%20Integration/badge.svg?branch=master)](https://github.com/panda3d/panda3d/actions?query=branch%3Amaster+workflow%3A%22Continuous+Integration%22)
 [![OpenCollective](https://opencollective.com/panda3d/backers/badge.svg)](https://opencollective.com/panda3d)
 [![OpenCollective](https://opencollective.com/panda3d/sponsors/badge.svg)](https://opencollective.com/panda3d)
 
@@ -24,7 +24,7 @@ Installing Panda3D
 ==================
 
 The latest Panda3D SDK can be downloaded from
-[this page](https://www.panda3d.org/download/sdk-1-10-6/).
+[this page](https://www.panda3d.org/download/sdk-1-10-11/).
 If you are familiar with installing Python packages, you can use
 the following command:
 
@@ -52,9 +52,9 @@ Building Panda3D
 Windows
 -------
 
-You can build Panda3D with the Microsoft Visual C++ 2015, 2017 or 2019 compiler,
-which can be downloaded for free from the [Visual Studio site](https://visualstudio.microsoft.com/downloads/).
-You will also need to install the [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk),
+You can build Panda3D with the Microsoft Visual C++ 2015, 2017, 2019 or 2022
+compiler, which can be downloaded for free from the [Visual Studio site](https://visualstudio.microsoft.com/downloads/).
+You will also need to install the [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk),
 and if you intend to target Windows Vista, you will also need the
 [Windows 8.1 SDK](https://go.microsoft.com/fwlink/p/?LinkId=323507).
 
@@ -64,17 +64,17 @@ 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
 building them from source.
 
-- https://www.panda3d.org/download/panda3d-1.10.6/panda3d-1.10.6-tools-win64.zip
-- https://www.panda3d.org/download/panda3d-1.10.6/panda3d-1.10.6-tools-win32.zip
+- https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-win64.zip
+- https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-win32.zip
 
 After acquiring these dependencies, you can build Panda3D from the command
 prompt using the following command.  Change the `--msvc-version` option based
-on your version of Visual C++; 2019 is 14.2, 2017 is 14.1, and 2015 is 14.
-Remove the `--windows-sdk=10` option if you need to support Windows Vista,
-which requires the Windows 8.1 SDK.
+on your version of Visual C++; 2022 is 14.3, 2019 is 14.2, 2017 is 14.1, and
+2015 is 14.  Remove the `--windows-sdk=10` option if you need to support
+Windows Vista, which requires the Windows 8.1 SDK.
 
 ```bash
-makepanda\makepanda.bat --everything --installer --msvc-version=14.2 --windows-sdk=10 --no-eigen --threads=2
+makepanda\makepanda.bat --everything --installer --msvc-version=14.3 --windows-sdk=10 --no-eigen --threads=2
 ```
 
 When the build succeeds, it will produce an .exe file that you can use to
@@ -136,7 +136,7 @@ macOS
 -----
 
 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.10.6/panda3d-1.10.6-tools-mac.tar.gz).
+compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.11/panda3d-1.10.11-tools-mac.tar.gz).
 
 After placing the thirdparty directory inside the panda3d source directory,
 you may build Panda3D using a command like the following:
@@ -249,10 +249,3 @@ 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)

+ 4 - 0
cmake/macros/AddFlexTarget.cmake

@@ -74,4 +74,8 @@ function(add_flex_target output_cxx input_lxx)
       ${commands}
       DEPENDS ${depends})
   endif()
+
+  if(MSVC)
+    set_source_files_properties(${outputs} PROPERTIES COMPILE_DEFINITIONS YY_NO_UNISTD_H=1)
+  endif()
 endfunction(add_flex_target)

+ 15 - 5
cmake/macros/PackageConfig.cmake

@@ -145,6 +145,8 @@ function(package_option name)
     endif()
   endif()
 
+  option("HAVE_${name}" "${cache_string}" "${default}")
+
   # If it was set by the user but not found, display an error.
   string(TOUPPER "${found_as}" FOUND_AS)
   if(HAVE_${name} AND NOT ${found_as}_FOUND AND NOT ${FOUND_AS}_FOUND)
@@ -165,6 +167,12 @@ function(package_option name)
 
   set(PANDA_PACKAGE_DEFAULT_${name} "${default}" PARENT_SCOPE)
 
+  if(${found_as}_FOUND OR ${FOUND_AS}_FOUND)
+    set(PANDA_PACKAGE_FOUND_${name} ON PARENT_SCOPE)
+  else()
+    set(PANDA_PACKAGE_FOUND_${name} OFF PARENT_SCOPE)
+  endif()
+
   # Create the INTERFACE library used to depend on this package.
   add_library(PKG::${name} INTERFACE IMPORTED GLOBAL)
 
@@ -180,9 +188,7 @@ function(package_option name)
       "$<TARGET_PROPERTY:PKG::${name},INTERFACE_INCLUDE_DIRECTORIES>")
   endif()
 
-  # Create the option, and if it actually is enabled, populate the INTERFACE
-  # library created above
-  option("HAVE_${name}" "${cache_string}" "${default}")
+  # If the option actually is enabled, populate the INTERFACE library created above
   if(HAVE_${name})
     set(use_variables ON)
 
@@ -279,15 +285,19 @@ function(show_packages)
   foreach(package ${_ALL_CONFIG_PACKAGES})
     set(desc "${PANDA_PACKAGE_DESC_${package}}")
     set(note "${PANDA_PACKAGE_NOTE_${package}}")
-    if(HAVE_${package})
+
+    if(HAVE_${package} AND PANDA_PACKAGE_FOUND_${package})
       if(NOT note STREQUAL "")
         message("+ ${desc} (${note})")
       else()
         message("+ ${desc}")
       endif()
 
+    elseif(HAVE_${package})
+      message("! ${desc} (enabled but not found)")
+
     else()
-      if(NOT ${package}_FOUND)
+      if(NOT PANDA_PACKAGE_FOUND_${package})
         set(reason "not found")
       elseif(NOT PANDA_PACKAGE_DEFAULT_${package})
         set(reason "not requested")

+ 24 - 0
cmake/modules/FindFFMPEG.cmake

@@ -89,16 +89,36 @@ if(APPLE)
   # When statically built for Apple, FFMPEG may have dependencies on these
   # additional frameworks and libraries.
 
+  find_library(APPLE_AUDIOTOOLBOX_LIBRARY AudioToolbox)
+  if(APPLE_AUDIOTOOLBOX_LIBRARY)
+    list(APPEND FFMPEG_LIBRARIES "${APPLE_AUDIOTOOLBOX_LIBRARY}")
+  endif()
+
+  find_library(APPLE_COREMEDIA_LIBRARY CoreMedia)
+  if(APPLE_COREMEDIA_LIBRARY)
+    list(APPEND FFMPEG_LIBRARIES "${APPLE_COREMEDIA_LIBRARY}")
+  endif()
+
   find_library(APPLE_COREVIDEO_LIBRARY CoreVideo)
   if(APPLE_COREVIDEO_LIBRARY)
     list(APPEND FFMPEG_LIBRARIES "${APPLE_COREVIDEO_LIBRARY}")
   endif()
 
+  find_library(APPLE_SECURITY_LIBRARY Security)
+  if(APPLE_SECURITY_LIBRARY)
+    list(APPEND FFMPEG_LIBRARIES "${APPLE_SECURITY_LIBRARY}")
+  endif()
+
   find_library(APPLE_VDA_LIBRARY VideoDecodeAcceleration)
   if(APPLE_VDA_LIBRARY)
     list(APPEND FFMPEG_LIBRARIES "${APPLE_VDA_LIBRARY}")
   endif()
 
+  find_library(APPLE_VIDEOTOOLBOX_LIBRARY VideoToolbox)
+  if(APPLE_VIDEOTOOLBOX_LIBRARY)
+    list(APPEND FFMPEG_LIBRARIES "${APPLE_VIDEOTOOLBOX_LIBRARY}")
+  endif()
+
   find_library(APPLE_ICONV_LIBRARY iconv)
   if(APPLE_ICONV_LIBRARY)
     list(APPEND FFMPEG_LIBRARIES "${APPLE_ICONV_LIBRARY}")
@@ -108,6 +128,10 @@ if(APPLE)
   if(APPLE_BZ2_LIBRARY)
     list(APPEND FFMPEG_LIBRARIES "${APPLE_BZ2_LIBRARY}")
   endif()
+
+  mark_as_advanced(APPLE_AUDIOTOOLBOX_LIBRARY APPLE_COREMEDIA_LIBRARY
+    APPLE_COREVIDEO_LIBRARY APPLE_SECURITY_LIBRARY APPLE_VDA_LIBRARY
+    APPLE_VIDEOTOOLBOX_LIBRARY APPLE_ICONV_LIBRARY APPLE_BZ2_LIBRARY)
 endif()
 
 mark_as_advanced(FFMPEG_LIBRARY_DIR)

+ 12 - 0
cmake/modules/FindGTK3.cmake

@@ -0,0 +1,12 @@
+find_package(PkgConfig QUIET)
+
+set(__gtk3_required_version "${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION}")
+if(__gtk3_required_version)
+    set(__gtk3_required_version " >= ${__gtk3_required_version}")
+endif()
+pkg_check_modules(GTK3 QUIET "gtk+-3.0${__gtk3_required_version}" IMPORTED_TARGET)
+
+if (NOT TARGET PkgConfig::GTK3)
+    set(GTK3_FOUND 0)
+endif()
+unset(__gtk3_required_version)

+ 1 - 1
contrib/src/ai/aiBehaviors.h

@@ -151,7 +151,7 @@ PUBLISHED:
 
   void obstacle_avoidance(float feeler_length = 1.0);
 
-  void path_follow(float follow_wt);
+  void path_follow(float follow_wt = 1.0f);
   void add_to_path(LVecBase3 pos);
   void start_follow(std::string type = "normal");
 

+ 4 - 0
contrib/src/ai/aiCharacter.cxx

@@ -117,6 +117,10 @@ NodePath AICharacter::get_char_render() {
   return _window_render;
 }
 
+std::string AICharacter::get_name() {
+  return _name;
+}
+
 void AICharacter::set_pf_guide(bool pf_guide) {
   _pf_guide = pf_guide;
 }

+ 4 - 0
contrib/src/ai/aiCharacter.h

@@ -46,6 +46,8 @@ class EXPCL_PANDAAI AICharacter : public ReferenceCount {
   void set_char_render(NodePath render);
   NodePath get_char_render();
 
+  std::string get_name();
+
 PUBLISHED:
     double get_mass();
     void set_mass(double m);
@@ -65,6 +67,8 @@ PUBLISHED:
 
     explicit AICharacter(std::string model_name, NodePath model_np, double mass, double movt_force, double max_force);
     ~AICharacter();
+
+  MAKE_PROPERTY(name, get_name);
 };
 
 #endif

+ 7 - 0
contrib/src/contribbase/contribsymbols.h

@@ -29,4 +29,11 @@
   #define EXPTP_PANDAAI IMPORT_TEMPL
 #endif
 
+#ifdef BUILDING_PANDASPEEDTREE
+  #define EXPCL_PANDASPEEDTREE EXPORT_CLASS
+  #define EXPTP_PANDASPEEDTREE EXPORT_TEMPL
+#else
+  #define EXPCL_PANDASPEEDTREE IMPORT_CLASS
+  #define EXPTP_PANDASPEEDTREE IMPORT_TEMPL
+#endif
 #endif

+ 10 - 2
contrib/src/rplight/pssmCameraRig.cxx

@@ -369,8 +369,16 @@ void PSSMCameraRig::update(NodePath cam_node, const LVecBase3 &light_vector) {
   LMatrix4 transform = cam_node.get_transform()->get_mat();
 
   // Get Camera and Lens pointers
-  Camera* cam = DCAST(Camera, cam_node.get_child(0).node());
-  nassertv(cam != nullptr);
+  Camera *cam;
+  PandaNode *node = cam_node.node();
+  if (node->is_of_type(Camera::get_class_type())) {
+    cam = (Camera *)node;
+  }
+  else {
+    // Perhaps we passed in something like base.camera ?
+    cam = DCAST(Camera, cam_node.get_child(0).node());
+    nassertv(cam != nullptr);
+  }
   Lens* lens = cam->get_lens();
 
   // Extract near and far points:

+ 21 - 21
contrib/src/rplight/rpLight.I

@@ -148,7 +148,7 @@ inline void RPLight::assign_slot(int slot) {
  *   changed. This will cause all shadow sources to be updated, emitting a
  *   shadow update. Be careful when calling this method if you don't want all
  *   sources to get updated. If you only have to invalidate a single shadow source,
- *   use get_shadow_source(n)->set_needs_update(true).
+ *   use `get_shadow_source(n)->set_needs_update(true)`.
  */
 inline void RPLight::invalidate_shadows() {
   for (size_t i = 0; i < _shadow_sources.size(); ++i) {
@@ -296,14 +296,14 @@ inline bool RPLight::get_casts_shadows() const {
 }
 
 /**
- * @brief Sets the lights shadow map resolution
- * @details This sets the lights shadow map resolution. This has no effect
+ * @brief Sets the light's shadow map resolution
+ * @details This sets the light's shadow map resolution. This has no effect
  *   when the light is not told to cast shadows (Use RPLight::set_casts_shadows).
  *
- *   When calling this on a light with multiple shadow sources (e.g. PointLight),
- *   this controls the resolution of each source. If the light has 6 shadow sources,
- *   and you use a resolution of 512x512, the lights shadow map will occur a
- *   space of 6 * 512x512 maps in the shadow atlas.
+ *   When calling this on a light with multiple shadow sources (e.g.
+ *   RPPointLight), this controls the resolution of each source. If the light
+ *   has 6 shadow sources, and you use a resolution of 512x512, the light's
+ *   shadow map will occupy a space of 6 * 512x512 maps in the shadow atlas.
  *
  * @param resolution Resolution of the shadow map in pixels
  */
@@ -326,14 +326,14 @@ inline size_t RPLight::get_shadow_map_resolution() const {
 }
 
 /**
- * @brief Sets the ies profile
+ * @brief Sets the IES profile
  * @details This sets the ies profile of the light. The parameter should be a
  *   handle previously returned by RenderPipeline.load_ies_profile. Using a
  *   value of -1 indicates no ies profile.
  *
- *   Notice that for ies profiles which cover a whole range, you should use
- *   PointLights, whereas for ies profiles which only cover the lower hemisphere
- *   you should use SpotLights for the best performance.
+ *   Notice that for IES profiles which cover a whole range, you should use an
+ *   RPPointLight, whereas for ies profiles which only cover the lower
+ *   hemisphere you should use an RPSpotLight for the best performance.
  *
  * @param profile IES Profile handle
  */
@@ -343,8 +343,8 @@ inline void RPLight::set_ies_profile(int profile) {
 }
 
 /**
- * @brief Returns the lights ies profile
- * @details This returns the ies profile of a light, previously set with
+ * @brief Returns the light's IES profile
+ * @details This returns the IES profile of a light, previously set with
  *   RPLight::set_ies_profile. In case no ies profile was set, returns -1.
  *
  * @return IES Profile handle
@@ -354,20 +354,20 @@ inline int RPLight::get_ies_profile() const {
 }
 
 /**
- * @brief Returns whether the light has an ies profile assigned
- * @details This returns whether the light has an ies profile assigned,
+ * @brief Returns whether the light has an IES profile assigned
+ * @details This returns whether the light has an IES profile assigned,
  *   previously done with RPLight::set_ies_profile.
  *
- * @return true if the light has an ies profile assigned, false otherwise
+ * @return true if the light has an IES profile assigned, false otherwise
  */
 inline bool RPLight::has_ies_profile() const {
   return _ies_profile >= 0;
 }
 
 /**
- * @brief Clears the ies profile
- * @details This clears the ies profile of the light, telling it to no longer
- *   use an ies profile, and instead use the default attenuation.
+ * @brief Clears the IES profile
+ * @details This clears the IES profile of the light, telling it to no longer
+ *   use an IES profile, and instead use the default attenuation.
  */
 inline void RPLight::clear_ies_profile() {
   set_ies_profile(-1);
@@ -377,7 +377,7 @@ inline void RPLight::clear_ies_profile() {
  * @brief Sets the near plane of the light
  * @details This sets the near plane of all shadow sources of the light. It has
  *   no effects if the light does not cast shadows. This prevents artifacts from
- *   objects near to the light. It behaves like Lens::set_near_plane.
+ *   objects near to the light. It behaves like Lens::set_near().
  *
  *   It can also help increasing shadow map precision, low near planes will
  *   cause the precision to suffer. Try setting the near plane as big as possible.
@@ -394,7 +394,7 @@ inline void RPLight::set_near_plane(float near_plane) {
 
 /**
  * @brief Returns the near plane of the light
- * @details This returns the lights near plane, previously set with
+ * @details This returns the light's near plane, previously set with
  *   RPLight::set_near_plane. If the light does not cast shadows, this value
  *   is meaningless.
  *

+ 3 - 8
contrib/src/sceneeditor/SideWindow.py

@@ -7,14 +7,9 @@ from direct.tkwidgets.VectorWidgets import ColorEntry
 from direct.showbase.TkGlobal import spawnTkLoop
 import seSceneGraphExplorer
 
-import Pmw, sys
-
-if sys.version_info >= (3, 0):
-    from tkinter import Frame, IntVar, Checkbutton, Toplevel
-    import tkinter
-else:
-    from Tkinter import Frame, IntVar, Checkbutton, Toplevel
-    import Tkinter as tkinter
+import Pmw
+from tkinter import Frame, IntVar, Checkbutton, Toplevel
+import tkinter
 
 
 class sideWindow(AppShell):

+ 0 - 1
contrib/src/sceneeditor/collisionWindow.py

@@ -9,7 +9,6 @@ from seColorEntry import *
 from direct.tkwidgets import VectorWidgets
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Slider
-import string, math, types
 from panda3d.core import *
 
 

+ 3 - 8
contrib/src/sceneeditor/controllerWindow.py

@@ -4,14 +4,9 @@
 #################################################################
 
 from direct.tkwidgets.AppShell import AppShell
-import sys, Pmw
-
-if sys.version_info >= (3, 0):
-    from tkinter import Frame, Label, Button
-    import tkinter
-else:
-    from Tkinter import Frame, Label, Button
-    import Tkinter as tkinter
+import Pmw
+from tkinter import Frame, Label, Button
+import tkinter
 
 # Define the Category
 KEYBOARD = 'Keyboard-'

+ 1 - 7
contrib/src/sceneeditor/dataHolder.py

@@ -5,12 +5,7 @@ from direct.showbase.TkGlobal import*
 import Pmw
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Floater
-
-if sys.version_info >= (3, 0):
-    from tkinter.filedialog import askopenfilename
-else:
-    from tkFileDialog import askopenfilename
-
+from tkinter.filedialog import askopenfilename
 
 #############################
 # Scene Editor Python Files #
@@ -27,7 +22,6 @@ from direct.actor import Actor
 # Core Python Modules         #
 ###############################
 import os
-import string
 import sys
 
 import seParticleEffect

+ 3 - 7
contrib/src/sceneeditor/lightingPanel.py

@@ -7,15 +7,11 @@ from direct.tkwidgets.AppShell import AppShell
 from seColorEntry import *
 from direct.tkwidgets.VectorWidgets import Vector3Entry
 from direct.tkwidgets.Slider import Slider
-import sys, math, types, Pmw
+import Pmw
 from panda3d.core import *
 
-if sys.version_info >= (3, 0):
-    from tkinter import Frame, Button, Menubutton, Menu
-    import tkinter
-else:
-    from Tkinter import Frame, Button, Menubutton, Menu
-    import Tkinter as tkinter
+from tkinter import Frame, Button, Menubutton, Menu
+import tkinter
 
 
 class lightingPanel(AppShell):

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

@@ -10,7 +10,7 @@ from direct.showbase.ShowBaseGlobal import *
 from direct.interval.IntervalGlobal import *
 from direct.showbase.DirectObject import DirectObject
 from panda3d.core import *
-import math
+from direct.task import Task
 
 
 class ViewPort:

+ 2 - 12
contrib/src/sceneeditor/sceneEditor.py

@@ -8,13 +8,8 @@ from direct.showbase.ShowBase import ShowBase
 ShowBase()
 
 from direct.showbase.TkGlobal import spawnTkLoop
-
-if sys.version_info >= (3, 0):
-    from tkinter import *
-    from tkinter.filedialog import *
-else:
-    from Tkinter import *
-    from tkFileDialog import *
+from tkinter import *
+from tkinter.filedialog import *
 
 from direct.directtools.DirectGlobals import *
 from direct.tkwidgets.AppShell import*
@@ -36,17 +31,12 @@ from seBlendAnimPanel import *
 from controllerWindow import *
 from AlignTool import *
 
-
-
-import os
-import string
 from direct.tkwidgets import Dial
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Slider
 from direct.actor import Actor
 import seAnimPanel
 from direct.task import Task
-import math
 
 #################################################################
 # All scene and windows object will be stored in here.

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

@@ -5,15 +5,9 @@
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import *
 from direct.showbase.TkGlobal import *
-import string
-import math
-import types
 from direct.task import Task
 
-if sys.version_info >= (3, 0):
-    from tkinter.simpledialog import askfloat
-else:
-    from tkSimpleDialog import askfloat
+from tkinter.simpledialog import askfloat
 
 FRAMES = 0
 SECONDS = 1
@@ -407,7 +401,7 @@ class AnimPanel(AppShell):
         #################################################################
         if self.animName in self['animList']:
             # Convert scale value to float
-            frame = string.atof(frame)
+            frame = float(frame)
             # Now convert t to seconds for offset calculations
             if self.unitsVar.get() == FRAMES:
                 frame = frame / self.fps

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

@@ -5,15 +5,8 @@
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import *
 from direct.showbase.TkGlobal import *
-import string
-import math
-import types
 from direct.task import Task
-
-if sys.version_info >= (3, 0):
-    from tkinter.simpledialog import askfloat
-else:
-    from tkSimpleDialog import askfloat
+from tkinter.simpledialog import askfloat
 
 FRAMES = 0
 SECONDS = 1
@@ -431,7 +424,7 @@ class BlendAnimPanel(AppShell):
         #################################################################
         if (self.animNameA in self['animList'])and(self.animNameB in self['animList']):
             # Convert scale value to float
-            frame = string.atof(frame)
+            frame = float(frame)
             # Now convert t to seconds for offset calculations
             if self.unitsVar.get() == FRAMES:
                 frame = frame / self.fps

+ 1 - 0
contrib/src/sceneeditor/seCameraControl.py

@@ -16,6 +16,7 @@ from direct.directtools.DirectUtil import *
 from seGeometry import *
 from direct.directtools.DirectGlobals import *
 from direct.task import Task
+import math
 
 CAM_MOVE_DURATION = 1.2
 COA_MARKER_SF = 0.0075

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

@@ -12,13 +12,9 @@
 from direct.tkwidgets import Valuator
 from direct.tkwidgets import Floater
 from direct.tkwidgets import Slider
-import sys, Pmw
+import Pmw
 from direct.tkwidgets.VectorWidgets import VectorEntry
-
-if sys.version_info >= (3, 0):
-    from tkinter.colorchooser import askcolor
-else:
-    from tkColorChooser import askcolor
+from tkinter.colorchooser import askcolor
 
 
 class seColorEntry(VectorEntry):

+ 0 - 1
contrib/src/sceneeditor/seFileSaver.py

@@ -8,7 +8,6 @@ from panda3d.core import *
 from direct.showbase.ShowBaseGlobal import *
 import os
 import shutil
-import string
 
 ####################################################################################################################################################
 #### These modules are modified versions of Disney's equivalent modules

+ 1 - 0
contrib/src/sceneeditor/seGrid.py

@@ -14,6 +14,7 @@
 from direct.showbase.DirectObject import *
 from direct.directtools.DirectUtil import *
 from seGeometry import *
+import math
 
 class DirectGrid(NodePath,DirectObject):
     def __init__(self):

+ 0 - 1
contrib/src/sceneeditor/seLights.py

@@ -5,7 +5,6 @@
 from direct.showbase.DirectObject import *
 from direct.directtools import DirectUtil
 from panda3d.core import *
-import string
 
 
 class seLight(NodePath):

+ 3 - 2
contrib/src/sceneeditor/seManipulation.py

@@ -17,6 +17,7 @@ from direct.directtools.DirectGlobals import *
 from direct.directtools.DirectUtil import *
 from seGeometry import *
 from direct.task import Task
+import math
 
 class DirectManipulationControl(DirectObject):
     def __init__(self):
@@ -601,7 +602,7 @@ class ObjectHandles(NodePath,DirectObject):
         self.reparentTo(hidden)
 
     def enableHandles(self, handles):
-        if type(handles) == types.ListType:
+        if type(handles) is list:
             for handle in handles:
                 self.enableHandle(handle)
         elif handles == 'x':
@@ -642,7 +643,7 @@ class ObjectHandles(NodePath,DirectObject):
             self.zDiscGroup.reparentTo(self.zHandles)
 
     def disableHandles(self, handles):
-        if type(handles) == types.ListType:
+        if type(handles) is list:
             for handle in handles:
                 self.disableHandle(handle)
         elif handles == 'x':

+ 6 - 10
contrib/src/sceneeditor/seMopathRecorder.py

@@ -25,16 +25,12 @@ from direct.tkwidgets.Slider import Slider
 from direct.tkwidgets.EntryScale import EntryScale
 from direct.tkwidgets.VectorWidgets import Vector2Entry, Vector3Entry
 from direct.tkwidgets.VectorWidgets import ColorEntry
-import os, string, sys, Pmw
+import os, Pmw
+import math
 
-if sys.version_info >= (3, 0):
-    from tkinter import Button, Frame, Radiobutton, Checkbutton, Label
-    from tkinter import StringVar, BooleanVar, Entry, Scale
-    import tkinter
-else:
-    from Tkinter import Button, Frame, Radiobutton, Checkbutton, Label
-    from Tkinter import StringVar, BooleanVar, Entry, Scale
-    import Tkinter as tkinter
+from tkinter import Button, Frame, Radiobutton, Checkbutton, Label
+from tkinter import StringVar, BooleanVar, Entry, Scale
+import tkinter
 
 
 PRF_UTILITIES = [
@@ -374,7 +370,7 @@ class MopathRecorder(AppShell, DirectObject):
         self.speedEntry.bind(
             '<Return>',
             lambda e = None, s = self: s.setSpeedScale(
-            string.atof(s.speedVar.get())))
+            float(s.speedVar.get())))
         self.speedEntry.pack(side = tkinter.LEFT, expand = 0)
         frame.pack(fill = tkinter.X, expand = 1)
 

+ 3 - 8
contrib/src/sceneeditor/seParticlePanel.py

@@ -2,7 +2,7 @@
 
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import AppShell
-import os, Pmw, sys
+import os, Pmw
 from direct.tkwidgets.Dial import AngleDial
 from direct.tkwidgets.Floater import Floater
 from direct.tkwidgets.Slider import Slider
@@ -13,13 +13,8 @@ import seForceGroup
 import seParticles
 import seParticleEffect
 
-
-if sys.version_info >= (3, 0):
-    from tkinter.filedialog import *
-    from tkinter.simpledialog import askstring
-else:
-    from tkFileDialog import *
-    from tkSimpleDialog import askstring
+from tkinter.filedialog import *
+from tkinter.simpledialog import askstring
 
 
 class ParticlePanel(AppShell):

+ 0 - 1
contrib/src/sceneeditor/seParticles.py

@@ -3,7 +3,6 @@ from panda3d.physics import *
 from direct.particles.ParticleManagerGlobal import *
 from direct.showbase.PhysicsManagerGlobal import *
 #import OrientedParticleFactory
-import string
 import os
 from direct.directnotify import DirectNotifyGlobal
 import sys

+ 3 - 8
contrib/src/sceneeditor/sePlacer.py

@@ -6,14 +6,9 @@ from direct.tkwidgets.AppShell import AppShell
 from direct.tkwidgets.Dial import AngleDial
 from direct.tkwidgets.Floater import Floater
 from panda3d.core import *
-import sys, Pmw
-
-if sys.version_info >= (3, 0):
-    from tkinter import Button, Menubutton, Menu, StringVar
-    import tkinter
-else:
-    from Tkinter import Button, Menubutton, Menu, StringVar
-    import Tkinter as tkinter
+import Pmw
+from tkinter import Button, Menubutton, Menu, StringVar
+import tkinter
 
 """
 TODO:

+ 3 - 8
contrib/src/sceneeditor/seSceneGraphExplorer.py

@@ -11,14 +11,9 @@
 from direct.showbase.DirectObject import DirectObject
 from seTree import TreeNode, TreeItem
 
-import Pmw, sys
-
-if sys.version_info >= (3, 0):
-    from tkinter import IntVar, Frame, Label
-    import tkinter
-else:
-    from Tkinter import IntVar, Frame, Label
-    import Tkinter as tkinter
+import Pmw
+from tkinter import IntVar, Frame, Label
+import tkinter
 
 # changing these strings requires changing sceneEditor.py SGE_ strs too!
 # This list of items will be showed on the pop out window when user right click on

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

@@ -548,7 +548,7 @@ class SelectionRay(SelectionQueue):
         if xy:
             mx = xy[0]
             my = xy[1]
-        elif direct:
+        elif base.direct:
             mx = SEditor.dr.mouseX
             my = SEditor.dr.mouseY
         else:

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

@@ -20,9 +20,9 @@ from seGeometry import *
 from direct.tkpanels import Placer
 from direct.tkwidgets import Slider
 from direct.gui import OnscreenText
-import types
-import string
+from direct.task import Task
 from direct.showbase import Loader
+import math
 
 class SeSession(DirectObject):  ### Customized DirectSession
 

+ 4 - 8
contrib/src/sceneeditor/seTree.py

@@ -12,16 +12,12 @@
 #
 #################################################################
 
-import os, sys, string, Pmw
+import os, Pmw
 from direct.showbase.DirectObject import DirectObject
 from panda3d.core import *
 
-if sys.version_info >= (3, 0):
-    import tkinter
-    from tkinter import IntVar, Menu, PhotoImage, Label, Frame, Entry
-else:
-    import Tkinter as tkinter
-    from Tkinter import IntVar, Menu, PhotoImage, Label, Frame, Entry
+import tkinter
+from tkinter import IntVar, Menu, PhotoImage, Label, Frame, Entry
 
 # Initialize icon directory
 ICONDIR = getModelPath().findFile(Filename('icons')).toOsSpecific()
@@ -221,7 +217,7 @@ class TreeNode:
             self.children[key] = child
             self.kidKeys.append(key)
         # Remove unused children
-        for key in self.children.keys():
+        for key in list(self.children.keys()):
             if key not in self.kidKeys:
                 del(self.children[key])
         cx = x+20

+ 0 - 0
panda/src/speedtree/config_speedtree.cxx → contrib/src/speedtree/config_speedtree.cxx


+ 1 - 1
panda/src/speedtree/config_speedtree.h → contrib/src/speedtree/config_speedtree.h

@@ -14,7 +14,7 @@
 #ifndef CONFIG_SPEEDTREE_H
 #define CONFIG_SPEEDTREE_H
 
-#include "pandabase.h"
+#include "contribbase.h"
 #include "notifyCategoryProxy.h"
 #include "configVariableBool.h"
 #include "configVariableDouble.h"

+ 0 - 0
panda/src/speedtree/loaderFileTypeSrt.cxx → contrib/src/speedtree/loaderFileTypeSrt.cxx


+ 1 - 1
panda/src/speedtree/loaderFileTypeSrt.h → contrib/src/speedtree/loaderFileTypeSrt.h

@@ -14,7 +14,7 @@
 #ifndef LOADERFILETYPESRT_H
 #define LOADERFILETYPESRT_H
 
-#include "pandabase.h"
+#include "contribbase.h"
 
 #include "loaderFileType.h"
 

+ 0 - 0
panda/src/speedtree/loaderFileTypeStf.cxx → contrib/src/speedtree/loaderFileTypeStf.cxx


+ 1 - 1
panda/src/speedtree/loaderFileTypeStf.h → contrib/src/speedtree/loaderFileTypeStf.h

@@ -14,7 +14,7 @@
 #ifndef LOADERFILETYPESTF_H
 #define LOADERFILETYPESTF_H
 
-#include "pandabase.h"
+#include "contribbase.h"
 
 #include "loaderFileType.h"
 

+ 0 - 0
panda/src/speedtree/pandaspeedtree_composite1.cxx → contrib/src/speedtree/pandaspeedtree_composite1.cxx


+ 0 - 0
panda/src/speedtree/speedTreeNode.I → contrib/src/speedtree/speedTreeNode.I


+ 2 - 12
panda/src/speedtree/speedTreeNode.cxx → contrib/src/speedtree/speedTreeNode.cxx

@@ -11,7 +11,7 @@
  * @date 2009-03-13
  */
 
-#include "pandabase.h"
+#include "contribbase.h"
 #include "speedTreeNode.h"
 #include "stBasicTerrain.h"
 #include "virtualFileSystem.h"
@@ -1002,17 +1002,6 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   return true;
 }
 
-/**
- * Returns true if there is some value to visiting this particular node during
- * the cull traversal for any camera, false otherwise.  This will be used to
- * optimize the result of get_net_draw_show_mask(), so that any subtrees that
- * contain only nodes for which is_renderable() is false need not be visited.
- */
-bool SpeedTreeNode::
-is_renderable() const {
-  return true;
-}
-
 /**
  * Adds the node's contents to the CullResult we are building up during the
  * cull traversal, so that it will be drawn at render time.  For most nodes
@@ -1162,6 +1151,7 @@ set_transparent_texture_mode(SpeedTree::ETextureAlphaRenderMode eMode) const {
 void SpeedTreeNode::
 init_node() {
   PandaNode::set_cull_callback();
+  PandaNode::set_renderable();
 
   _is_valid = false;
   _needs_repopulate = false;

+ 1 - 2
panda/src/speedtree/speedTreeNode.h → contrib/src/speedtree/speedTreeNode.h

@@ -14,7 +14,7 @@
 #ifndef SPEEDTREENODE_H
 #define SPEEDTREENODE_H
 
-#include "pandabase.h"
+#include "contribbase.h"
 #include "pandaNode.h"
 #include "pointerTo.h"
 #include "stTree.h"
@@ -157,7 +157,6 @@ public:
                                          GeomTransformer &transformer);
 
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
-  virtual bool is_renderable() const;
   virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
 
   void prepare_scene(GraphicsStateGuardianBase *gsgbase, const RenderState *net_state);

+ 0 - 0
panda/src/speedtree/speedtree_api.cxx → contrib/src/speedtree/speedtree_api.cxx


+ 0 - 0
panda/src/speedtree/speedtree_api.h → contrib/src/speedtree/speedtree_api.h


+ 0 - 0
panda/src/speedtree/stBasicTerrain.I → contrib/src/speedtree/stBasicTerrain.I


+ 0 - 0
panda/src/speedtree/stBasicTerrain.cxx → contrib/src/speedtree/stBasicTerrain.cxx


+ 1 - 1
panda/src/speedtree/stBasicTerrain.h → contrib/src/speedtree/stBasicTerrain.h

@@ -14,7 +14,7 @@
 #ifndef STBASICTERRAIN_H
 #define STBASICTERRAIN_H
 
-#include "pandabase.h"
+#include "contribbase.h"
 #include "stTerrain.h"
 #include "luse.h"
 #include "pvector.h"

+ 0 - 0
panda/src/speedtree/stTerrain.I → contrib/src/speedtree/stTerrain.I


+ 0 - 0
panda/src/speedtree/stTerrain.cxx → contrib/src/speedtree/stTerrain.cxx


+ 1 - 1
panda/src/speedtree/stTerrain.h → contrib/src/speedtree/stTerrain.h

@@ -14,7 +14,7 @@
 #ifndef STTERRAIN_H
 #define STTERRAIN_H
 
-#include "pandabase.h"
+#include "contribbase.h"
 #include "typedReferenceCount.h"
 #include "namable.h"
 #include "geomVertexData.h"

+ 0 - 0
panda/src/speedtree/stTransform.I → contrib/src/speedtree/stTransform.I


+ 0 - 0
panda/src/speedtree/stTransform.cxx → contrib/src/speedtree/stTransform.cxx


+ 1 - 1
panda/src/speedtree/stTransform.h → contrib/src/speedtree/stTransform.h

@@ -14,7 +14,7 @@
 #ifndef STTRANSFORM_H
 #define STTRANSFORM_H
 
-#include "pandabase.h"
+#include "contribbase.h"
 #include "transformState.h"
 #include "speedtree_api.h"
 #include "deg_2_rad.h"

+ 0 - 0
panda/src/speedtree/stTree.I → contrib/src/speedtree/stTree.I


+ 0 - 0
panda/src/speedtree/stTree.cxx → contrib/src/speedtree/stTree.cxx


+ 1 - 1
panda/src/speedtree/stTree.h → contrib/src/speedtree/stTree.h

@@ -14,7 +14,7 @@
 #ifndef STTREE_H
 #define STTREE_H
 
-#include "pandabase.h"
+#include "contribbase.h"
 #include "typedReferenceCount.h"
 #include "namable.h"
 #include "speedtree_api.h"

+ 15 - 1
direct/CMakeLists.txt

@@ -53,7 +53,8 @@ if(HAVE_PYTHON)
   file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/pandac/PandaModules.py"
     "\"This module is deprecated.  Import from panda3d.core and other panda3d.* modules instead.\"
 
-print(\"Warning: pandac.PandaModules is deprecated, import from panda3d.core instead\")\n")
+if __debug__:
+    print(\"Warning: pandac.PandaModules is deprecated, import from panda3d.core instead\")\n")
 
   foreach(module ${ALL_INTERROGATE_MODULES})
     string(REGEX REPLACE "^.*\\." "" module_name "${module}")
@@ -66,6 +67,19 @@ except ImportError as err:
 ")
   endforeach()
 
+  file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/pandac/PandaModules.py"
+    "from direct.showbase import DConfig
+
+def get_config_showbase():
+    return DConfig
+
+def get_config_express():
+    return DConfig
+
+getConfigShowbase = get_config_showbase
+getConfigExpress = get_config_express
+")
+
   # Now install ourselves:
   install_python_package(pandac SOURCE "${CMAKE_CURRENT_BINARY_DIR}/pandac" LIB COMPONENT Direct)
 endif()

+ 80 - 77
direct/src/actor/Actor.py

@@ -1,4 +1,8 @@
-"""Actor module: contains the Actor class"""
+"""Actor module: contains the Actor class.
+
+See the :ref:`loading-actors-and-animations` page in the Programming Guide
+to learn more about loading animated models.
+"""
 
 __all__ = ['Actor']
 
@@ -7,7 +11,7 @@ from panda3d.core import Loader as PandaLoader
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.Loader import Loader
 from direct.directnotify import DirectNotifyGlobal
-
+import warnings
 
 class Actor(DirectObject, NodePath):
     """
@@ -159,11 +163,10 @@ class Actor(DirectObject, NodePath):
             #the actor for a few frames, otherwise it has no effect
             a.fixBounds()
         """
-        try:
-            self.Actor_initialized
+        if hasattr(self, 'Actor_initialized'):
             return
-        except:
-            self.Actor_initialized = 1
+
+        self.Actor_initialized = 1
 
         # initialize our NodePath essence
         NodePath.__init__(self)
@@ -248,9 +251,9 @@ class Actor(DirectObject, NodePath):
             # make sure we have models
             if models:
                 # do we have a dictionary of models?
-                if type(models) == dict:
+                if isinstance(models, dict):
                     # if this is a dictionary of dictionaries
-                    if type(models[next(iter(models))]) == dict:
+                    if isinstance(models[next(iter(models))], dict):
                         # then it must be a multipart actor w/LOD
                         self.setLODNode(node = lodNode)
                         # preserve numerical order for lod's
@@ -267,7 +270,7 @@ class Actor(DirectObject, NodePath):
                                                modelName, lodName, copy = copy,
                                                okMissing = okMissing)
                     # then if there is a dictionary of dictionaries of anims
-                    elif type(anims[next(iter(anims))]) == dict:
+                    elif isinstance(anims[next(iter(anims))], dict):
                         # then this is a multipart actor w/o LOD
                         for partName in models:
                             # pass in each part
@@ -293,10 +296,10 @@ class Actor(DirectObject, NodePath):
             if anims:
                 if len(anims) >= 1:
                     # if so, does it have a dictionary of dictionaries?
-                    if type(anims[next(iter(anims))]) == dict:
+                    if isinstance(anims[next(iter(anims))], dict):
                         # are the models a dict of dicts too?
-                        if type(models) == dict:
-                            if type(models[next(iter(models))]) == dict:
+                        if isinstance(models, dict):
+                            if isinstance(models[next(iter(models))], dict):
                                 # then we have a multi-part w/ LOD
                                 sortedKeys = list(models.keys())
                                 sortedKeys.sort()
@@ -309,7 +312,7 @@ class Actor(DirectObject, NodePath):
                                 # then it must be multi-part w/o LOD
                                 for partName in anims:
                                     self.loadAnims(anims[partName], partName)
-                    elif type(models) == dict:
+                    elif isinstance(models, dict):
                         # then we have single-part w/ LOD
                         sortedKeys = list(models.keys())
                         sortedKeys.sort()
@@ -340,50 +343,46 @@ class Actor(DirectObject, NodePath):
             self.__geomNode.node().setFinal(1)
 
     def delete(self):
-        try:
-            self.Actor_deleted
-            return
-        except:
+        if not hasattr(self, 'Actor_deleted'):
             self.Actor_deleted = 1
             self.cleanup()
 
     def copyActor(self, other, overwrite=False):
-            # act like a copy constructor
-            self.gotName = other.gotName
-
-            # copy the scene graph elements of other
-            if overwrite:
-                otherCopy = other.copyTo(NodePath())
-                otherCopy.detachNode()
-                # assign these elements to ourselve (overwrite)
-                self.assign(otherCopy)
-            else:
-                # just copy these to ourselves
-                otherCopy = other.copyTo(self)
-            # masad: check if otherCopy has a geomNode as its first child
-            # if actor is initialized with flattenable, then otherCopy, not
-            # its first child, is the geom node; check __init__, for reference
-            if other.getGeomNode().getName() == other.getName():
-                self.setGeomNode(otherCopy)
-            else:
-                self.setGeomNode(otherCopy.getChild(0))
-
-            # copy the switches for lods
-            self.switches = other.switches
-            self.__LODNode = self.find('**/+LODNode')
-            self.__hasLOD = 0
-            if not self.__LODNode.isEmpty():
-                self.__hasLOD = 1
+        # act like a copy constructor
+        self.gotName = other.gotName
+
+        # copy the scene graph elements of other
+        if overwrite:
+            otherCopy = other.copyTo(NodePath())
+            otherCopy.detachNode()
+            # assign these elements to ourselve (overwrite)
+            self.assign(otherCopy)
+        else:
+            # just copy these to ourselves
+            otherCopy = other.copyTo(self)
+        # masad: check if otherCopy has a geomNode as its first child
+        # if actor is initialized with flattenable, then otherCopy, not
+        # its first child, is the geom node; check __init__, for reference
+        if other.getGeomNode().getName() == other.getName():
+            self.setGeomNode(otherCopy)
+        else:
+            self.setGeomNode(otherCopy.getChild(0))
 
+        # copy the switches for lods
+        self.switches = other.switches
+        self.__LODNode = self.find('**/+LODNode')
+        self.__hasLOD = 0
+        if not self.__LODNode.isEmpty():
+            self.__hasLOD = 1
 
-            # copy the part dictionary from other
-            self.__copyPartBundles(other)
-            self.__copySubpartDict(other)
-            self.__subpartsComplete = other.__subpartsComplete
 
-            # copy the anim dictionary from other
-            self.__copyAnimControls(other)
+        # copy the part dictionary from other
+        self.__copyPartBundles(other)
+        self.__copySubpartDict(other)
+        self.__subpartsComplete = other.__subpartsComplete
 
+        # copy the anim dictionary from other
+        self.__copyAnimControls(other)
 
     def __cmp__(self, other):
         # Actor inherits from NodePath, which inherits a definition of
@@ -510,7 +509,7 @@ class Actor(DirectObject, NodePath):
         self.stop(None)
         self.clearPythonData()
         self.flush()
-        if(self.__geomNode):
+        if self.__geomNode:
             self.__geomNode.removeNode()
             self.__geomNode = None
         if not self.isEmpty():
@@ -593,12 +592,10 @@ class Actor(DirectObject, NodePath):
                         'l':1,
                         'f':0}
 
-                """
-                sx = smap.get(x[0], None)
-
-                if sx is None:
-                    self.notify.error('Invalid lodName: %s' % x)
-                """
+                #sx = smap.get(x[0], None)
+                #
+                #if sx is None:
+                #    self.notify.error('Invalid lodName: %s' % x)
                 return smap[x[0]]
             else:
                 return int(x)
@@ -1467,7 +1464,7 @@ class Actor(DirectObject, NodePath):
             #iterate through for a specific part
             for lodData in self.__partBundleDict.values():
                 partData = lodData.get(partName)
-                if(partData):
+                if partData:
                     char = partData.partBundleNP
                     char.node().update()
                     geomNodes = char.findAllMatches("**/+GeomNode")
@@ -1655,6 +1652,8 @@ class Actor(DirectObject, NodePath):
 
         This method is deprecated.  You should use setBlend() instead.
         """
+        if __debug__:
+            warnings.warn("This method is deprecated.  You should use setBlend() instead.", DeprecationWarning, stacklevel=2)
         self.setBlend(animBlend = True, blendType = blendType, partName = partName)
 
     def disableBlend(self, partName = None):
@@ -1664,6 +1663,8 @@ class Actor(DirectObject, NodePath):
 
         This method is deprecated.  You should use setBlend() instead.
         """
+        if __debug__:
+            warnings.warn("This method is deprecated.  You should use setBlend() instead.", DeprecationWarning, stacklevel=2)
         self.setBlend(animBlend = False, partName = partName)
 
     def setControlEffect(self, animName, effect,
@@ -1689,11 +1690,20 @@ class Actor(DirectObject, NodePath):
         else:
             lodName = 'lodRoot'
 
-        try:
-            return self.__animControlDict[lodName][partName][animName].filename
-        except:
+        partDict = self.__animControlDict.get(lodName)
+        if partDict is None:
             return None
 
+        animDict = partDict.get(partName)
+        if animDict is None:
+            return None
+
+        anim = animDict.get(animName)
+        if anim is None:
+            return None
+
+        return anim.filename
+
     def getAnimControl(self, animName, partName=None, lodName=None,
                        allowAsyncBind = True):
         """
@@ -1727,7 +1737,6 @@ class Actor(DirectObject, NodePath):
             if anim is None:
                 # anim was not present
                 assert Actor.notify.debug("couldn't find anim: %s" % (animName))
-                pass
             else:
                 # bind the animation first if we need to
                 if not anim.animControl:
@@ -1848,7 +1857,6 @@ class Actor(DirectObject, NodePath):
                         if anim is None:
                             # anim was not present
                             assert Actor.notify.debug("couldn't find anim: %s" % (animName))
-                            pass
                         else:
                             # bind the animation first if we need to
                             animControl = anim.animControl
@@ -1983,7 +1991,7 @@ class Actor(DirectObject, NodePath):
 
         node = bundleNP.node()
         # A model loaded from disk will always have just one bundle.
-        assert(node.getNumBundles() == 1)
+        assert node.getNumBundles() == 1
         bundleHandle = node.getBundleHandle(0)
 
         if self.mergeLODBundles:
@@ -1999,7 +2007,7 @@ class Actor(DirectObject, NodePath):
         bundleDict[partName] = Actor.PartDef(bundleNP, bundleHandle, partModel)
 
 
-    def makeSubpart(self, partName, includeJoints, excludeJoints = [],
+    def makeSubpart(self, partName, includeJoints, excludeJoints = (),
                     parent="modelRoot", overlapping = False):
 
         """Defines a new "part" of the Actor that corresponds to the
@@ -2124,10 +2132,7 @@ class Actor(DirectObject, NodePath):
             lodNames = ['common']
         elif lodName == 'all':
             reload = False
-            lodNames = list(self.switches.keys())
-            lodNames.sort()
-            for i in range(0, len(lodNames)):
-                lodNames[i] = str(lodNames[i])
+            lodNames = list(map(str, sorted(self.switches.keys())))
         else:
             lodNames = [lodName]
 
@@ -2136,11 +2141,10 @@ class Actor(DirectObject, NodePath):
 
         firstLoad = True
         if not reload:
-            try:
-                self.__animControlDict[lodNames[0]][partName]
+            if lodNames[0] in self.__animControlDict and \
+               partName in self.__animControlDict[lodNames[0]]:
                 firstLoad = False
-            except:
-                pass
+
         for lName in lodNames:
             if firstLoad:
                 self.__animControlDict.setdefault(lName, {})
@@ -2434,7 +2438,7 @@ class Actor(DirectObject, NodePath):
         bundles in our part bundle dict that have matching names, and
         store the resulting anim controls in our own part bundle dict"""
 
-        assert(other.mergeLODBundles == self.mergeLODBundles)
+        assert other.mergeLODBundles == self.mergeLODBundles
 
         for lodName in other.__animControlDict:
             self.__animControlDict[lodName] = {}
@@ -2505,11 +2509,10 @@ class Actor(DirectObject, NodePath):
         for lodName, animList in self.getAnimBlends(animName, partName, lodName):
             print('LOD %s:' % (lodName))
             for animName, blendList in animList:
-
-                list = []
+                strings = []
                 for partName, effect in blendList:
-                    list.append('%s:%.3f' % (partName, effect))
-                print('  %s: %s' % (animName, ', '.join(list)))
+                    strings.append('%s:%.3f' % (partName, effect))
+                print('  %s: %s' % (animName, ', '.join(strings)))
 
     def osdAnimBlends(self, animName=None, partName=None, lodName=None):
         if not onScreenDebug.enabled:

+ 3 - 7
direct/src/actor/DistributedActor.py

@@ -8,9 +8,7 @@ from . import Actor
 
 class DistributedActor(DistributedNode.DistributedNode, Actor.Actor):
     def __init__(self, cr):
-        try:
-            self.DistributedActor_initialized
-        except:
+        if not hasattr(self, 'DistributedActor_initialized'):
             self.DistributedActor_initialized = 1
             Actor.Actor.__init__(self)
             DistributedNode.DistributedNode.__init__(self, cr)
@@ -20,14 +18,12 @@ class DistributedActor(DistributedNode.DistributedNode, Actor.Actor):
 
     def disable(self):
         # remove all anims, on all parts and all lods
-        if (not self.isEmpty()):
+        if not self.isEmpty():
             Actor.Actor.unloadAnims(self, None, None, None)
         DistributedNode.DistributedNode.disable(self)
 
     def delete(self):
-        try:
-            self.DistributedActor_deleted
-        except:
+        if not hasattr(self, 'DistributedActor_deleted'):
             self.DistributedActor_deleted = 1
             DistributedNode.DistributedNode.delete(self)
             Actor.Actor.delete(self)

+ 3 - 0
direct/src/actor/__init__.py

@@ -4,4 +4,7 @@ distributed variant thereof.  Actor is a high-level interface around
 the lower-level :class:`panda3d.core.Character` implementation.
 It loads and controls an animated character and manages the animations
 playing on it.
+
+See the :ref:`loading-actors-and-animations` page in the Programming Guide
+to learn more about loading animated models.
 """

+ 39 - 43
direct/src/cluster/ClusterClient.py

@@ -6,6 +6,7 @@ from .ClusterConfig import *
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 import os
 
 
@@ -70,7 +71,7 @@ class ClusterClient(DirectObject.DirectObject):
             server = DisplayConnection(
                 self.qcm, serverConfig.serverName,
                 serverConfig.serverMsgPort, self.msgHandler)
-            if server == None:
+            if server is None:
                 self.notify.error('Could not open %s on %s port %d' %
                                   (serverConfig.serverConfigName,
                                    serverConfig.serverName,
@@ -121,9 +122,10 @@ class ClusterClient(DirectObject.DirectObject):
         taskMgr.add(self.synchronizeTimeTask, "synchronizeTimeTask", -40)
 
     def synchronizeTimeTask(self, task):
-        frameCount = globalClock.getFrameCount()
-        frameTime = globalClock.getFrameTime()
-        dt = globalClock.getDt()
+        clock = ClockObject.getGlobalClock()
+        frameCount = clock.getFrameCount()
+        frameTime = clock.getFrameTime()
+        dt = clock.dt
         for server in self.serverList:
             server.sendTimeData(frameCount, frameTime, dt)
         return Task.cont
@@ -138,15 +140,14 @@ class ClusterClient(DirectObject.DirectObject):
             object     = pair[1]
             name       = self.controlMappings[object][0]
             serverList = self.controlMappings[object][1]
-            if (object in self.objectMappings):
+            if object in self.objectMappings:
                 self.moveObject(self.objectMappings[object],name,serverList,
                                 self.controlOffsets[object], self.objectHasColor[object])
         self.sendNamedMovementDone()
         return Task.cont
 
     def sendNamedMovementDone(self, serverList = None):
-
-        if (serverList == None):
+        if serverList is None:
             serverList = range(len(self.serverList))
 
         for server in serverList:
@@ -155,7 +156,6 @@ class ClusterClient(DirectObject.DirectObject):
 
 
     def redoSortedPriorities(self):
-
         self.sortedControlMappings = []
         for key in self.controlMappings:
             self.sortedControlMappings.append([self.controlPriorities[key],
@@ -169,7 +169,7 @@ class ClusterClient(DirectObject.DirectObject):
         hpr = nodePath.getHpr(render)
         scale = nodePath.getScale(render)
         hidden = nodePath.isHidden()
-        if (hasColor):
+        if hasColor:
             color = nodePath.getColor()
         else:
             color = [1,1,1,1]
@@ -193,7 +193,7 @@ class ClusterClient(DirectObject.DirectObject):
 
     def moveSelectedTask(self, state):
         # Update cluster if current display is a cluster client
-        if (last is not None):
+        if last is not None:
             self.notify.debug('moving selected node path')
             xyz = Point3(0)
             hpr = VBase3(0)
@@ -204,24 +204,24 @@ class ClusterClient(DirectObject.DirectObject):
         return Task.cont
 
 
-    def addNamedObjectMapping(self,object,name,hasColor = True):
-        if (name not in self.objectMappings):
+    def addNamedObjectMapping(self, object, name, hasColor = True):
+        if name not in self.objectMappings:
             self.objectMappings[name] = object
             self.objectHasColor[name] = hasColor
         else:
             self.notify.debug('attempt to add duplicate named object: '+name)
 
     def removeObjectMapping(self,name):
-        if (name in self.objectMappings):
+        if name in self.objectMappings:
             self.objectMappings.pop(name)
 
 
-    def addControlMapping(self,objectName,controlledName, serverList = None,
+    def addControlMapping(self, objectName, controlledName, serverList = None,
                           offset = None, priority = 0):
-        if (objectName not in self.controlMappings):
-            if (serverList == None):
+        if objectName not in self.controlMappings:
+            if serverList is None:
                 serverList = range(len(self.serverList))
-            if (offset == None):
+            if offset is None:
                 offset = Vec3(0,0,0)
 
             self.controlMappings[objectName] = [controlledName,serverList]
@@ -233,30 +233,29 @@ class ClusterClient(DirectObject.DirectObject):
             for item in oldList:
                 mergedList.append(item)
             for item in serverList:
-                if (item not in mergedList):
+                if item not in mergedList:
                     mergedList.append(item)
 
         self.redoSortedPriorities()
             #self.notify.debug('attempt to add duplicate controlled object: '+name)
 
-    def setControlMappingOffset(self,objectName,offset):
-        if (objectName in self.controlMappings):
+    def setControlMappingOffset(self, objectName, offset):
+        if objectName in self.controlMappings:
             self.controlOffsets[objectName] = offset
 
-    def removeControlMapping(self,name, serverList = None):
-        if (name in self.controlMappings):
-
-            if (serverList == None):
+    def removeControlMapping(self, name, serverList = None):
+        if name in self.controlMappings:
+            if serverList is None:
                 self.controlMappings.pop(name)
                 self.controlPriorities.pop(name)
             else:
-                list = self.controlMappings[key][1]
+                oldList = self.controlMappings[key][1]
                 newList = []
-                for server in list:
-                    if (server not in serverList):
+                for server in oldList:
+                    if server not in serverList:
                         newList.append(server)
                 self.controlMappings[key][1] = newList
-                if (len(newList) == 0):
+                if len(newList) == 0:
                     self.controlMappings.pop(name)
                     self.controlPriorities.pop(name)
         self.redoSortedPriorities()
@@ -303,7 +302,7 @@ class ClusterClient(DirectObject.DirectObject):
             tag = self.taggedObjects[name]
             function = tag["selectFunction"]
             args     = tag["selectArgs"]
-            if (function != None):
+            if function is not None:
                 function(*args)
         else:
             self(self.getNodePathFindCmd(nodePath) + '.select()', 0)
@@ -315,7 +314,7 @@ class ClusterClient(DirectObject.DirectObject):
             tag = self.taggedObjects[name]
             function = tag["deselectFunction"]
             args     = tag["deselectArgs"]
-            if (function != None):
+            if function is not None:
                 function(*args)
             self.startMoveSelectedTask()
         self(self.getNodePathFindCmd(nodePath) + '.deselect()', 0)
@@ -348,24 +347,23 @@ class ClusterClient(DirectObject.DirectObject):
 
 
     def handleDatagram(self,dgi,type,server):
-        if (type == CLUSTER_NONE):
+        if type == CLUSTER_NONE:
             pass
-        elif (type == CLUSTER_NAMED_OBJECT_MOVEMENT):
+        elif type == CLUSTER_NAMED_OBJECT_MOVEMENT:
             self.serverQueues[server].append(self.msgHandler.parseNamedMovementDatagram(dgi))
             #self.handleNamedMovement(dgi)
         # when we recieve a 'named movement done' packet from a server we handle
         # all of its messages
-        elif (type == CLUSTER_NAMED_MOVEMENT_DONE):
+        elif type == CLUSTER_NAMED_MOVEMENT_DONE:
             self.handleMessageQueue(server)
         else:
             self.notify.warning("Received unsupported packet type:" % type)
         return type
 
     def handleMessageQueue(self,server):
-
-        list = self.serverQueues[server]
+        queue = self.serverQueues[server]
         # handle all messages in the queue
-        for data in list:
+        for data in queue:
             #print dgi
             self.handleNamedMovement(data)
 
@@ -378,14 +376,14 @@ class ClusterClient(DirectObject.DirectObject):
 
         (name,x, y, z, h, p, r, sx, sy, sz,red,g,b,a, hidden) = data
         #print "name"
-        #if (name == "camNode"):
+        #if name == "camNode":
         #    print x,y,z,h,p,r, sx, sy, sz,red,g,b,a, hidden
-        if (name in self.objectMappings):
+        if name in self.objectMappings:
             self.objectMappings[name].setPosHpr(render, x, y, z, h, p, r)
             self.objectMappings[name].setScale(render,sx,sy,sz)
-            if (self.objectHasColor[name]):
+            if self.objectHasColor[name]:
                 self.objectMappings[name].setColor(red,g,b,a)
-            if (hidden):
+            if hidden:
                 self.objectMappings[name].hide()
             else:
                 self.objectMappings[name].show()
@@ -451,7 +449,7 @@ class DisplayConnection:
         self.tcpConn = qcm.openTCPClientConnection(
             serverName, port, gameServerTimeoutMs)
         # Test for bad connection
-        if self.tcpConn == None:
+        if self.tcpConn is None:
             return None
         else:
             self.tcpConn.setNoDelay(1)
@@ -680,5 +678,3 @@ class DummyClusterClient(DirectObject.DirectObject):
         if fLocally:
             # Execute locally
             exec(commandString, __builtins__)
-
-

+ 0 - 1
direct/src/cluster/ClusterConfig.py

@@ -165,4 +165,3 @@ ClientConfigs = {
                               }
                              ],
     }
-

+ 10 - 19
direct/src/cluster/ClusterMsgs.py

@@ -59,17 +59,17 @@ class ClusterMsgHandler:
         if qcr.dataAvailable():
             datagram = NetDatagram()
             if qcr.getData(datagram):
-                (dgi, type) = self.readHeader(datagram)
+                (dgi, dtype) = self.readHeader(datagram)
             else:
                 dgi = None
-                type = CLUSTER_NONE
+                dtype = CLUSTER_NONE
                 self.notify.warning("getData returned false")
         else:
             datagram = None
             dgi = None
-            type = CLUSTER_NONE
+            dtype = CLUSTER_NONE
         # Note, return datagram to keep a handle on the data
-        return (datagram, dgi, type)
+        return (datagram, dgi, dtype)
 
     def blockingRead(self, qcr):
         """
@@ -85,19 +85,19 @@ class ClusterMsgHandler:
         # Data is available, create a datagram iterator
         datagram = NetDatagram()
         if qcr.getData(datagram):
-            (dgi, type) = self.readHeader(datagram)
+            (dgi, dtype) = self.readHeader(datagram)
         else:
-            (dgi, type) = (None, CLUSTER_NONE)
+            (dgi, dtype) = (None, CLUSTER_NONE)
             self.notify.warning("getData returned false")
         # Note, return datagram to keep a handle on the data
-        return (datagram, dgi, type)
+        return (datagram, dgi, dtype)
 
     def readHeader(self, datagram):
         dgi = PyDatagramIterator(datagram)
         number = dgi.getUint32()
-        type = dgi.getUint8()
-        self.notify.debug("Packet %d type %d received" % (number, type))
-        return (dgi, type)
+        dtype = dgi.getUint8()
+        self.notify.debug("Packet %d type %d received" % (number, dtype))
+        return (dgi, dtype)
 
     def makeCamOffsetDatagram(self, xyz, hpr):
         datagram = PyDatagram()
@@ -298,12 +298,3 @@ class ClusterMsgHandler:
         dt=dgi.getFloat32()
         self.notify.debug('time data=%f %f' % (frameTime, dt))
         return (frameCount, frameTime, dt)
-
-
-
-
-
-
-
-
-

+ 33 - 34
direct/src/cluster/ClusterServer.py

@@ -4,6 +4,7 @@ from direct.distributed.MsgTypes import *
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 
 # NOTE: This assumes the following variables are set via bootstrap command line
 # arguments on server startup:
@@ -51,7 +52,7 @@ class ClusterServer(DirectObject.DirectObject):
             self.startSwapCoordinator()
             base.graphicsEngine.setAutoFlip(0)
         # Set global clock mode to slave mode
-        globalClock.setMode(ClockObject.MSlave)
+        ClockObject.getGlobalClock().setMode(ClockObject.MSlave)
         # Send verification of startup to client
         self.daemon = DirectD()
 
@@ -100,16 +101,16 @@ class ClusterServer(DirectObject.DirectObject):
         return Task.cont
 
 
-    def addNamedObjectMapping(self,object,name,hasColor = True,
+    def addNamedObjectMapping(self, object, name, hasColor = True,
                               priority = 0):
-        if (name not in self.objectMappings):
+        if name not in self.objectMappings:
             self.objectMappings[name] = object
             self.objectHasColor[name] = hasColor
         else:
             self.notify.debug('attempt to add duplicate named object: '+name)
 
-    def removeObjectMapping(self,name):
-        if (name in self.objectMappings):
+    def removeObjectMapping(self, name):
+        if name in self.objectMappings:
             self.objectMappings.pop(name)
 
 
@@ -123,11 +124,11 @@ class ClusterServer(DirectObject.DirectObject):
         self.sortedControlMappings.sort()
 
 
-    def addControlMapping(self,objectName,controlledName, offset = None,
+    def addControlMapping(self, objectName, controlledName, offset = None,
                           priority = 0):
-        if (objectName not in self.controlMappings):
+        if objectName not in self.controlMappings:
             self.controlMappings[objectName] = controlledName
-            if (offset == None):
+            if offset is None:
                 offset = Vec3(0,0,0)
             self.controlOffsets[objectName]  = offset
             self.controlPriorities[objectName] = priority
@@ -135,13 +136,13 @@ class ClusterServer(DirectObject.DirectObject):
         else:
             self.notify.debug('attempt to add duplicate controlled object: '+name)
 
-    def setControlMappingOffset(self,objectName,offset):
-        if (objectName in self.controlMappings):
+    def setControlMappingOffset(self, objectName, offset):
+        if objectName in self.controlMappings:
             self.controlOffsets[objectName] = offset
 
 
-    def removeControlMapping(self,name):
-        if (name in self.controlMappings):
+    def removeControlMapping(self, name):
+        if name in self.controlMappings:
             self.controlMappings.pop(name)
             self.controlPriorities.pop(name)
         self.redoSortedPriorities()
@@ -156,7 +157,7 @@ class ClusterServer(DirectObject.DirectObject):
         for pair in self.sortedControlPriorities:
             object = pair[1]
             name   = self.controlMappings[object]
-            if (object in self.objectMappings):
+            if object in self.objectMappings:
                 self.moveObject(self.objectMappings[object],name,self.controlOffsets[object],
                                 self.objectHasColor[object])
 
@@ -165,7 +166,6 @@ class ClusterServer(DirectObject.DirectObject):
 
 
     def sendNamedMovementDone(self):
-
         self.notify.debug("named movement done")
         datagram = self.msgHandler.makeNamedMovementDone()
         self.cw.send(datagram,self.lastConnection)
@@ -176,7 +176,7 @@ class ClusterServer(DirectObject.DirectObject):
         xyz = nodePath.getPos(render) + offset
         hpr = nodePath.getHpr(render)
         scale = nodePath.getScale(render)
-        if (hasColor):
+        if hasColor:
             color = nodePath.getColor()
         else:
             color = [1,1,1,1]
@@ -243,34 +243,34 @@ class ClusterServer(DirectObject.DirectObject):
 
     def handleDatagram(self, dgi, type):
         """ Process a datagram depending upon type flag """
-        if (type == CLUSTER_NONE):
+        if type == CLUSTER_NONE:
             pass
-        elif (type == CLUSTER_EXIT):
+        elif type == CLUSTER_EXIT:
             print('GOT EXIT')
             import sys
             sys.exit()
-        elif (type == CLUSTER_CAM_OFFSET):
+        elif type == CLUSTER_CAM_OFFSET:
             self.handleCamOffset(dgi)
-        elif (type == CLUSTER_CAM_FRUSTUM):
+        elif type == CLUSTER_CAM_FRUSTUM:
             self.handleCamFrustum(dgi)
-        elif (type == CLUSTER_CAM_MOVEMENT):
+        elif type == CLUSTER_CAM_MOVEMENT:
             self.handleCamMovement(dgi)
-        elif (type == CLUSTER_SELECTED_MOVEMENT):
+        elif type == CLUSTER_SELECTED_MOVEMENT:
             self.handleSelectedMovement(dgi)
-        elif (type == CLUSTER_COMMAND_STRING):
+        elif type == CLUSTER_COMMAND_STRING:
             self.handleCommandString(dgi)
-        elif (type == CLUSTER_SWAP_READY):
+        elif type == CLUSTER_SWAP_READY:
             pass
-        elif (type == CLUSTER_SWAP_NOW):
+        elif type == CLUSTER_SWAP_NOW:
             self.notify.debug('swapping')
             base.graphicsEngine.flipFrame()
-        elif (type == CLUSTER_TIME_DATA):
+        elif type == CLUSTER_TIME_DATA:
             self.notify.debug('time data')
             self.handleTimeData(dgi)
-        elif (type == CLUSTER_NAMED_OBJECT_MOVEMENT):
+        elif type == CLUSTER_NAMED_OBJECT_MOVEMENT:
             self.messageQueue.append(self.msgHandler.parseNamedMovementDatagram(dgi))
             #self.handleNamedMovement(dgi)
-        elif (type == CLUSTER_NAMED_MOVEMENT_DONE):
+        elif type == CLUSTER_NAMED_MOVEMENT_DONE:
             #print "got done",self.messageQueue
             #if (len(self.messageQueue) > 0):
             #    print self.messageQueue[0]
@@ -297,11 +297,11 @@ class ClusterServer(DirectObject.DirectObject):
     def handleNamedMovement(self, data):
         """ Update cameraJig position to reflect latest position """
         (name,x, y, z, h, p, r,sx,sy,sz, red, g, b, a, hidden) = data
-        if (name in self.objectMappings):
+        if name in self.objectMappings:
             self.objectMappings[name].setPosHpr(render, x, y, z, h, p, r)
             self.objectMappings[name].setScale(render,sx,sy,sz)
             self.objectMappings[name].setColor(red,g,b,a)
-            if (hidden):
+            if hidden:
                 self.objectMappings[name].hide()
             else:
                 self.objectMappings[name].show()
@@ -335,9 +335,10 @@ class ClusterServer(DirectObject.DirectObject):
         """ Update cameraJig position to reflect latest position """
         (frameCount, frameTime, dt) = self.msgHandler.parseTimeDataDatagram(dgi)
         # Use frame time from client for both real and frame time
-        globalClock.setFrameCount(frameCount)
-        globalClock.setFrameTime(frameTime)
-        globalClock.setDt(dt)
+        clock = ClockObject.getGlobalClock()
+        clock.setFrameCount(frameCount)
+        clock.setFrameTime(frameTime)
+        clock.dt = dt
 
     def handleCommandString(self, dgi):
         """ Handle arbitrary command string from client """
@@ -346,5 +347,3 @@ class ClusterServer(DirectObject.DirectObject):
             exec(command, __builtins__)
         except:
             pass
-
-

+ 2 - 148
direct/src/controls/BattleWalker.py

@@ -1,5 +1,6 @@
 
 from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
 from panda3d.core import *
 from . import GravityWalker
@@ -54,7 +55,7 @@ class BattleWalker(GravityWalker.GravityWalker):
 
         debugRunning = inputState.isSet("debugRunning")
 
-        if(debugRunning):
+        if debugRunning:
             self.speed*=base.debugRunningMultiplier
             self.slideSpeed*=base.debugRunningMultiplier
             self.rotationSpeed*=1.25
@@ -139,150 +140,3 @@ class BattleWalker(GravityWalker.GravityWalker):
         if self.moving or jump:
             messenger.send("avatarMoving")
         return Task.cont
-
-    if 0:
-        def handleAvatarControls(self, task):
-            # If targetNp is not available, revert back to GravityWalker.handleAvatarControls.
-            # This situation occurs when the target dies, but we aren't switched out of
-            # battle walker control mode.
-
-            targetNp = self.avatarNodePath.currentTarget
-            if not BattleStrafe or targetNp == None or targetNp.isEmpty():
-                return GravityWalker.GravityWalker.handleAvatarControls(self, task)
-
-            # get the button states:
-            run = inputState.isSet("run")
-            forward = inputState.isSet("forward")
-            reverse = inputState.isSet("reverse")
-            turnLeft = inputState.isSet("turnLeft")
-            turnRight = inputState.isSet("turnRight")
-            slide = inputState.isSet("slide")
-            jump = inputState.isSet("jump")
-            # Determine what the speeds are based on the buttons:
-            self.advanceSpeed=(forward and self.avatarControlForwardSpeed or
-                               reverse and -self.avatarControlReverseSpeed)
-            if run and self.advanceSpeed>0.0:
-                self.advanceSpeed*=2.0 #*#
-            # Should fSlide be renamed slideButton?
-            self.slideSpeed=.15*(turnLeft and -self.avatarControlForwardSpeed or
-                                 turnRight and self.avatarControlForwardSpeed)
-            print('slideSpeed: %s' % self.slideSpeed)
-            self.rotationSpeed=0
-            self.speed=0
-
-            debugRunning = inputState.isSet("debugRunning")
-            if debugRunning:
-                self.advanceSpeed*=4.0
-                self.slideSpeed*=4.0
-                self.rotationSpeed*=1.25
-
-            if self.needToDeltaPos:
-                self.setPriorParentVector()
-                self.needToDeltaPos = 0
-            if self.wantDebugIndicator:
-                self.displayDebugInfo()
-            if self.lifter.isOnGround():
-                if self.isAirborne:
-                    self.isAirborne = 0
-                    assert self.debugPrint("isAirborne 0 due to isOnGround() true")
-                    impact = self.lifter.getImpactVelocity()
-                    if impact < -30.0:
-                        messenger.send("jumpHardLand")
-                        self.startJumpDelay(0.3)
-                    else:
-                        messenger.send("jumpLand")
-                        if impact < -5.0:
-                            self.startJumpDelay(0.2)
-                        # else, ignore the little potholes.
-                assert self.isAirborne == 0
-                self.priorParent = Vec3.zero()
-                if jump and self.mayJump:
-                    # The jump button is down and we're close
-                    # enough to the ground to jump.
-                    self.lifter.addVelocity(self.avatarControlJumpForce)
-                    messenger.send("jumpStart")
-                    self.isAirborne = 1
-                    assert self.debugPrint("isAirborne 1 due to jump")
-            else:
-                if self.isAirborne == 0:
-                    assert self.debugPrint("isAirborne 1 due to isOnGround() false")
-                self.isAirborne = 1
-
-            self.__oldPosDelta = self.avatarNodePath.getPosDelta(render)
-            # How far did we move based on the amount of time elapsed?
-            self.__oldDt = ClockObject.getGlobalClock().getDt()
-            dt=self.__oldDt
-
-            # Before we do anything with position or orientation, make the avatar
-            # face it's target.  Only allow rMax degrees rotation per frame, so
-            # we don't get an unnatural spinning effect
-            curH = self.avatarNodePath.getH()
-            self.avatarNodePath.headsUp(targetNp)
-            newH = self.avatarNodePath.getH()
-            delH = reduceAngle(newH-curH)
-            rMax = 10
-            if delH < -rMax:
-                self.avatarNodePath.setH(curH-rMax)
-                self.rotationSpeed=-self.avatarControlRotateSpeed
-            elif delH > rMax:
-                self.avatarNodePath.setH(curH+rMax)
-                self.rotationSpeed=self.avatarControlRotateSpeed
-
-            # Check to see if we're moving at all:
-            self.moving = self.speed or self.slideSpeed or self.rotationSpeed or (self.priorParent!=Vec3.zero())
-            if self.moving:
-                distance = dt * self.speed
-                slideDistance = dt * self.slideSpeed
-                print('slideDistance: %s' % slideDistance)
-                rotation = dt * self.rotationSpeed
-
-                # Take a step in the direction of our previous heading.
-                self.vel=Vec3(Vec3.forward() * distance +
-                              Vec3.right() * slideDistance)
-                if self.vel != Vec3.zero() or self.priorParent != Vec3.zero():
-                    if 1:
-                        # rotMat is the rotation matrix corresponding to
-                        # our previous heading.
-                        rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
-                        step=(self.priorParent * dt) + rotMat.xform(self.vel)
-                        self.avatarNodePath.setFluidPos(Point3(
-                                self.avatarNodePath.getPos()+step))
-                self.avatarNodePath.setH(self.avatarNodePath.getH()+rotation)
-            else:
-                self.vel.set(0.0, 0.0, 0.0)
-
-            """
-            # Check to see if we're moving at all:
-            self.moving = self.advanceSpeed or self.slideSpeed or self.rotationSpeed or (self.priorParent!=Vec3.zero())
-            if self.moving:
-                distance = dt * self.advanceSpeed
-                slideDistance = dt * self.slideSpeed
-                rotation = dt * self.rotationSpeed
-
-                # Prevent avatar from getting too close to target
-                d = self.avatarNodePath.getPos(targetNp)
-                # TODO:  make min distance adjust for current weapon
-                if (d[0]*d[0]+d[1]*d[1] < 6.0 and distance > 0):
-                    # move the avatar sideways instead of forward
-                    slideDistance += .2
-                    distance = 0
-
-                # Take a step in the direction of our previous heading.
-                self.vel=Vec3(Vec3.forward() * distance +
-                              Vec3.right() * slideDistance)
-                if self.vel != Vec3.zero() or self.priorParent != Vec3.zero():
-                    # rotMat is the rotation matrix corresponding to
-                    # our previous heading.
-                    rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up())
-                    step=rotMat.xform(self.vel) + (self.priorParent * dt)
-                    self.avatarNodePath.setFluidPos(Point3(
-                        self.avatarNodePath.getPos()+step))
-                self.avatarNodePath.setH(self.avatarNodePath.getH()+rotation)
-            else:
-                self.vel.set(0.0, 0.0, 0.0)
-            """
-            if self.moving or jump:
-                messenger.send("avatarMoving")
-            return Task.cont
-
-

+ 9 - 9
direct/src/controls/ControlManager.py

@@ -1,5 +1,6 @@
 
 from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.MessengerGlobal import messenger
 #from DirectGui import *
 #from PythonUtil import *
 #from IntervalGlobal import *
@@ -292,18 +293,18 @@ class ControlManager:
         self.forceAvJumpToken.release()
         self.forceAvJumpToken = None
 
-    def monitor(self, foo):
+    def monitor(self, _):
         #assert self.debugPrint("monitor()")
         #if 1:
         #    airborneHeight=self.avatar.getAirborneHeight()
         #    onScreenDebug.add("airborneHeight", "% 10.4f"%(airborneHeight,))
-        if 0:
-            onScreenDebug.add("InputState forward", "%d"%(inputState.isSet("forward")))
-            onScreenDebug.add("InputState reverse", "%d"%(inputState.isSet("reverse")))
-            onScreenDebug.add("InputState turnLeft", "%d"%(inputState.isSet("turnLeft")))
-            onScreenDebug.add("InputState turnRight", "%d"%(inputState.isSet("turnRight")))
-            onScreenDebug.add("InputState slideLeft", "%d"%(inputState.isSet("slideLeft")))
-            onScreenDebug.add("InputState slideRight", "%d"%(inputState.isSet("slideRight")))
+        #if 0:
+        #    onScreenDebug.add("InputState forward", "%d"%(inputState.isSet("forward")))
+        #    onScreenDebug.add("InputState reverse", "%d"%(inputState.isSet("reverse")))
+        #    onScreenDebug.add("InputState turnLeft", "%d"%(inputState.isSet("turnLeft")))
+        #    onScreenDebug.add("InputState turnRight", "%d"%(inputState.isSet("turnRight")))
+        #    onScreenDebug.add("InputState slideLeft", "%d"%(inputState.isSet("slideLeft")))
+        #    onScreenDebug.add("InputState slideRight", "%d"%(inputState.isSet("slideRight")))
         return Task.cont
 
     def setWASDTurn(self, turn):
@@ -343,4 +344,3 @@ class ControlManager:
 
             inputState.set("turnLeft", False, inputSource=inputState.WASD)
             inputState.set("turnRight", False, inputSource=inputState.WASD)
-

+ 2 - 0
direct/src/controls/DevWalker.py

@@ -19,7 +19,9 @@ animations based on walker events.
 from direct.showbase.InputStateGlobal import inputState
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
+from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from panda3d.core import *
 
 

+ 10 - 8
direct/src/controls/GravityWalker.py

@@ -19,10 +19,12 @@ from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.showbase import DirectObject
 from direct.controls.ControlManager import CollisionHandlerRayStart
 from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
-from panda3d.core import *
+from direct.task.TaskManagerGlobal import taskMgr
 from direct.extensions_native import VBase3_extensions
 from direct.extensions_native import VBase4_extensions
+from panda3d.core import *
 import math
 
 
@@ -142,7 +144,7 @@ class GravityWalker(DirectObject.DirectObject):
         cSphereNode.setIntoCollideMask(BitMask32.allOff())
 
         # set up collision mechanism
-        if config.GetBool('want-fluid-pusher', 0):
+        if ConfigVariableBool('want-fluid-pusher', 0):
             self.pusher = CollisionHandlerFluidPusher()
         else:
             self.pusher = CollisionHandlerPusher()
@@ -284,11 +286,11 @@ class GravityWalker(DirectObject.DirectObject):
             # make sure we have a shadow traverser
             base.initShadowTrav()
             if active:
-                if 1:
-                    # Please let skyler or drose know if this is causing a problem
-                    # This is a bit of a hack fix:
-                    self.avatarNodePath.setP(0.0)
-                    self.avatarNodePath.setR(0.0)
+                # Please let skyler or drose know if this is causing a problem
+                # This is a bit of a hack fix:
+                self.avatarNodePath.setP(0.0)
+                self.avatarNodePath.setR(0.0)
+
                 self.cTrav.addCollider(self.cWallSphereNodePath, self.pusher)
                 if self.wantFloorSphere:
                     self.cTrav.addCollider(self.cFloorSphereNodePath, self.pusherFloor)
@@ -440,7 +442,7 @@ class GravityWalker(DirectObject.DirectObject):
             self.slideSpeed *= GravityWalker.DiagonalFactor
 
         debugRunning = inputState.isSet("debugRunning")
-        if(debugRunning):
+        if debugRunning:
             self.speed*=base.debugRunningMultiplier
             self.slideSpeed*=base.debugRunningMultiplier
             self.rotationSpeed*=1.25

+ 2 - 0
direct/src/controls/InputState.py

@@ -1,6 +1,8 @@
 from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.showbase.PythonUtil import SerialNumGen
+from direct.showbase.MessengerGlobal import messenger
+
 
 # internal class, don't create these on your own
 class InputStateToken:

+ 7 - 5
direct/src/controls/NonPhysicsWalker.py

@@ -20,7 +20,9 @@ from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.controls.ControlManager import CollisionHandlerRayStart
 from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from panda3d.core import *
 
 class NonPhysicsWalker(DirectObject.DirectObject):
@@ -184,7 +186,7 @@ class NonPhysicsWalker(DirectObject.DirectObject):
         tempCTrav = CollisionTraverser("oneTimeCollide")
         tempCTrav.addCollider(self.cSphereNodePath, self.pusher)
         tempCTrav.addCollider(self.cRayNodePath, self.lifter)
-        tempCTrav.traverse(render)
+        tempCTrav.traverse(base.render)
 
     def addBlastForce(self, vector):
         pass
@@ -268,12 +270,12 @@ class NonPhysicsWalker(DirectObject.DirectObject):
         else:
             self.vel.set(0.0, 0.0, 0.0)
 
-        self.__oldPosDelta = self.avatarNodePath.getPosDelta(render)
+        self.__oldPosDelta = self.avatarNodePath.getPosDelta(base.render)
         self.__oldDt = dt
 
-        try:
-            self.worldVelocity = self.__oldPosDelta*(1/self.__oldDt)
-        except:
+        if self.__oldDt != 0:
+            self.worldVelocity = self.__oldPosDelta * (1 / self.__oldDt)
+        else:
             # divide by zero
             self.worldVelocity = 0
 

+ 0 - 5
direct/src/controls/ObserverWalker.py

@@ -31,9 +31,6 @@ class ObserverWalker(NonPhysicsWalker.NonPhysicsWalker):
         """
         Set up the avatar for collisions
         """
-        """
-        Set up the avatar for collisions
-        """
         assert not avatarNodePath.isEmpty()
 
         self.cTrav = collisionTraverser
@@ -102,11 +99,9 @@ class ObserverWalker(NonPhysicsWalker.NonPhysicsWalker):
         Activate the arrow keys, etc.
         """
         assert self.debugPrint("enableAvatarControls")
-        pass
 
     def disableAvatarControls(self):
         """
         Ignore the arrow keys, etc.
         """
         assert self.debugPrint("disableAvatarControls")
-        pass

+ 98 - 135
direct/src/controls/PhysicsWalker.py

@@ -20,12 +20,14 @@ from direct.directnotify import DirectNotifyGlobal
 from direct.showbase import DirectObject
 from direct.controls.ControlManager import CollisionHandlerRayStart
 from direct.showbase.InputStateGlobal import inputState
+from direct.showbase.MessengerGlobal import messenger
 from direct.task.Task import Task
-from panda3d.core import *
-from panda3d.physics import *
+from direct.task.TaskManagerGlobal import taskMgr
 from direct.extensions_native import Mat3_extensions
 from direct.extensions_native import VBase3_extensions
 from direct.extensions_native import VBase4_extensions
+from panda3d.core import *
+from panda3d.physics import *
 import math
 
 #import LineStream
@@ -131,11 +133,6 @@ class PhysicsWalker(DirectObject.DirectObject):
             assert onScreenDebug.add("height", height.getZ())
             return height.getZ() - self.floorOffset
         else: # useCollisionHandlerQueue
-            """
-            returns the height of the avatar above the ground.
-            If there is no floor below the avatar, 0.0 is returned.
-            aka get airborne height.
-            """
             height = 0.0
             #*#self.cRayTrav.traverse(render)
             if self.cRayQueue.getNumEntries() != 0:
@@ -239,7 +236,7 @@ class PhysicsWalker(DirectObject.DirectObject):
         self.floorOffset = floorOffset = 7.0
 
         self.avatarNodePath = self.setupPhysics(avatarNodePath)
-        if 0 or self.useHeightRay:
+        if self.useHeightRay:
             #self.setupRay(floorBitmask, avatarRadius)
             self.setupRay(floorBitmask, 0.0)
         self.setupSphere(wallBitmask|floorBitmask, avatarRadius)
@@ -257,14 +254,14 @@ class PhysicsWalker(DirectObject.DirectObject):
         self.cSphereNodePath.show()
         if indicator:
             # Indicator Node:
-            change=render.attachNewNode("change")
+            change = render.attachNewNode("change")
             #change.setPos(Vec3(1.0, 1.0, 1.0))
             #change.setHpr(0.0, 0.0, 0.0)
             change.setScale(0.1)
             #change.setColor(Vec4(1.0, 1.0, 1.0, 1.0))
             indicator.reparentTo(change)
 
-            indicatorNode=render.attachNewNode("physVelocityIndicator")
+            indicatorNode = render.attachNewNode("physVelocityIndicator")
             #indicatorNode.setScale(0.1)
             #indicatorNode.setP(90.0)
             indicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 6.0)
@@ -273,7 +270,7 @@ class PhysicsWalker(DirectObject.DirectObject):
 
             self.physVelocityIndicator=indicatorNode
             # Contact Node:
-            contactIndicatorNode=render.attachNewNode("physContactIndicator")
+            contactIndicatorNode = render.attachNewNode("physContactIndicator")
             contactIndicatorNode.setScale(0.25)
             contactIndicatorNode.setP(90.0)
             contactIndicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 5.0)
@@ -472,137 +469,103 @@ class PhysicsWalker(DirectObject.DirectObject):
                 onScreenDebug.add("posDelta1",
                     self.avatarNodePath.getPosDelta(render).pPrintValues())
 
-                if 0:
-                    onScreenDebug.add("posDelta3",
-                        render.getRelativeVector(
-                            self.avatarNodePath,
-                            self.avatarNodePath.getPosDelta(render)).pPrintValues())
-
-                if 0:
-                    onScreenDebug.add("gravity",
-                        self.gravity.getLocalVector().pPrintValues())
-                    onScreenDebug.add("priorParent",
-                        self.priorParent.getLocalVector().pPrintValues())
-                    onScreenDebug.add("avatarViscosity",
-                        "% 10.4f"%(self.avatarViscosity.getCoef(),))
-
-                    onScreenDebug.add("physObject pos",
-                        physObject.getPosition().pPrintValues())
-                    onScreenDebug.add("physObject hpr",
-                        physObject.getOrientation().getHpr().pPrintValues())
-                    onScreenDebug.add("physObject orien",
-                        physObject.getOrientation().pPrintValues())
-
-                if 1:
-                    onScreenDebug.add("physObject vel",
-                        physObject.getVelocity().pPrintValues())
-                    onScreenDebug.add("physObject len",
-                        "% 10.4f"%physObject.getVelocity().length())
-
-                if 0:
-                    onScreenDebug.add("posDelta4",
-                        self.priorParentNp.getRelativeVector(
-                            render,
-                            self.avatarNodePath.getPosDelta(render)).pPrintValues())
-
-                if 1:
-                    onScreenDebug.add("priorParent",
-                        self.priorParent.getLocalVector().pPrintValues())
-
-                if 0:
-                    onScreenDebug.add("priorParent po",
-                        self.priorParent.getVector(physObject).pPrintValues())
-
-                if 0:
-                    onScreenDebug.add("__posDelta",
-                        self.__oldPosDelta.pPrintValues())
-
-                if 1:
-                    onScreenDebug.add("contact",
-                        contact.pPrintValues())
-                    #onScreenDebug.add("airborneHeight", "% 10.4f"%(
-                    #    self.getAirborneHeight(),))
-
-                if 0:
-                    onScreenDebug.add("__oldContact",
-                        contact.pPrintValues())
-                    onScreenDebug.add("__oldAirborneHeight", "% 10.4f"%(
-                        self.getAirborneHeight(),))
-        airborneHeight=self.getAirborneHeight()
+                #onScreenDebug.add("posDelta3",
+                #    render.getRelativeVector(
+                #        self.avatarNodePath,
+                #        self.avatarNodePath.getPosDelta(render)).pPrintValues())
+
+                #onScreenDebug.add("gravity",
+                #    self.gravity.getLocalVector().pPrintValues())
+                #onScreenDebug.add("priorParent",
+                #    self.priorParent.getLocalVector().pPrintValues())
+                #onScreenDebug.add("avatarViscosity",
+                #    "% 10.4f"%(self.avatarViscosity.getCoef(),))
+                #
+                #onScreenDebug.add("physObject pos",
+                #    physObject.getPosition().pPrintValues())
+                #onScreenDebug.add("physObject hpr",
+                #    physObject.getOrientation().getHpr().pPrintValues())
+                #onScreenDebug.add("physObject orien",
+                #    physObject.getOrientation().pPrintValues())
+
+                onScreenDebug.add("physObject vel",
+                    physObject.getVelocity().pPrintValues())
+                onScreenDebug.add("physObject len",
+                    "% 10.4f"%physObject.getVelocity().length())
+
+                #onScreenDebug.add("posDelta4",
+                #    self.priorParentNp.getRelativeVector(
+                #        render,
+                #        self.avatarNodePath.getPosDelta(render)).pPrintValues())
+
+                onScreenDebug.add("priorParent",
+                    self.priorParent.getLocalVector().pPrintValues())
+
+                #onScreenDebug.add("priorParent po",
+                #    self.priorParent.getVector(physObject).pPrintValues())
+
+                #onScreenDebug.add("__posDelta",
+                #    self.__oldPosDelta.pPrintValues())
+
+                onScreenDebug.add("contact",
+                    contact.pPrintValues())
+                #onScreenDebug.add("airborneHeight", "% 10.4f"%(
+                #    self.getAirborneHeight(),))
+
+                #onScreenDebug.add("__oldContact",
+                #    contact.pPrintValues())
+                #onScreenDebug.add("__oldAirborneHeight", "% 10.4f"%(
+                #    self.getAirborneHeight(),))
+        airborneHeight = self.getAirborneHeight()
         if airborneHeight > self.highMark:
             self.highMark = airborneHeight
             if __debug__:
                 onScreenDebug.add("highMark", "% 10.4f"%(self.highMark,))
         #if airborneHeight < 0.1: #contact!=Vec3.zero():
-        if 1:
-            if (airborneHeight > self.avatarRadius*0.5
-                    or physObject.getVelocity().getZ() > 0.0
-                    ): # Check stair angles before changing this.
-                # ...the avatar is airborne (maybe a lot or a tiny amount).
-                self.isAirborne = 1
-            else:
-                # ...the avatar is very close to the ground (close enough to be
-                # considered on the ground).
-                if self.isAirborne and physObject.getVelocity().getZ() <= 0.0:
-                    # ...the avatar has landed.
-                    contactLength = contact.length()
-                    if contactLength>self.__hardLandingForce:
-                        #print "jumpHardLand"
-                        messenger.send("jumpHardLand")
-                    else:
-                        #print "jumpLand"
-                        messenger.send("jumpLand")
-                    self.priorParent.setVector(Vec3.zero())
-                    self.isAirborne = 0
-                elif jump:
-                    #print "jump"
-                    #self.__jumpButton=0
-                    messenger.send("jumpStart")
-                    if 0:
-                        # ...jump away from walls and with with the slope normal.
-                        jumpVec=Vec3(contact+Vec3.up())
-                        #jumpVec=Vec3(rotAvatarToPhys.xform(jumpVec))
-                        jumpVec.normalize()
-                    else:
-                        # ...jump straight up, even if next to a wall.
-                        jumpVec=Vec3.up()
-                    jumpVec*=self.avatarControlJumpForce
-                    physObject.addImpulse(Vec3(jumpVec))
-                    self.isAirborne = 1 # Avoid double impulse before fully airborne.
-                else:
-                    self.isAirborne = 0
-            if __debug__:
-                onScreenDebug.add("isAirborne", "%d"%(self.isAirborne,))
+        if (airborneHeight > self.avatarRadius*0.5
+                or physObject.getVelocity().getZ() > 0.0
+                ): # Check stair angles before changing this.
+            # ...the avatar is airborne (maybe a lot or a tiny amount).
+            self.isAirborne = 1
         else:
-            if contact!=Vec3.zero():
-                # ...the avatar has touched something (but might not be on the ground).
+            # ...the avatar is very close to the ground (close enough to be
+            # considered on the ground).
+            if self.isAirborne and physObject.getVelocity().getZ() <= 0.0:
+                # ...the avatar has landed.
                 contactLength = contact.length()
-                contact.normalize()
-                angle=contact.dot(Vec3.up())
-                if angle>self.__standableGround:
-                    # ...avatar is on standable ground.
-                    if self.__oldContact==Vec3.zero():
-                    #if self.__oldAirborneHeight > 0.1: #self.__oldContact==Vec3.zero():
-                        # ...avatar was airborne.
-                        self.jumpCount-=1
-                        if contactLength>self.__hardLandingForce:
-                            messenger.send("jumpHardLand")
-                        else:
-                            messenger.send("jumpLand")
-                    elif jump:
-                        self.jumpCount+=1
-                        #self.__jumpButton=0
-                        messenger.send("jumpStart")
-                        jump=Vec3(contact+Vec3.up())
-                        #jump=Vec3(rotAvatarToPhys.xform(jump))
-                        jump.normalize()
-                        jump*=self.avatarControlJumpForce
-                        physObject.addImpulse(Vec3(jump))
-
-        if contact!=self.__oldContact:
+                if contactLength>self.__hardLandingForce:
+                    #print "jumpHardLand"
+                    messenger.send("jumpHardLand")
+                else:
+                    #print "jumpLand"
+                    messenger.send("jumpLand")
+                self.priorParent.setVector(Vec3.zero())
+                self.isAirborne = 0
+            elif jump:
+                #print "jump"
+                #self.__jumpButton = 0
+                messenger.send("jumpStart")
+
+                ## ...jump away from walls and with with the slope normal.
+                #jumpVec=Vec3(contact+Vec3.up())
+                ##jumpVec=Vec3(rotAvatarToPhys.xform(jumpVec))
+                #jumpVec.normalize()
+
+                # ...jump straight up, even if next to a wall.
+                jumpVec=Vec3.up()
+
+                jumpVec *= self.avatarControlJumpForce
+                physObject.addImpulse(Vec3(jumpVec))
+                self.isAirborne = 1 # Avoid double impulse before fully airborne.
+            else:
+                self.isAirborne = 0
+        if __debug__:
+            onScreenDebug.add("isAirborne", "%d"%(self.isAirborne,))
+
+        if contact != self.__oldContact:
             # We must copy the vector to preserve it:
-            self.__oldContact=Vec3(contact)
-        self.__oldAirborneHeight=airborneHeight
+            self.__oldContact = Vec3(contact)
+        self.__oldAirborneHeight = airborneHeight
 
         moveToGround = Vec3.zero()
         if not self.useHeightRay or self.isAirborne:
@@ -741,7 +704,7 @@ class PhysicsWalker(DirectObject.DirectObject):
     if __debug__:
         def setupAvatarPhysicsIndicator(self):
             if self.wantDebugIndicator:
-                indicator=loader.loadModel('phase_5/models/props/dagger')
+                indicator = base.loader.loadModel('phase_5/models/props/dagger')
                 #self.walkControls.setAvatarPhysicsIndicator(indicator)
 
         def debugPrint(self, message):

+ 1 - 0
direct/src/controls/TwoDWalker.py

@@ -3,6 +3,7 @@ TwoDWalker.py is for controlling the avatars in a 2D scroller game environment.
 """
 
 from .GravityWalker import *
+from direct.showbase.MessengerGlobal import messenger
 from panda3d.core import ConfigVariableBool
 
 

+ 1 - 3
direct/src/dcparser/dcClass_ext.cxx

@@ -400,9 +400,7 @@ pack_required_field(DCPacker &packer, PyObject *distobj,
     PyObject_GetAttrString(distobj, (char *)getter_name.c_str());
   nassertr(func != nullptr, false);
 
-  PyObject *empty_args = PyTuple_New(0);
-  PyObject *result = PyObject_CallObject(func, empty_args);
-  Py_DECREF(empty_args);
+  PyObject *result = PyObject_CallNoArgs(func);
   Py_DECREF(func);
   if (result == nullptr) {
     // We don't set this as an exception, since presumably the Python method

+ 1 - 1
direct/src/dcparser/dcPacker.cxx

@@ -769,7 +769,7 @@ enquote_string(ostream &out, char quote_mark, const string &str) {
     if ((*pi) == quote_mark || (*pi) == '\\') {
       out << '\\' << (*pi);
 
-    } else if (!isprint(*pi)) {
+    } else if (!isprint(*pi) || (*pi) == '\t') {
       char buffer[10];
       sprintf(buffer, "%02x", (unsigned char)(*pi));
       out << "\\x" << buffer;

+ 5 - 1
direct/src/dcparser/dcPacker_ext.cxx

@@ -226,6 +226,10 @@ unpack_object() {
       std::string str;
       _this->unpack_string(str);
       object = PyUnicode_FromStringAndSize(str.data(), str.size());
+      if (object == nullptr) {
+        nassert_raise("Unable to decode UTF-8 string; use blob type for binary data");
+        return nullptr;
+      }
     }
     break;
 
@@ -309,7 +313,7 @@ unpack_class_object(const DCClass *dclass) {
   if (!dclass->has_constructor()) {
     // If the class uses a default constructor, go ahead and create the Python
     // object for it now.
-    object = PyObject_CallObject(class_def, nullptr);
+    object = PyObject_CallNoArgs(class_def);
     if (object == nullptr) {
       return nullptr;
     }

+ 3 - 16
direct/src/directbase/TestStart.py

@@ -7,26 +7,13 @@ from direct.showbase import ShowBase
 base = ShowBase.ShowBase()
 
 # Put an axis in the world:
-loader.loadModel("models/misc/xyzAxis").reparentTo(render)
+base.loader.loadModel("models/misc/xyzAxis").reparentTo(render)
 
-if 0:
-    # Hack:
-    # Enable drive mode but turn it off, and reset the camera
-    # This is here because ShowBase sets up a drive interface, this
-    # can be removed if ShowBase is changed to not set that up.
-    base.useDrive()
-    base.disableMouse()
-    if base.mouseInterface:
-        base.mouseInterface.reparentTo(base.dataUnused)
-    if base.mouse2cam:
-        base.mouse2cam.reparentTo(base.dataUnused)
-    # end of hack.
-
-camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
+base.camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
 base.camLens.setFov(52.0)
 base.camLens.setNearFar(1.0, 10000.0)
 
-globalClock.setMaxDt(0.2)
+base.clock.setMaxDt(0.2)
 base.enableParticles()
 
 # Force the screen to update:

+ 6 - 18
direct/src/directbase/ThreeUpStart.py

@@ -5,29 +5,17 @@ from panda3d.core import *
 
 from direct.showbase.PythonUtil import *
 from direct.showbase import ThreeUpShow
-ThreeUpShow.ThreeUpShow()
+
+base = ThreeUpShow.ThreeUpShow()
 
 # Put an axis in the world:
-loader.loadModel("models/misc/xyzAxis").reparentTo(render)
-
-if 0:
-    # Hack:
-    # Enable drive mode but turn it off, and reset the camera
-    # This is here because ShowBase sets up a drive interface, this
-    # can be removed if ShowBase is changed to not set that up.
-    base.useDrive()
-    base.disableMouse()
-    if base.mouseInterface:
-        base.mouseInterface.reparentTo(base.dataUnused)
-    if base.mouse2cam:
-        base.mouse2cam.reparentTo(base.dataUnused)
-    # end of hack.
-
-camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
+base.loader.loadModel("models/misc/xyzAxis").reparentTo(render)
+
+base.camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
 base.camLens.setFov(52.0)
 base.camLens.setNearFar(1.0, 10000.0)
 
-globalClock.setMaxDt(0.2)
+base.clock.setMaxDt(0.2)
 base.enableParticles()
 base.addAngularIntegrator()
 

+ 17 - 16
direct/src/directdevices/DirectDeviceManager.py

@@ -14,7 +14,7 @@ class DirectDeviceManager(VrpnClient, DirectObject):
     def __init__(self, server = None):
 
         # Determine which server to use
-        if server != None:
+        if server is not None:
             # One given as constructor argument
             self.server = server
         else:
@@ -77,10 +77,10 @@ class DirectButtons(ButtonNode, DirectObject):
         return self.nodePath
 
     def __repr__(self):
-        str = self.name + ': '
+        string = self.name + ': '
         for val in self:
-            str = str + '%d' % val + ' '
-        return str
+            string = string + '%d' % val + ' '
+        return string
 
 class DirectAnalogs(AnalogNode, DirectObject):
     analogCount = 0
@@ -149,12 +149,12 @@ class DirectAnalogs(AnalogNode, DirectObject):
         aMin = self.analogMin
         center = self.analogCenter
         deadband = self.analogDeadband
-        range = self.analogRange
+
         # Zero out values in deadband
-        if (abs(rawValue-center) <= deadband):
+        if abs(rawValue - center) <= deadband:
             return 0.0
         # Clamp value between aMin and aMax and scale around center
-        if (rawValue >= center):
+        if rawValue >= center:
             # Convert positive values to range 0 to 1
             val = min(rawValue * sf, aMax)
             percentVal = ((val - (center + deadband))/
@@ -165,7 +165,7 @@ class DirectAnalogs(AnalogNode, DirectObject):
             percentVal = -((val - (center - deadband))/
                            float(aMin - (center - deadband)))
         # Normalize values to given minVal and maxVal range
-        return (((maxVal - minVal) * ((percentVal + 1)/2.0)) + minVal)
+        return ((maxVal - minVal) * ((percentVal + 1)/2.0)) + minVal
 
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1, sf = 1.0):
         try:
@@ -180,13 +180,14 @@ class DirectAnalogs(AnalogNode, DirectObject):
         return self.nodePath
 
     def __repr__(self):
-        str = self.name + ': '
+        string = self.name + ': '
         for val in self:
-            str = str + '%.3f' % val + ' '
-        return str
+            string = string + '%.3f' % val + ' '
+        return string
 
 class DirectTracker(TrackerNode, DirectObject):
     trackerCount = 0
+
     def __init__(self, vrpnClient, device):
         # Keep track of number of trackers created
         DirectTracker.trackerCount += 1
@@ -257,10 +258,10 @@ class DirectDials(DialNode, DirectObject):
         return self.nodePath
 
     def __repr__(self):
-        str = self.name + ': '
+        string = self.name + ': '
         for i in range(self.getNumDials()):
-            str = str + '%.3f' % self[i] + ' '
-        return str
+            string = string + '%.3f' % self[i] + ' '
+        return string
 
 class DirectTimecodeReader(AnalogNode, DirectObject):
     timecodeReaderCount = 0
@@ -316,5 +317,5 @@ class DirectTimecodeReader(AnalogNode, DirectObject):
                 self.totalSeconds)
 
     def __repr__(self):
-        str = ('%s: %d:%d:%d:%d' % ((self.name,) + self.getTime()[:-1]))
-        return str
+        string = ('%s: %d:%d:%d:%d' % ((self.name,) + self.getTime()[:-1]))
+        return string

+ 3 - 6
direct/src/directdevices/DirectFastrak.py

@@ -1,14 +1,12 @@
 """ Class used to create and control radamec device """
-from math import *
 from direct.showbase.DirectObject import DirectObject
+from direct.task.Task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from .DirectDeviceManager import *
 
 from direct.directnotify import DirectNotifyGlobal
 
-"""
-TODO:
-Handle interaction between widget, followSelectedTask and updateTask
-"""
+#TODO: Handle interaction between widget, followSelectedTask and updateTask
 
 # ANALOGS
 NULL_AXIS = -1
@@ -65,4 +63,3 @@ class DirectFastrak(DirectObject):
                                3.280839895013123 * pos[1],
                                3.280839895013123 * pos[0])
         self.notify.debug("Tracker(%d) Pos = %s" % (self.deviceNo, repr(self.trackerPos)))
-

+ 18 - 19
direct/src/directdevices/DirectJoybox.py

@@ -4,12 +4,12 @@ from .DirectDeviceManager import *
 from direct.directtools.DirectUtil import *
 from direct.gui import OnscreenText
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
+from panda3d.core import ClockObject
+
 import math
 
-"""
-TODO:
-Handle interaction between widget, followSelectedTask and updateTask
-"""
+#TODO: Handle interaction between widget, followSelectedTask and updateTask
 
 # BUTTONS
 L_STICK = 0
@@ -39,10 +39,11 @@ class DirectJoybox(DirectObject):
     joyboxCount = 0
     xyzMultiplier = 1.0
     hprMultiplier = 1.0
+
     def __init__(self, device = 'CerealBox', nodePath = base.direct.camera,
                  headingNP = base.direct.camera):
         # See if device manager has been initialized
-        if base.direct.deviceManager == None:
+        if base.direct.deviceManager is None:
             base.direct.deviceManager = DirectDeviceManager()
         # Set name
         DirectJoybox.joyboxCount += 1
@@ -59,7 +60,7 @@ class DirectJoybox(DirectObject):
                         R_TWIST, L_TWIST, NULL_AXIS]
         self.modifier = [1, 1, 1, -1, -1, 0]
         # Initialize time
-        self.lastTime = globalClock.getFrameTime()
+        self.lastTime = ClockObject.getGlobalClock().getFrameTime()
         # Record node path
         self.nodePath = nodePath
         self.headingNP = headingNP
@@ -149,7 +150,7 @@ class DirectJoybox(DirectObject):
 
     def updateVals(self):
         # Update delta time
-        cTime = globalClock.getFrameTime()
+        cTime = ClockObject.getGlobalClock().getFrameTime()
         self.deltaTime = cTime - self.lastTime
         self.lastTime = cTime
         # Update analogs
@@ -165,19 +166,19 @@ class DirectJoybox(DirectObject):
 
     def updateValsUnrolled(self):
         # Update delta time
-        cTime = globalClock.getFrameTime()
+        cTime = ClockObject.getGlobalClock().getFrameTime()
         self.deltaTime = cTime - self.lastTime
         self.lastTime = cTime
         # Update analogs
         for chan in range(len(self.analogs)):
             val = self.analogs.getControlState(chan)
             # Zero out values in deadband
-            if (val < 0):
+            if val < 0:
                 val = min(val + ANALOG_DEADBAND, 0.0)
             else:
                 val = max(val - ANALOG_DEADBAND, 0.0)
             # Scale up rotating knob values
-            if (chan == L_TWIST) or (chan == R_TWIST):
+            if chan == L_TWIST or chan == R_TWIST:
                 val *= 3.0
             # Now clamp value between minVal and maxVal
             val = CLAMP(val, JOYBOX_MIN, JOYBOX_MAX)
@@ -240,7 +241,7 @@ class DirectJoybox(DirectObject):
 
     def joyboxFly(self):
         # Do nothing if no nodePath selected
-        if self.nodePath == None:
+        if self.nodePath is None:
             return
         hprScale = ((self.aList[L_SLIDE] + 1.0) *
                     50.0 * DirectJoybox.hprMultiplier)
@@ -264,14 +265,14 @@ class DirectJoybox(DirectObject):
         # if we are using a heading nodepath, we want
         # to drive in the direction we are facing,
         # however, we don't want the z component to change
-        if (self.useHeadingNP and self.headingNP != None):
+        if self.useHeadingNP and self.headingNP is not None:
             oldZ = pos.getZ()
             pos = self.nodePath.getRelativeVector(self.headingNP,
                                                   pos)
             pos.setZ(oldZ)
             # if we are using a heading NP we might want to rotate
             # in place around that NP
-            if (self.rotateInPlace):
+            if self.rotateInPlace:
                 parent = self.nodePath.getParent()
                 self.floatingNP.reparentTo(parent)
                 self.floatingNP.setPos(self.headingNP,0,0,0)
@@ -389,7 +390,7 @@ class DirectJoybox(DirectObject):
 
     def spaceFly(self):
         # Do nothing if no nodePath selected
-        if self.nodePath == None:
+        if self.nodePath is None:
             return
         hprScale = (self.normalizeChannel(L_SLIDE, 0.1, 100) *
                     DirectJoybox.hprMultiplier)
@@ -408,7 +409,7 @@ class DirectJoybox(DirectObject):
 
     def planetFly(self):
         # Do nothing if no nodePath selected
-        if self.nodePath == None:
+        if self.nodePath is None:
             return
         hprScale = (self.normalizeChannel(L_SLIDE, 0.1, 100) *
                     DirectJoybox.hprMultiplier)
@@ -459,7 +460,7 @@ class DirectJoybox(DirectObject):
 
     def orbitFly(self):
         # Do nothing if no nodePath selected
-        if self.nodePath == None:
+        if self.nodePath is None:
             return
         hprScale = (self.normalizeChannel(L_SLIDE, 0.1, 100) *
                     DirectJoybox.hprMultiplier)
@@ -502,7 +503,7 @@ class DirectJoybox(DirectObject):
     # correct the ranges of the two twist axes of the joybox.
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1):
         try:
-            if (chan == L_TWIST) or (chan == R_TWIST):
+            if chan == L_TWIST or chan == R_TWIST:
                 # These channels have reduced range
                 return self.analogs.normalize(
                     self.analogs.getControlState(chan), minVal, maxVal, 3.0)
@@ -511,5 +512,3 @@ class DirectJoybox(DirectObject):
                     self.analogs.getControlState(chan), minVal, maxVal)
         except IndexError:
             return 0.0
-
-

+ 12 - 13
direct/src/directdevices/DirectRadamec.py

@@ -1,15 +1,13 @@
 """ Class used to create and control radamec device """
 from math import *
 from direct.showbase.DirectObject import DirectObject
+from direct.task.Task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 from .DirectDeviceManager import *
 
 from direct.directnotify import DirectNotifyGlobal
 
-
-"""
-TODO:
-Handle interaction between widget, followSelectedTask and updateTask
-"""
+#TODO: Handle interaction between widget, followSelectedTask and updateTask
 
 # ANALOGS
 RAD_PAN = 0
@@ -23,7 +21,7 @@ class DirectRadamec(DirectObject):
 
     def __init__(self, device = 'Analog0', nodePath = None):
         # See if device manager has been initialized
-        if base.direct.deviceManager == None:
+        if base.direct.deviceManager is None:
             base.direct.deviceManager = DirectDeviceManager()
         # Set name
         self.name = 'Radamec-' + repr(DirectRadamec.radamecCount)
@@ -73,11 +71,12 @@ class DirectRadamec(DirectObject):
     # Normalize to the range [-minVal, maxVal] based on some hard-coded
     # max/min numbers of the Radamec device
     def normalizeChannel(self, chan, minVal = -1, maxVal = 1):
-        try:
-            maxRange = self.maxRange[chan]
-            minRange = self.minRange[chan]
-        except IndexError:
+        if chan < 0 or chan >= min(len(self.maxRange), len(self.minRange)):
             raise RuntimeError("can't normalize this channel (channel %d)" % chan)
-        range = maxRange - minRange
-        clampedVal = CLAMP(self.aList[chan], minRange, maxRange)
-        return ((maxVal - minVal) * (clampedVal - minRange) / range) + minVal
+
+        maxRange = self.maxRange[chan]
+        minRange = self.minRange[chan]
+
+        diff = maxRange - minRange
+        clampedVal = max(min(self.aList[chan], maxRange), maxRange)
+        return ((maxVal - minVal) * (clampedVal - minRange) / diff) + minVal

+ 3 - 3
direct/src/directnotify/DirectNotify.py

@@ -41,17 +41,17 @@ class DirectNotify:
         """getCategory(self, string)
         Return the category with given name if present, None otherwise
         """
-        return (self.__categories.get(categoryName, None))
+        return self.__categories.get(categoryName, None)
 
     def newCategory(self, categoryName, logger=None):
         """newCategory(self, string)
         Make a new notify category named categoryName. Return new category
         if no such category exists, else return existing category
         """
-        if (categoryName not in self.__categories):
+        if categoryName not in self.__categories:
             self.__categories[categoryName] = Notifier.Notifier(categoryName, logger)
             self.setDconfigLevel(categoryName)
-        return (self.getCategory(categoryName))
+        return self.getCategory(categoryName)
 
     def setDconfigLevel(self, categoryName):
         """

+ 11 - 26
direct/src/directnotify/Logger.py

@@ -4,6 +4,7 @@
 import time
 import math
 
+
 class Logger:
     def __init__(self, fileName="log"):
         """
@@ -14,18 +15,17 @@ class Logger:
         self.__logFile = None
         self.__logFileName = fileName
 
-    def setTimeStamp(self, bool):
+    def setTimeStamp(self, enable):
         """
         Toggle time stamp printing with log entries on and off
         """
-        self.__timeStamp = bool
+        self.__timeStamp = enable
 
     def getTimeStamp(self):
         """
         Return whether or not we are printing time stamps with log entries
         """
-        return(self.__timeStamp)
-
+        return self.__timeStamp
 
     # logging control
 
@@ -38,13 +38,12 @@ class Logger:
     def log(self, entryString):
         """log(self, string)
         Print the given string to the log file"""
-        if (self.__logFile == None):
+        if self.__logFile is None:
             self.__openLogFile()
-        if (self.__timeStamp):
+        if self.__timeStamp:
             self.__logFile.write(self.__getTimeStamp())
         self.__logFile.write(entryString + '\n')
 
-
     # logging functions
 
     def __openLogFile(self):
@@ -61,7 +60,7 @@ class Logger:
         """
         Close the error/warning output file
         """
-        if (self.__logFile != None):
+        if self.__logFile is not None:
             self.__logFile.close()
 
     def __getTimeStamp(self):
@@ -70,22 +69,8 @@ class Logger:
         """
         t = time.time()
         dt = t - self.__startTime
-        if (dt >= 86400):
-            days = int(math.floor(dt/86400))
-            dt = dt%86400
-        else:
-            days = 0
-        if (dt >= 3600):
-            hours = int(math.floor(dt/3600))
-            dt = dt%3600
-        else:
-            hours = 0
-        if (dt >= 60):
-            minutes = int(math.floor(dt/60))
-            dt = dt%60
-        else:
-            minutes = 0
+        days, dt = divmod(dt, 86400)
+        hours, dt = divmod(dt, 3600)
+        minutes, dt = divmod(dt, 60)
         seconds = int(math.ceil(dt))
-        return("%02d:%02d:%02d:%02d: " % (days, hours, minutes, seconds))
-
-
+        return "%02d:%02d:%02d:%02d: " % (days, hours, minutes, seconds)

+ 11 - 12
direct/src/directnotify/Notifier.py

@@ -32,7 +32,7 @@ class Notifier:
         """
         self.__name = name
 
-        if (logger==None):
+        if logger is None:
             self.__logger = defaultLogger
         else:
             self.__logger = logger
@@ -144,17 +144,17 @@ class Notifier:
             self.__print(string)
         return 1 # to allow assert myNotify.warning("blah")
 
-    def setWarning(self, bool):
+    def setWarning(self, enable):
         """
         Enable/Disable the printing of warning messages
         """
-        self.__warning = bool
+        self.__warning = enable
 
     def getWarning(self):
         """
         Return whether the printing of warning messages is on or off
         """
-        return(self.__warning)
+        return self.__warning
 
     # debug funcs
     def debug(self, debugString):
@@ -171,11 +171,11 @@ class Notifier:
             self.__print(string)
         return 1 # to allow assert myNotify.debug("blah")
 
-    def setDebug(self, bool):
+    def setDebug(self, enable):
         """
         Enable/Disable the printing of debug messages
         """
-        self.__debug = bool
+        self.__debug = enable
 
     def getDebug(self):
         """
@@ -204,11 +204,11 @@ class Notifier:
         """
         return self.__info
 
-    def setInfo(self, bool):
+    def setInfo(self, enable):
         """
         Enable/Disable informational message  printing
         """
-        self.__info = bool
+        self.__info = enable
 
     # log funcs
     def __log(self, logEntry):
@@ -222,13 +222,13 @@ class Notifier:
         """
         Return 1 if logging enabled, 0 otherwise
         """
-        return (self.__logging)
+        return self.__logging
 
-    def setLogging(self, bool):
+    def setLogging(self, enable):
         """
         Set the logging flag to int (1=on, 0=off)
         """
-        self.__logging = bool
+        self.__logging = enable
 
     def __print(self, string):
         """
@@ -297,4 +297,3 @@ class Notifier:
             self.__log(string)
             self.__print(string)
         return 1 # to allow assert self.notify.debugCall("blah")
-

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

@@ -4,7 +4,7 @@ import time
 
 class RotatingLog:
     """
-    A file() (or open()) replacement that will automatically open and write
+    An `open()` replacement that will automatically open and write
     to a new file if the prior file is too large or after a time interval.
     """
 

+ 1 - 0
direct/src/directscripts/Doxyfile.cxx

@@ -789,6 +789,7 @@ RECURSIVE              = YES
 EXCLUDE                = dtool/src/parser-inc \
                          dtool/src/cppparser \
                          dtool/src/interrogate \
+                         direct/src/directscripts \
                          direct/src/plugin \
                          direct/src/plugin_standalone \
                          direct/src/plugin_npapi \

+ 22 - 17
direct/src/directscripts/eggcacher.py

@@ -8,7 +8,9 @@
 #
 ##############################################################################
 
-import os,sys,gc
+import os
+import sys
+import gc
 from panda3d.core import *
 
 class EggCacher:
@@ -18,7 +20,7 @@ class EggCacher:
         self.bamcache = BamCache.getGlobalPtr()
         self.pandaloader = Loader()
         self.loaderopts = LoaderOptions(LoaderOptions.LF_no_ram_cache)
-        if (self.bamcache.getActive() == 0):
+        if not self.bamcache.getActive():
             print("The model cache is not currently active.")
             print("You must set a model-cache-dir in your config file.")
             sys.exit(1)
@@ -29,42 +31,44 @@ class EggCacher:
     def parseArgs(self, args):
         self.concise = 0
         self.pzkeep = 0
-        while len(args):
-            if (args[0]=="--concise"):
+        while len(args) > 0:
+            if args[0] == "--concise":
                 self.concise = 1
                 args = args[1:]
-            elif (args[0]=="--pzkeep"):
+            elif args[0] == "--pzkeep":
                 self.pzkeep = 1
                 args = args[1:]
             else:
                 break
-        if (len(args) < 1):
+        if len(args) < 1:
             print("Usage: eggcacher options file-or-directory")
             sys.exit(1)
         self.paths = args
 
     def scanPath(self, eggs, path):
-        if (os.path.exists(path)==0):
+        if not os.path.exists(path):
             print("No such file or directory: " + path)
             return
-        if (os.path.isdir(path)):
+        if os.path.isdir(path):
             for f in os.listdir(path):
                 self.scanPath(eggs, os.path.join(path,f))
             return
-        if (path.endswith(".egg")):
+        if path.endswith(".egg"):
             size = os.path.getsize(path)
             eggs.append((path,size))
             return
-        if (path.endswith(".egg.pz") or path.endswith(".egg.gz")):
+        if path.endswith(".egg.pz") or path.endswith(".egg.gz"):
             size = os.path.getsize(path)
-            if (self.pzkeep): eggs.append((path,size))
-            else: eggs.append((path[:-3],size))
+            if self.pzkeep:
+                eggs.append((path, size))
+            else:
+                eggs.append((path[:-3], size))
 
     def scanPaths(self, paths):
         eggs = []
         for path in paths:
-            abs = os.path.abspath(path)
-            self.scanPath(eggs,path)
+            #abs = os.path.abspath(path)
+            self.scanPath(eggs, path)
         return eggs
 
     def processFiles(self, files):
@@ -72,15 +76,16 @@ class EggCacher:
         for (path, size) in files:
             total += size
         progress = 0
-        for (path,size) in files:
+        for (path, size) in files:
             fn = Filename.fromOsSpecific(path)
             cached = self.bamcache.lookup(fn, "bam")
             percent = (progress * 100) / total
             report = path
-            if (self.concise): report = os.path.basename(report)
+            if self.concise:
+                report = os.path.basename(report)
             print("Preprocessing Models %2d%% %s" % (percent, report))
             sys.stdout.flush()
-            if (cached) and (cached.hasData()==0):
+            if cached and not cached.hasData():
                 self.pandaloader.loadSync(fn, self.loaderopts)
             gc.collect()
             ModelPool.releaseAllModels()

+ 2 - 2
direct/src/directscripts/extract_docs.py

@@ -10,8 +10,8 @@ from __future__ import print_function
 __all__ = []
 
 import os
-from distutils import sysconfig
-import panda3d, pandac
+import panda3d
+import pandac
 from panda3d.interrogatedb import *
 
 

+ 0 - 986
direct/src/directscripts/gendocs.py

@@ -1,986 +0,0 @@
-########################################################################
-#
-# Documentation generator for panda.
-#
-# How to use this module:
-#
-#   from direct.directscripts import gendocs
-#   gendocs.generate(version, indirlist, directdirlist, docdir, header, footer, urlprefix, urlsuffix)
-#
-#   - version is the panda version number
-#
-#   - indirlist is the name of a directory, or a list of directories,
-#     containing the "xxx.in" files that interrogate generates.  No
-#     slash at end.
-#
-#   - directdirlist is the name of a directory, or a list of
-#     directories, containing the source code for "direct," as well as
-#     for other Python-based trees that should be included in the
-#     documentation pages.  No slash at end.
-#
-#   - docdir is the name of a directory into which HTML files
-#     will be emitted.  No slash at end.
-#
-#   - header is a string that will be placed at the front of
-#     every HTML page.
-#
-#   - footer is a string that will be placed at the end of
-#     every HTML page.
-#
-#   - urlprefix is a string that will be appended to the front of
-#     every URL.
-#
-#   - urlsuffix is a string that will be appended to the end of
-#     every URL.
-#
-########################################################################
-#
-# The major subsystems are:
-#
-# * The module that loads interrogate databases.
-#
-# * The module that loads python parse-trees.
-#
-# * The "code database", which provides a single access point
-#   for both interrogate databases and python parse trees.
-#
-# * The HTML generator.
-#
-########################################################################
-
-import os, sys, parser, symbol, token, re
-
-########################################################################
-#
-# assorted utility functions
-#
-########################################################################
-
-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_]+")
-FILEHEADER = re.compile(
-r"""^// Filename: [a-zA-Z.]+
-// Created by:  [a-zA-Z. ()0-9]+(
-//)?
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright \(c\) Carnegie Mellon University.  All rights reserved.
-//
-// All use of this software is subject to the terms of the revised BSD
-// license.  You should have received a copy of this license along
-// with this source code in a file named "LICENSE."
-//
-////////////////////////////////////////////////////////////////////""")
-
-def readFile(fn):
-    try:
-        srchandle = open(fn, "r")
-        data = srchandle.read()
-        srchandle.close()
-        return data
-    except:
-        sys.exit("Cannot read "+fn)
-
-def writeFile(wfile, data):
-    try:
-        dsthandle = open(wfile, "wb")
-        dsthandle.write(data)
-        dsthandle.close()
-    except:
-        sys.exit("Cannot write "+wfile)
-
-def writeFileLines(wfile, lines):
-    try:
-        dsthandle = open(wfile, "wb")
-        for x in lines:
-            dsthandle.write(x)
-            dsthandle.write("\n")
-        dsthandle.close()
-    except:
-        sys.exit("Cannot write "+wfile)
-
-def findFiles(dirlist, ext, ign, list):
-    if isinstance(dirlist, str):
-        dirlist = [dirlist]
-    for dir in dirlist:
-        for file in os.listdir(dir):
-            full = dir + "/" + file
-            if full not in ign and file not in ign:
-                if (os.path.isfile(full)):
-                    if (file.endswith(ext)):
-                        list.append(full)
-                elif (os.path.isdir(full)):
-                    findFiles(full, ext, ign, list)
-
-def pathToModule(result):
-    if (result[-3:]==".py"): result=result[:-3]
-    result = result.replace("/src/","/")
-    result = result.replace("/",".")
-    return result
-
-def textToHTML(comment, sep, delsection=None):
-    sections = [""]
-    included = {}
-    for line in comment.split("\n"):
-        line = line.lstrip(" ").lstrip(sep).lstrip(" ").rstrip("\r").rstrip(" ")
-        if (line == ""):
-            sections.append("")
-        elif (line[0]=="*") or (line[0]=="-"):
-            sections.append(line)
-            sections.append("")
-        elif (SECHEADER.match(line)):
-            sections.append(line)
-        else:
-            sections[-1] = sections[-1] + " " + line
-    total = ""
-    for sec in sections:
-        if (sec != ""):
-            sec = sec.replace("&","&amp;")
-            sec = sec.replace("<","&lt;")
-            sec = sec.replace(">","&gt;")
-            sec = sec.replace("  "," ")
-            sec = sec.replace("  "," ")
-            if (delsection != None) and (delsection.match(sec)):
-                included[sec] = 1
-            if sec not in included:
-                included[sec] = 1
-                total = total + sec + "<br>\n"
-    return total
-
-def linkTo(link, text):
-    return '<a href="' + link + '">' + text + '</a>'
-
-def convertToPythonFn(fn):
-    result = ""
-    lastc = 0
-    for c in fn:
-        if (c!="_"):
-            if (lastc=="_"):
-                result = result + c.upper()
-            else:
-                result = result + c
-        lastc = c
-    return result
-
-def removeFileLicense(content):
-    # Removes the license part at the top of a file.
-    return re.sub(FILEHEADER, "", content).strip()
-
-########################################################################
-#
-# Interrogate Database Tokenizer
-#
-########################################################################
-
-class InterrogateTokenizer:
-    """
-    A big string, with a "parse pointer", and routines to
-    extract integers and strings.  The token syntax is that
-    used by interrogate databases.
-    """
-
-    def __init__(self, fn):
-        self.fn = fn
-        self.pos = 0
-        self.data = readFile(fn)
-
-    def readint(self):
-        neg = 0
-        while (self.data[self.pos].isspace()):
-            self.pos += 1
-        if (self.data[self.pos] == "-"):
-            neg = 1
-            self.pos += 1
-        if (self.data[self.pos].isdigit()==0):
-            print("File position " + str(self.pos))
-            print("Text: " + self.data[self.pos:self.pos+50])
-            sys.exit("Syntax error in interrogate file format 0")
-        value = 0
-        while (self.data[self.pos].isdigit()):
-            value = value*10 + int(self.data[self.pos])
-            self.pos += 1
-        if (neg): value = -value
-        return value
-
-    def readstring(self):
-        length = self.readint()
-        if (self.data[self.pos].isspace()==0):
-            sys.exit("Syntax error in interrogate file format 1")
-        self.pos += 1
-        body = self.data[self.pos:self.pos+length]
-        if (len(body) != length):
-            sys.exit("Syntax error in interrogate file format 2")
-        self.pos += length
-        return body
-
-########################################################################
-#
-# Interrogate Database Storage/Parsing
-#
-########################################################################
-
-def parseInterrogateIntVec(tokzr):
-    length = tokzr.readint()
-    result = []
-    for i in range(length):
-        result.append(tokzr.readint())
-    return result
-
-class InterrogateFunction:
-    def __init__(self, tokzr, db):
-        self.db = db
-        self.index = tokzr.readint()
-        self.componentname = tokzr.readstring()
-        self.flags = tokzr.readint()
-        self.classindex = tokzr.readint()
-        self.scopedname = tokzr.readstring()
-        self.cwrappers = parseInterrogateIntVec(tokzr)
-        self.pythonwrappers = parseInterrogateIntVec(tokzr)
-        self.comment = tokzr.readstring()
-        self.prototype = tokzr.readstring()
-
-class InterrogateEnumValue:
-    def __init__(self, tokzr):
-        self.name = tokzr.readstring()
-        self.scopedname = tokzr.readstring()
-        self.value = tokzr.readint()
-
-class InterrogateDerivation:
-    def __init__(self, tokzr):
-        self.flags = tokzr.readint()
-        self.base = tokzr.readint()
-        self.upcast = tokzr.readint()
-        self.downcast = tokzr.readint()
-
-class InterrogateType:
-    def __init__(self, tokzr, db):
-        self.db = db
-        self.index = tokzr.readint()
-        self.componentname = tokzr.readstring()
-        self.flags = tokzr.readint()
-        self.scopedname = tokzr.readstring()
-        self.truename = tokzr.readstring()
-        self.outerclass = tokzr.readint()
-        self.atomictype = tokzr.readint()
-        self.wrappedtype = tokzr.readint()
-        self.constructors = parseInterrogateIntVec(tokzr)
-        self.destructor = tokzr.readint()
-        self.elements = parseInterrogateIntVec(tokzr)
-        self.methods = parseInterrogateIntVec(tokzr)
-        self.casts = parseInterrogateIntVec(tokzr)
-        self.derivations = []
-        nderivations = tokzr.readint()
-        for i in range(nderivations):
-            self.derivations.append(InterrogateDerivation(tokzr))
-        self.enumvalues = []
-        nenumvalues = tokzr.readint()
-        for i in range(nenumvalues):
-            self.enumvalues.append(InterrogateEnumValue(tokzr))
-        self.nested = parseInterrogateIntVec(tokzr)
-        self.comment = tokzr.readstring()
-
-class InterrogateParameter:
-    def __init__(self, tokzr):
-        self.name = tokzr.readstring()
-        self.parameterflags = tokzr.readint()
-        self.type = tokzr.readint()
-
-class InterrogateWrapper:
-    def __init__(self, tokzr, db):
-        self.db = db
-        self.index = tokzr.readint()
-        self.componentname = tokzr.readstring()
-        self.flags = tokzr.readint()
-        self.function = tokzr.readint()
-        self.returntype = tokzr.readint()
-        self.returnvaluedestructor = tokzr.readint()
-        self.uniquename = tokzr.readstring()
-        self.parameters = []
-        nparameters = tokzr.readint()
-        for i in range(nparameters):
-            self.parameters.append(InterrogateParameter(tokzr))
-
-class InterrogateDatabase:
-    def __init__(self, tokzr):
-        self.fn = tokzr.fn
-        self.magic = tokzr.readint()
-        version1 = tokzr.readint()
-        version2 = tokzr.readint()
-        if (version1 != 2) or (version2 != 2):
-            sys.exit("This program only understands interrogate file format 2.2")
-        self.library = tokzr.readstring()
-        self.libhash = tokzr.readstring()
-        self.module = tokzr.readstring()
-        self.functions = {}
-        self.wrappers = {}
-        self.types = {}
-        self.namedtypes = {}
-        count_functions = tokzr.readint()
-        for i in range(count_functions):
-            fn = InterrogateFunction(tokzr, self)
-            self.functions[fn.index] = fn
-        count_wrappers = tokzr.readint()
-        for i in range(count_wrappers):
-            wr = InterrogateWrapper(tokzr, self)
-            self.wrappers[wr.index] = wr
-        count_types = tokzr.readint()
-        for i in range(count_types):
-            tp = InterrogateType(tokzr, self)
-            self.types[tp.index] = tp
-            self.namedtypes[tp.scopedname] = tp
-
-########################################################################
-#
-# Pattern Matching for Python Parse Trees
-#
-########################################################################
-
-def printTree(tree, indent):
-    spacing = "                                                        "[:indent]
-    if isinstance(tree, tuple) and isinstance(tree[0], int):
-        if tree[0] in symbol.sym_name:
-            for i in range(len(tree)):
-                if (i==0):
-                    print(spacing + "(symbol." + symbol.sym_name[tree[0]] + ",")
-                else:
-                    printTree(tree[i], indent+1)
-            print(spacing + "),")
-        elif tree[0] in token.tok_name:
-            print(spacing + "(token." + token.tok_name[tree[0]] + ", '" + tree[1] + "'),")
-        else:
-            print(spacing + str(tree))
-    else:
-        print(spacing + str(tree))
-
-
-COMPOUND_STMT_PATTERN = (
-    symbol.stmt,
-    (symbol.compound_stmt, ['compound'])
-    )
-
-
-DOCSTRING_STMT_PATTERN = (
-    symbol.stmt,
-    (symbol.simple_stmt,
-     (symbol.small_stmt,
-      (symbol.expr_stmt,
-       (symbol.testlist,
-        (symbol.test,
-         (symbol.or_test,
-           (symbol.and_test,
-            (symbol.not_test,
-             (symbol.comparison,
-              (symbol.expr,
-               (symbol.xor_expr,
-                (symbol.and_expr,
-                 (symbol.shift_expr,
-                  (symbol.arith_expr,
-                   (symbol.term,
-                    (symbol.factor,
-                     (symbol.power,
-                      (symbol.atom,
-                       (token.STRING, ['docstring'])
-                       ))))))))))))))))),
-     (token.NEWLINE, '')
-     ))
-
-DERIVATION_PATTERN = (
-    symbol.test,
-    (symbol.or_test,
-     (symbol.and_test,
-      (symbol.not_test,
-       (symbol.comparison,
-        (symbol.expr,
-         (symbol.xor_expr,
-          (symbol.and_expr,
-           (symbol.shift_expr,
-            (symbol.arith_expr,
-             (symbol.term,
-              (symbol.factor,
-               (symbol.power,
-                (symbol.atom,
-                 (token.NAME, ['classname'])
-   ))))))))))))))
-
-ASSIGNMENT_STMT_PATTERN = (
-    symbol.stmt,
-    (symbol.simple_stmt,
-     (symbol.small_stmt,
-      (symbol.expr_stmt,
-       (symbol.testlist,
-        (symbol.test,
-         (symbol.or_test,
-           (symbol.and_test,
-            (symbol.not_test,
-             (symbol.comparison,
-              (symbol.expr,
-               (symbol.xor_expr,
-                (symbol.and_expr,
-                 (symbol.shift_expr,
-                  (symbol.arith_expr,
-                   (symbol.term,
-                    (symbol.factor,
-                     (symbol.power,
-                      (symbol.atom,
-                       (token.NAME, ['varname']),
-       ))))))))))))))),
-       (token.EQUAL, '='),
-       (symbol.testlist, ['rhs']))),
-     (token.NEWLINE, ''),
-   ))
-
-class ParseTreeInfo:
-    docstring = ''
-    name = ''
-
-    def __init__(self, tree, name, file):
-        """
-        The code can be a string (in which case it is parsed), or it
-        can be in parse tree form already.
-        """
-        self.name = name
-        self.file = file
-        self.class_info = {}
-        self.function_info = {}
-        self.assign_info = {}
-        self.derivs = {}
-        if isinstance(tree, str):
-            try:
-                tree = parser.suite(tree+"\n").totuple()
-                if (tree):
-                    found, vars = self.match(DOCSTRING_STMT_PATTERN, tree[1])
-                    if found:
-                        self.docstring = vars["docstring"]
-            except:
-                print("CAUTION --- Parse failed: " + name)
-        if isinstance(tree, tuple):
-            self.extract_info(tree)
-
-    def match(self, pattern, data, vars=None):
-        """
-        pattern
-            Pattern to match against, possibly containing variables.
-        data
-            Data to be checked and against which variables are extracted.
-        vars
-            Dictionary of variables which have already been found.  If not
-            provided, an empty dictionary is created.
-
-        The `pattern' value may contain variables of the form ['varname']
-        which are allowed to parseTreeMatch anything.  The value that is
-        parseTreeMatched is returned as part of a dictionary which maps
-        'varname' to the parseTreeMatched value.  'varname' is not required
-        to be a string object, but using strings makes patterns and the code
-        which uses them more readable.  This function returns two values: a
-        boolean indicating whether a parseTreeMatch was found and a
-        dictionary mapping variable names to their associated values.
-        """
-        if vars is None:
-            vars = {}
-        if type(pattern) is list:       # 'variables' are ['varname']
-            vars[pattern[0]] = data
-            return 1, vars
-        if type(pattern) is not tuple:
-            return (pattern == data), vars
-        if len(data) != len(pattern):
-            return 0, vars
-        for pattern, data in map(None, pattern, data):
-            same, vars = self.match(pattern, data, vars)
-            if not same:
-                break
-        return same, vars
-
-    def extract_info(self, tree):
-        # extract docstring
-        found = 0
-        if len(tree) == 2:
-            found, vars = self.match(DOCSTRING_STMT_PATTERN[1], tree[1])
-        elif len(tree) >= 4:
-            found, vars = self.match(DOCSTRING_STMT_PATTERN, tree[3])
-        if found:
-            self.docstring = eval(vars['docstring'])
-        # discover inner definitions
-        for node in tree[1:]:
-            found, vars = self.match(ASSIGNMENT_STMT_PATTERN, node)
-            if found:
-                self.assign_info[vars['varname']] = 1
-            found, vars = self.match(COMPOUND_STMT_PATTERN, node)
-            if found:
-                cstmt = vars['compound']
-                if cstmt[0] == symbol.funcdef:
-                    name = cstmt[2][1]
-                    # Workaround for a weird issue with static and classmethods
-                    if name == "def":
-                        name = cstmt[3][1]
-                        self.function_info[name] = ParseTreeInfo(cstmt and cstmt[-1] or None, name, self.file)
-                        self.function_info[name].prototype = self.extract_tokens("", cstmt[4])
-                    else:
-                        self.function_info[name] = ParseTreeInfo(cstmt and cstmt[-1] or None, name, self.file)
-                        self.function_info[name].prototype = self.extract_tokens("", cstmt[3])
-                elif cstmt[0] == symbol.classdef:
-                    name = cstmt[2][1]
-                    self.class_info[name] = ParseTreeInfo(cstmt and cstmt[-1] or None, name, self.file)
-                    self.extract_derivs(self.class_info[name], cstmt)
-
-    def extract_derivs(self, classinfo, tree):
-        if (len(tree)==8):
-            derivs = tree[4]
-            for deriv in derivs[1:]:
-                found, vars = self.match(DERIVATION_PATTERN, deriv)
-                if (found):
-                    classinfo.derivs[vars["classname"]] = 1
-
-    def extract_tokens(self, str, tree):
-        if (isinstance(tree, tuple)):
-            if tree[0] in token.tok_name:
-                str = str + tree[1]
-                if (tree[1]==","): str=str+" "
-            elif tree[0] in symbol.sym_name:
-                for sub in tree[1:]:
-                    str = self.extract_tokens(str, sub)
-        return str
-
-########################################################################
-#
-# The code database contains:
-#
-#  - a list of InterrogateDatabase objects representing C++ modules.
-#  - a list of ParseTreeInfo objects representing python modules.
-#
-# Collectively, these make up all the data about all the code.
-#
-########################################################################
-
-class CodeDatabase:
-    def __init__(self, cxxlist, pylist):
-        self.types = {}
-        self.funcs = {}
-        self.goodtypes = {}
-        self.funcExports = {}
-        self.typeExports = {}
-        self.varExports = {}
-        self.globalfn = []
-        self.formattedprotos = {}
-        print("Reading C++ source files")
-        for cxx in cxxlist:
-            tokzr = InterrogateTokenizer(cxx)
-            idb = InterrogateDatabase(tokzr)
-            for type in idb.types.values():
-                if (type.flags & 8192) or type.scopedname not in self.types:
-                    self.types[type.scopedname] = type
-                if (type.flags & 8192) and (type.atomictype == 0) and (type.scopedname.count(" ")==0) and (type.scopedname.count(":")==0):
-                    self.goodtypes[type.scopedname] = type
-                    self.typeExports.setdefault("pandac.PandaModules", []).append(type.scopedname)
-            for func in idb.functions.values():
-                type = idb.types.get(func.classindex)
-                func.pyname = convertToPythonFn(func.componentname)
-                if (type == None):
-                    self.funcs["GLOBAL."+func.pyname] = func
-                    self.globalfn.append("GLOBAL."+func.pyname)
-                    self.funcExports.setdefault("pandac.PandaModules", []).append(func.pyname)
-                else:
-                    self.funcs[type.scopedname+"."+func.pyname] = func
-        print("Reading Python sources files")
-        for py in pylist:
-            pyinf = ParseTreeInfo(readFile(py), py, py)
-            mod = pathToModule(py)
-            for type in pyinf.class_info.keys():
-                typinf = pyinf.class_info[type]
-                self.types[type] = typinf
-                self.goodtypes[type] = typinf
-                self.typeExports.setdefault(mod, []).append(type)
-                for func in typinf.function_info.keys():
-                    self.funcs[type+"."+func] = typinf.function_info[func]
-            for func in pyinf.function_info.keys():
-                self.funcs["GLOBAL."+func] = pyinf.function_info[func]
-                self.globalfn.append("GLOBAL."+func)
-                self.funcExports.setdefault(mod, []).append(func)
-            for var in pyinf.assign_info.keys():
-                self.varExports.setdefault(mod, []).append(var)
-
-    def getClassList(self):
-        return list(self.goodtypes.keys())
-
-    def getGlobalFunctionList(self):
-        return self.globalfn
-
-    def getClassComment(self, cn):
-        type = self.types.get(cn)
-        if (isinstance(type, InterrogateType)):
-            return textToHTML(type.comment,"/")
-        elif (isinstance(type, ParseTreeInfo)):
-            return textToHTML(type.docstring,"#")
-        else:
-            return ""
-
-    def getClassParents(self, cn):
-        type = self.types.get(cn)
-        if (isinstance(type, InterrogateType)):
-            parents = []
-            for deriv in type.derivations:
-                basetype = type.db.types[deriv.base]
-                parents.append(basetype.scopedname)
-            return parents
-        elif (isinstance(type, ParseTreeInfo)):
-            return list(type.derivs.keys())
-        else:
-            return []
-
-    def getClassConstants(self, cn):
-        type = self.types.get(cn)
-        if (isinstance(type, InterrogateType)):
-            result = []
-            for subtype in type.nested:
-                enumtype = type.db.types[subtype]
-                if (len(enumtype.enumvalues)):
-                    for enumvalue in enumtype.enumvalues:
-                        name = convertToPythonFn(enumvalue.name)
-                        result.append((name, "("+enumtype.componentname+")"))
-                    result.append(("",""))
-            return result
-        else:
-            return []
-
-    def buildInheritance(self, inheritance, cn):
-        if (inheritance.count(cn) == 0):
-            inheritance.append(cn)
-            for parent in self.getClassParents(cn):
-                self.buildInheritance(inheritance, parent)
-
-    def getInheritance(self, cn):
-        inheritance = []
-        self.buildInheritance(inheritance, cn)
-        return inheritance
-
-    def getClassImport(self, cn):
-        type = self.types.get(cn)
-        if (isinstance(type, InterrogateType)):
-            return "pandac.PandaModules"
-        else:
-            return pathToModule(type.file)
-
-    def getClassConstructors(self, cn):
-        # Only detects C++ constructors, not Python constructors, since
-        # those are treated as ordinary methods.
-        type = self.types.get(cn)
-        result = []
-        if (isinstance(type, InterrogateType)):
-            for constructor in type.constructors:
-                func = type.db.functions[constructor]
-                if (func.classindex == type.index):
-                    result.append(type.scopedname+"."+func.pyname)
-        return result
-
-    def getClassMethods(self, cn):
-        type = self.types.get(cn)
-        result = []
-        if (isinstance(type, InterrogateType)):
-            for method in type.methods:
-                func = type.db.functions[method]
-                if (func.classindex == type.index):
-                    result.append(type.scopedname+"."+func.pyname)
-        elif (isinstance(type, ParseTreeInfo)):
-            for method in type.function_info.keys():
-                result.append(type.name + "." + method)
-        return result
-
-    def getFunctionName(self, fn):
-        func = self.funcs.get(fn)
-        if (isinstance(func, InterrogateFunction)):
-            return func.pyname
-        elif (isinstance(func, ParseTreeInfo)):
-            return func.name
-        else:
-            return fn
-
-    def getFunctionImport(self, fn):
-        func = self.funcs.get(fn)
-        if (isinstance(func, InterrogateFunction)):
-            return "pandac.PandaModules"
-        else:
-            return pathToModule(func.file)
-
-    def getFunctionPrototype(self, fn, urlprefix, urlsuffix):
-        func = self.funcs.get(fn)
-        if (isinstance(func, InterrogateFunction)):
-            if fn in self.formattedprotos:
-                proto = self.formattedprotos[fn]
-            else:
-                proto = func.prototype
-                proto = proto.replace(" inline "," ")
-                if (proto.startswith("inline ")): proto = proto[7:]
-                proto = proto.replace("basic_string< char >", "string")
-                proto = textToHTML(proto,"")
-                if "." in fn:
-                    for c in self.goodtypes.keys():
-                        if c != fn.split(".")[0] and (c in proto):
-                            proto = re.sub("\\b%s\\b" % c, linkTo(urlprefix+c+urlsuffix, c), proto)
-                self.formattedprotos[fn] = proto
-            return proto
-        elif (isinstance(func, ParseTreeInfo)):
-            return textToHTML("def "+func.name+func.prototype,"")
-        return fn
-
-    def getFunctionComment(self, fn):
-        func = self.funcs.get(fn)
-        if (isinstance(func, InterrogateFunction)):
-            return textToHTML(removeFileLicense(func.comment), "/", JUNKHEADER)
-        elif (isinstance(func, ParseTreeInfo)):
-            return textToHTML(func.docstring, "#")
-        return fn
-
-    def isFunctionPython(self, fn):
-        func = self.funcs.get(fn)
-        if (isinstance(func, InterrogateFunction)):
-            return False
-        elif (isinstance(func, ParseTreeInfo)):
-            return True
-        return False
-
-    def getFuncExports(self, mod):
-        return self.funcExports.get(mod, [])
-
-    def getTypeExports(self, mod):
-        return self.typeExports.get(mod, [])
-
-    def getVarExports(self, mod):
-        return self.varExports.get(mod, [])
-
-########################################################################
-#
-# The "Class Rename Dictionary" - Yech.
-#
-########################################################################
-
-CLASS_RENAME_DICT = {
-    # No longer used, now empty.
-}
-
-
-########################################################################
-#
-# HTML generation
-#
-########################################################################
-
-def makeCodeDatabase(indirlist, directdirlist):
-    if isinstance(directdirlist, str):
-        directdirlist = [directdirlist]
-    ignore = {}
-    ignore["__init__.py"] = 1
-    for directdir in directdirlist:
-        ignore[directdir + "/src/directscripts"] = 1
-        ignore[directdir + "/src/extensions"] = 1
-        ignore[directdir + "/src/extensions_native"] = 1
-        ignore[directdir + "/src/ffi"] = 1
-        ignore[directdir + "/built"] = 1
-    cxxfiles = []
-    pyfiles = []
-    findFiles(indirlist,     ".in", ignore, cxxfiles)
-    findFiles(directdirlist, ".py", ignore, pyfiles)
-    return CodeDatabase(cxxfiles, pyfiles)
-
-def generateFunctionDocs(code, method, urlprefix, urlsuffix):
-    name = code.getFunctionName(method)
-    proto = code.getFunctionPrototype(method, urlprefix, urlsuffix)
-    comment = code.getFunctionComment(method)
-    if (comment == ""): comment = "Undocumented function.<br>\n"
-    chunk = '<table bgcolor="e8e8e8" border=0 cellspacing=0 cellpadding=5 width="100%"><tr><td>' + "\n"
-    chunk = chunk + '<a name="' + name + '"><b>' + name + "</b></a><br>\n"
-    chunk = chunk + proto + "<br>\n"
-    chunk = chunk + comment
-    chunk = chunk + "</td></tr></table><br>\n"
-    return chunk
-
-def generateLinkTable(link, text, cols, urlprefix, urlsuffix):
-    column = (len(link)+cols-1)/cols
-    percent = 100 / cols
-    result = '<table width="100%">\n'
-    for i in range(column):
-        line = ""
-        for j in range(cols):
-            slot = i + column*j
-            linkval = ""
-            textval = ""
-            if (slot < len(link)): linkval = link[slot]
-            if (slot < len(text)): textval = text[slot]
-            if (i==0):
-                line = line + '<td width="' + str(percent) + '%">' + linkTo(urlprefix+linkval+urlsuffix, textval) + "</td>"
-            else:
-                line = line + '<td>' + linkTo(urlprefix+linkval+urlsuffix, textval) + "</td>"
-        result = result + "<tr>" + line + "</tr>\n"
-    result = result + "</table>\n"
-    return result
-
-def generate(pversion, indirlist, directdirlist, docdir, header, footer, urlprefix, urlsuffix):
-    code = makeCodeDatabase(indirlist, directdirlist)
-    classes = code.getClassList()[:]
-    classes.sort(None, str.lower)
-    xclasses = classes[:]
-    print("Generating HTML pages")
-    for type in classes:
-        body = "<h1>" + type + "</h1>\n"
-        comment = code.getClassComment(type)
-        body = body + "<ul>\nfrom " + code.getClassImport(type) + " import " + type + "</ul>\n\n"
-        body = body + "<ul>\n" + comment + "</ul>\n\n"
-        inheritance = code.getInheritance(type)
-        body = body + "<h2>Inheritance:</h2>\n<ul>\n"
-        for inh in inheritance:
-            line = "  " + linkTo(urlprefix+inh+urlsuffix, inh) + ": "
-            for parent in code.getClassParents(inh):
-                line = line + linkTo(urlprefix+parent+urlsuffix, parent) + " "
-            body = body + line + "<br>\n"
-        body = body + "</ul>\n"
-        for sclass in inheritance:
-            methods = code.getClassMethods(sclass)[:]
-            methods.sort(None, str.lower)
-            constructors = code.getClassConstructors(sclass)
-            if (len(methods) > 0 or len(constructors) > 0):
-                body = body + "<h2>Methods of "+sclass+":</h2>\n<ul>\n"
-                if len(constructors) > 0:
-                    fn = code.getFunctionName(constructors[0])
-                    body = body + '<a href="#' + fn + '">' + fn + " (Constructor)</a><br>\n"
-                for method in methods:
-                    fn = code.getFunctionName(method)
-                    body = body + '<a href="#' + fn + '">' + fn + "</a><br>\n"
-                body = body + "</ul>\n"
-        for sclass in inheritance:
-            enums = code.getClassConstants(sclass)[:]
-            if (len(enums) > 0):
-                body = body + "<h2>Constants in "+sclass+":</h2>\n<ul><table>\n"
-                for (value, comment) in enums:
-                    body = body + "<tr><td>" + value + "</td><td>" + comment + "</td></tr>\n"
-                body = body + "</table></ul>"
-        for sclass in inheritance:
-            constructors = code.getClassConstructors(sclass)
-            for constructor in constructors:
-                body = body + generateFunctionDocs(code, constructor, urlprefix, urlsuffix)
-            methods = code.getClassMethods(sclass)[:]
-            methods.sort(None, str.lower)
-            for method in methods:
-                body = body + generateFunctionDocs(code, method, urlprefix, urlsuffix)
-        body = header + body + footer
-        writeFile(docdir + "/" + type + ".html", body)
-        if type in CLASS_RENAME_DICT:
-            modtype = CLASS_RENAME_DICT[type]
-            writeFile(docdir + "/" + modtype + ".html", body)
-            xclasses.append(modtype)
-    xclasses.sort(None, str.lower)
-
-    index = "<h1>List of Classes - Panda " + pversion + "</h1>\n"
-    index = index + generateLinkTable(xclasses, xclasses, 3, urlprefix, urlsuffix)
-    fnlist = code.getGlobalFunctionList()[:]
-    fnlist.sort(None, str.lower)
-    fnnames = []
-    for i in range(len(fnlist)):
-        fnnames.append(code.getFunctionName(fnlist[i]))
-    index = header + index + footer
-    writeFile(docdir + "/classes.html", index)
-
-    index = "<h1>List of Global Functions - Panda " + pversion + "</h1>\n"
-    index = index + generateLinkTable(fnnames, fnnames, 3,"#","")
-    for func in fnlist:
-        index = index + generateFunctionDocs(code, func, urlprefix, urlsuffix)
-    index = header + index + footer
-    writeFile(docdir + "/functions.html", index)
-
-    table = {}
-    for type in classes:
-        for method in code.getClassMethods(type)[:]:
-            name = code.getFunctionName(method)
-            prefix = name[0].upper()
-            if prefix not in table:
-                table[prefix] = {}
-            if name not in table[prefix]:
-                table[prefix][name] = []
-            table[prefix][name].append(type)
-
-    index = "<h1>List of Methods - Panda " + pversion + "</h1>\n"
-
-    prefixes = list(table.keys())
-    prefixes.sort(None, str.lower)
-    for prefix in prefixes:
-        index = index + linkTo("#"+prefix, prefix) + " "
-    index = index + "<br><br>"
-    for prefix in prefixes:
-        index = index + '<a name="' + prefix + '">' + "\n"
-        names = list(table[prefix].keys())
-        names.sort(None, str.lower)
-        for name in names:
-            line = '<b>' + name + ":</b><ul>\n"
-            ctypes = table[prefix][name]
-            ctypes.sort(None, str.lower)
-            for type in ctypes:
-                line = line + "<li>" + linkTo(urlprefix+type+urlsuffix+"#"+name, type) + "\n"
-            line = line + "</ul>\n"
-            index = index + line + "\n"
-    index = header + index + footer
-    writeFile(docdir + "/methods.html", index)
-
-    index = "<h1>Panda " + pversion + "</h1>\n"
-    index = index + "<ul>\n"
-    index = index + "<li>" + linkTo(urlprefix+"classes"+urlsuffix, "List of all Classes") + "\n"
-    index = index + "</ul>\n"
-    index = index + "<ul>\n"
-    index = index + "<li>" + linkTo(urlprefix+"functions"+urlsuffix, "List of all Global Functions") + "\n"
-    index = index + "</ul>\n"
-    index = index + "<ul>\n"
-    index = index + "<li>" + linkTo(urlprefix+"methods"+urlsuffix, "List of all Methods (very long)") + "\n"
-    index = index + "</ul>\n"
-    writeFile(docdir + "/index.html", index)
-
-
-########################################################################
-#
-# IMPORT repair
-#
-########################################################################
-
-def expandImports(indirlist, directdirlist, fixdirlist):
-    code = makeCodeDatabase(indirlist, directdirlist)
-    fixfiles = []
-    findFiles(fixdirlist, ".py", {}, fixfiles)
-    for fixfile in fixfiles:
-        if (os.path.isfile(fixfile+".orig")):
-            text = readFile(fixfile+".orig")
-        else:
-            text = readFile(fixfile)
-            writeFile(fixfile+".orig", text)
-        text = text.replace("\r","")
-        lines = text.split("\n")
-        used = {}
-        for id in IDENTIFIER.findall(text):
-            used[id] = 1
-        result = []
-        for line in lines:
-            mat = IMPORTSTAR.match(line)
-            if (mat):
-                module = mat.group(1)
-                if (fixfile.count("/")!=0) and (module.count(".")==0):
-                    modfile = os.path.dirname(fixfile)+"/"+module+".py"
-                    if (os.path.isfile(modfile)):
-                        module = pathToModule(modfile)
-                typeExports = code.getTypeExports(module)
-                funcExports = code.getFuncExports(module)
-                varExports = code.getVarExports(module)
-                if (len(typeExports)+len(funcExports)+len(varExports)==0):
-                    result.append(line)
-                    print(fixfile + " : " + module + " : no exports")
-                else:
-                    print(fixfile + " : " + module + " : repairing")
-                    for x in funcExports:
-                        fn = code.getFunctionName(x)
-                        if fn in used:
-                            result.append("from "+module+" import "+fn)
-                    for x in typeExports:
-                        if x in used:
-                            result.append("from "+module+" import "+x)
-                    for x in varExports:
-                        if x in used:
-                            result.append("from "+module+" import "+x)
-            else:
-                result.append(line)
-        writeFileLines(fixfile, result)

+ 16 - 16
direct/src/directtools/DirectCameraControl.py

@@ -6,6 +6,7 @@ from .DirectSelection import SelectionRay
 from direct.interval.IntervalGlobal import Sequence, Func
 from direct.directnotify import DirectNotifyGlobal
 from direct.task import Task
+from direct.task.TaskManagerGlobal import taskMgr
 
 CAM_MOVE_DURATION = 1.2
 COA_MARKER_SF = 0.0075
@@ -22,7 +23,7 @@ class DirectCameraControl(DirectObject):
         self.orthoViewRoll = 0.0
         self.lastView = 0
         self.coa = Point3(0, 100, 0)
-        self.coaMarker = loader.loadModel('models/misc/sphere')
+        self.coaMarker = base.loader.loadModel('models/misc/sphere')
         self.coaMarker.setName('DirectCameraCOAMarker')
         self.coaMarker.setTransparency(1)
         self.coaMarker.setColor(1, 0, 0, 0)
@@ -128,8 +129,8 @@ class DirectCameraControl(DirectObject):
             # Hide the marker for this kind of motion
             self.coaMarker.hide()
             # Record time of start of mouse interaction
-            self.startT= globalClock.getFrameTime()
-            self.startF = globalClock.getFrameCount()
+            self.startT = base.clock.getFrameTime()
+            self.startF = base.clock.getFrameCount()
             # If the cam is orthogonal, spawn differentTask
             if hasattr(base.direct, "manipulationControl") and base.direct.manipulationControl.fMultiView and\
                base.direct.camera.getName() != 'persp':
@@ -150,7 +151,7 @@ class DirectCameraControl(DirectObject):
     def __startManipulateCamera(self, func = None, task = None, ival = None):
         self.__stopManipulateCamera()
         if func:
-            assert(task is None)
+            assert task is None
             task = Task.Task(func)
         if task:
             self.manipulateCameraTask = taskMgr.add(task, 'manipulateCamera')
@@ -168,8 +169,8 @@ class DirectCameraControl(DirectObject):
             # Hide the marker for this kind of motion
             self.coaMarker.hide()
             # Record time of start of mouse interaction
-            self.startT= globalClock.getFrameTime()
-            self.startF = globalClock.getFrameCount()
+            self.startT = base.clock.getFrameTime()
+            self.startF = base.clock.getFrameCount()
             # Start manipulation
             # If the cam is orthogonal, spawn differentTask
             if hasattr(base.direct, "manipulationControl") and base.direct.manipulationControl.fMultiView and\
@@ -185,8 +186,8 @@ class DirectCameraControl(DirectObject):
                 # Hide the marker for this kind of motion
                 self.coaMarker.hide()
                 # Record time of start of mouse interaction
-                self.startT= globalClock.getFrameTime()
-                self.startF = globalClock.getFrameCount()
+                self.startT = base.clock.getFrameTime()
+                self.startF = base.clock.getFrameCount()
                 # Start manipulation
                 self.spawnXZTranslateOrHPanYZoom()
                 # END MOUSE IN CENTRAL REGION
@@ -203,9 +204,9 @@ class DirectCameraControl(DirectObject):
 
     def mouseFlyStop(self):
         self.__stopManipulateCamera()
-        stopT = globalClock.getFrameTime()
+        stopT = base.clock.getFrameTime()
         deltaT = stopT - self.startT
-        stopF = globalClock.getFrameCount()
+        stopF = base.clock.getFrameCount()
         deltaF = stopF - self.startF
         ## No reason this shouldn't work with Maya cam on
         # if not self.useMayaCamControls and (deltaT <= 0.25) or (deltaF <= 1):
@@ -366,7 +367,7 @@ class DirectCameraControl(DirectObject):
                                 moveDir[2],
                                 hVal,
                                 0.0, 0.0)
-        if (self.lockRoll == True):
+        if self.lockRoll:
             # flatten roll
             base.direct.camera.setR(0)
 
@@ -449,7 +450,7 @@ class DirectCameraControl(DirectObject):
                                  (deltaX * base.direct.dr.fovH),
                                  (-deltaY * base.direct.dr.fovV),
                                  0.0)
-            if (self.lockRoll == True):
+            if self.lockRoll:
                 # flatten roll
                 base.direct.camera.setR(0)
             self.camManipRef.setPos(self.coaMarkerPos)
@@ -466,7 +467,7 @@ class DirectCameraControl(DirectObject):
                                     (deltaY * 180.0),
                                     0.0)
 
-            if (self.lockRoll == True):
+            if self.lockRoll:
                 # flatten roll
                 self.camManipRef.setR(0)
             base.direct.camera.setTransform(self.camManipRef, wrt)
@@ -491,7 +492,7 @@ class DirectCameraControl(DirectObject):
         deltaAngle = angle - state.lastAngle
         state.lastAngle = angle
         self.camManipRef.setHpr(self.camManipRef, 0, 0, deltaAngle)
-        if (self.lockRoll == True):
+        if self.lockRoll:
             # flatten roll
             self.camManipRef.setR(0)
         base.direct.camera.setTransform(self.camManipRef, wrt)
@@ -576,7 +577,7 @@ class DirectCameraControl(DirectObject):
         if not coaDist:
             coaDist = Vec3(self.coa - ZERO_POINT).length()
         # Place the marker in render space
-        if ref == None:
+        if ref is None:
             # KEH: use the current display region
             # ref = base.cam
             ref = base.direct.drList.getCurrentDr().cam
@@ -892,4 +893,3 @@ class DirectCameraControl(DirectObject):
 
     def removeManipulateCameraTask(self):
         self.__stopManipulateCamera()
-

Някои файлове не бяха показани, защото твърде много файлове са промени