瀏覽代碼

Merge branch 'release/1.10.x'

rdb 6 年之前
父節點
當前提交
0ac36185a9

+ 18 - 3
direct/src/gui/DirectOptionMenu.py

@@ -22,10 +22,12 @@ class DirectOptionMenu(DirectButton):
             # List of items to display on the popup menu
             ('items',       [],             self.setItems),
             # Initial item to display on menu button
-            # Can be an interger index or the same string as the button
+            # Can be an integer index or the same string as the button
             ('initialitem', None,           DGG.INITOPT),
             # Amount of padding to place around popup button indicator
             ('popupMarkerBorder', (.1, .1), None),
+            # The initial position of the popup marker
+            ('popupMarker_pos', (0, 0, 0), None),
             # Background color to use to highlight popup menu items
             ('highlightColor', (.5, .5, .5, 1), None),
             # Extra scale to use on highlight popup menu items
@@ -42,6 +44,8 @@ class DirectOptionMenu(DirectButton):
         DirectButton.__init__(self, parent)
         # Record any user specified frame size
         self.initFrameSize = self['frameSize']
+        # Record any user specified popup marker position
+        self.initPopupMarkerPos = self['popupMarker_pos']
         # Create a small rectangular marker to distinguish this button
         # as a popup menu button
         self.popupMarker = self.createcomponent(
@@ -168,8 +172,13 @@ class DirectOptionMenu(DirectButton):
         else:
             # Or base it upon largest item
             bounds = [self.minX, self.maxX, self.minZ, self.maxZ]
-        pm.setPos(bounds[1] + pmw/2.0, 0,
-                  bounds[2] + (bounds[3] - bounds[2])/2.0)
+        if self.initPopupMarkerPos:
+            # Use specified position
+            pmPos = list(self.initPopupMarkerPos)
+        else:
+            # Or base the position on the frame size.
+            pmPos = [bounds[1] + pmw/2.0, 0, bounds[2] + (bounds[3] - bounds[2])/2.0]
+        pm.setPos(pmPos[0], pmPos[1], pmPos[2])
         # Adjust popup menu button to fit all items (or use user specified
         # frame size
         bounds[1] += pmw
@@ -184,6 +193,12 @@ class DirectOptionMenu(DirectButton):
         Adjust popup position if default position puts it outside of
         visible screen region
         """
+
+        # Needed attributes (such as minZ) won't be set unless the user has specified
+        # items to display. Let's assert that we've given items to work with.
+        items = self['items']
+        assert items and len(items) > 0, 'Cannot show an empty popup menu! You must add items!'
+
         # Show the menu
         self.popupMenu.show()
         # Make sure its at the right scale

+ 2 - 3
direct/src/showbase/SfxPlayer.py

@@ -53,6 +53,8 @@ class SfxPlayer:
                 d = node.getDistance(listenerNode)
             else:
                 d = node.getDistance(base.cam)
+        if not cutoff:
+            cutoff = self.cutoffDistance
         if d == None or d > cutoff:
             volume = 0
         else:
@@ -70,9 +72,6 @@ class SfxPlayer:
             self, sfx, looping = 0, interrupt = 1, volume = None,
             time = 0.0, node=None, listenerNode = None, cutoff = None):
         if sfx:
-            if not cutoff:
-                cutoff = self.cutoffDistance
-
             self.setFinalVolume(sfx, node, volume, listenerNode, cutoff)
 
             # don't start over if it's already playing, unless

+ 8 - 4
direct/src/tkpanels/AnimPanel.py

@@ -9,13 +9,16 @@ __all__ = ['AnimPanel', 'ActorControl']
 # Import Tkinter, Pmw, and the floater code from this directory tree.
 from direct.tkwidgets.AppShell import *
 from direct.showbase.TkGlobal import *
-import Pmw, sys
+import Pmw, sys, os
 from direct.task import Task
+from panda3d.core import Filename, getModelPath
 
 if sys.version_info >= (3, 0):
     from tkinter.simpledialog import askfloat
+    from tkinter.filedialog import askopenfilename
 else:
     from tkSimpleDialog import askfloat
+    from tkFileDialog import askopenfilename
 
 
 FRAMES = 0
@@ -273,7 +276,7 @@ class AnimPanel(AppShell):
             title = 'Load Animation',
             parent = self.component('hull')
             )
-        if (animFilename == ''):
+        if not animFilename:
             # no file selected, canceled
             return
 
@@ -369,8 +372,9 @@ class AnimPanel(AppShell):
     def destroy(self):
         # First clean up
         taskMgr.remove(self.id + '_UpdateTask')
-        self.destroyCallBack()
-        self.destroyCallBack = None
+        if self.destroyCallBack is not None:
+            self.destroyCallBack()
+            self.destroyCallBack = None
         AppShell.destroy(self)
 
 class ActorControl(Pmw.MegaWidget):

+ 32 - 17
direct/src/tkwidgets/Valuator.py

@@ -656,25 +656,35 @@ def rgbPanel(nodePath, callback = None, style = 'mini'):
     pButton.pack(expand = 1, fill = BOTH)
 
     # Update menu
-    menu = vgp.component('menubar').component('Valuator Group-menu')
+    menubar = vgp.component('menubar')
+    menubar.deletemenuitems('Valuator Group', 1, 1)
+
     # Some helper functions
     # Clear color
-    menu.insert_command(index = 1, label = 'Clear Color',
-                        command = lambda: nodePath.clearColor())
+    menubar.addmenuitem(
+        'Valuator Group', 'command',
+        label='Clear Color', command=lambda: nodePath.clearColor())
     # Set Clear Transparency
-    menu.insert_command(index = 2, label = 'Set Transparency',
-                        command = lambda: nodePath.setTransparency(1))
-    menu.insert_command(
-        index = 3, label = 'Clear Transparency',
-        command = lambda: nodePath.clearTransparency())
+    menubar.addmenuitem(
+        'Valuator Group', 'command',
+        label='Set Transparency', command=lambda: nodePath.setTransparency(1))
+    menubar.addmenuitem(
+        'Valuator Group', 'command',
+        label='Clear Transparency', command=lambda: nodePath.clearTransparency())
 
 
     # System color picker
-    menu.insert_command(index = 4, label = 'Popup Color Picker',
-                        command = popupColorPicker)
+    menubar.addmenuitem(
+        'Valuator Group', 'command',
+        label='Popup Color Picker', command=popupColorPicker)
+
+    menubar.addmenuitem(
+        'Valuator Group', 'command',
+        label='Print to log', command=printToLog)
 
-    menu.insert_command(index = 5, label = 'Print to log',
-                        command = printToLog)
+    menubar.addmenuitem(
+        'Valuator Group', 'command', 'Dismiss Valuator Group panel',
+        label='Dismiss', command=vgp.destroy)
 
     def setNodePathColor(color):
         nodePath.setColor(color[0]/255.0, color[1]/255.0,
@@ -724,18 +734,23 @@ def lightRGBPanel(light, style = 'mini'):
     # Update menu button
     vgp.component('menubar').component('Valuator Group-button')['text'] = (
         'Light Control Panel')
+
     # Add a print button which will also serve as a color tile
     pButton = Button(vgp.interior(), text = 'Print to Log',
                      bg = getTkColorString(initColor),
                      command = printToLog)
     pButton.pack(expand = 1, fill = BOTH)
+
     # Update menu
-    menu = vgp.component('menubar').component('Valuator Group-menu')
+    menubar = vgp.component('menubar')
     # System color picker
-    menu.insert_command(index = 4, label = 'Popup Color Picker',
-                        command = popupColorPicker)
-    menu.insert_command(index = 5, label = 'Print to log',
-                        command = printToLog)
+    menubar.addmenuitem(
+        'Valuator Group', 'command',
+        label='Popup Color Picker', command=popupColorPicker)
+    menubar.addmenuitem(
+        'Valuator Group', 'command',
+        label='Print to log', command=printToLog)
+
     def setLightColor(color):
         light.setColor(Vec4(color[0]/255.0, color[1]/255.0,
                             color[2]/255.0, color[3]/255.0))

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

@@ -4952,13 +4952,14 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       expected_params += "NoneType";
 
     } else if (TypeManager::is_char(type)) {
-      indent(out, indent_level) << "char " << param_name << default_expr << ";\n";
+      indent(out, indent_level) << "char *" << param_name << "_str;\n";
+      indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n";
 
-      format_specifiers += "c";
-      parameter_list += ", &" + param_name;
+      format_specifiers += "s#";
+      parameter_list += ", &" + param_name + "_str, &" + param_name + "_len";
+      extra_param_check << " && " << param_name << "_len == 1";
 
-      // extra_param_check << " && isascii(" << param_name << ")";
-      pexpr_string = "(char) " + param_name;
+      pexpr_string = param_name + "_str[0]";
       expected_params += "char";
       only_pyobjects = false;
 

+ 16 - 13
makepanda/test_wheel.py

@@ -17,31 +17,34 @@ from optparse import OptionParser
 def test_wheel(wheel, verbose=False):
     envdir = tempfile.mkdtemp(prefix="venv-")
     print("Setting up virtual environment in {0}".format(envdir))
+    sys.stdout.flush()
 
+    # Make sure pip is up-to-date first.
+    subprocess.call([sys.executable, "-B", "-m", "pip", "install", "-U", "pip"])
+
+    # Create a virtualenv.
     if sys.version_info >= (3, 0):
-        subprocess.call([sys.executable, "-m", "venv", "--clear", envdir])
+        subprocess.call([sys.executable, "-B", "-m", "venv", "--clear", envdir])
     else:
-        subprocess.call([sys.executable, "-m", "virtualenv", "--clear", envdir])
+        subprocess.call([sys.executable, "-B", "-m", "virtualenv", "--clear", envdir])
 
-    # Make sure pip is up-to-date first.
-    if subprocess.call([sys.executable, "-m", "pip", "install", "-U", "pip"]) != 0:
+    # Determine the path to the Python interpreter.
+    if sys.platform == "win32":
+        python = os.path.join(envdir, "Scripts", "python.exe")
+    else:
+        python = os.path.join(envdir, "bin", "python")
+
+    # Upgrade pip inside the environment too.
+    if subprocess.call([python, "-m", "pip", "install", "-U", "pip"]) != 0:
         shutil.rmtree(envdir)
         sys.exit(1)
 
     # Install pytest into the environment, as well as our wheel.
-    if sys.platform == "win32":
-        pip = os.path.join(envdir, "Scripts", "pip.exe")
-    else:
-        pip = os.path.join(envdir, "bin", "pip")
-    if subprocess.call([pip, "install", "pytest", wheel]) != 0:
+    if subprocess.call([python, "-m", "pip", "install", "pytest", wheel]) != 0:
         shutil.rmtree(envdir)
         sys.exit(1)
 
     # Run the test suite.
-    if sys.platform == "win32":
-        python = os.path.join(envdir, "Scripts", "python.exe")
-    else:
-        python = os.path.join(envdir, "bin", "python")
     test_cmd = [python, "-m", "pytest", "tests"]
     if verbose:
         test_cmd.append("--verbose")

+ 2 - 0
panda/src/collide/collisionTraverser.h

@@ -51,6 +51,8 @@ PUBLISHED:
   INLINE bool get_respect_prev_transform() const;
   MAKE_PROPERTY(respect_prev_transform, get_respect_prev_transform,
                                         set_respect_prev_transform);
+  MAKE_PROPERTY(respect_prev_transform, get_respect_prev_transform,
+                                        set_respect_prev_transform);
 
   void add_collider(const NodePath &collider, CollisionHandler *handler);
   bool remove_collider(const NodePath &collider);

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

@@ -62,6 +62,9 @@ enum QuirkBits {
   // We only connect it if it is reporting any events, because when Steam is
   // running, the Steam controller is muted in favour of a dummy Xbox device.
   QB_steam_controller = 32,
+
+  // Axes on the right stick are swapped, using x for y and vice versa.
+  QB_right_axes_swapped = 64,
 };
 
 static const struct DeviceMapping {
@@ -81,7 +84,7 @@ static const struct DeviceMapping {
   // Steam Controller (wireless)
   {0x28de, 0x1142, InputDevice::DeviceClass::unknown, QB_steam_controller},
   // Jess Tech Colour Rumble Pad
-  {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, 0},
+  {0x0f30, 0x0111, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_right_axes_swapped},
   // SPEED Link SL-6535-SBK-01
   {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, 0},
   // 8bitdo N30 Pro Controller
@@ -488,7 +491,11 @@ init_device() {
           break;
         case ABS_Z:
           if (quirks & QB_rstick_from_z) {
-            axis = InputDevice::Axis::right_x;
+            if (quirks & QB_right_axes_swapped) {
+              axis = InputDevice::Axis::right_y;
+            } else {
+              axis = InputDevice::Axis::right_x;
+            }
           } else if (_device_class == DeviceClass::gamepad) {
             axis = InputDevice::Axis::left_trigger;
             have_analog_triggers = true;
@@ -514,7 +521,11 @@ init_device() {
           break;
         case ABS_RZ:
           if (quirks & QB_rstick_from_z) {
-            axis = InputDevice::Axis::right_y;
+            if (quirks & QB_right_axes_swapped) {
+              axis = InputDevice::Axis::right_x;
+            } else {
+              axis = InputDevice::Axis::right_y;
+            }
           } else if (_device_class == DeviceClass::gamepad) {
             axis = InputDevice::Axis::right_trigger;
             have_analog_triggers = true;

+ 42 - 7
panda/src/device/winRawInputDevice.cxx

@@ -32,6 +32,12 @@ enum QuirkBits : int {
 
   // Throttle is reversed.
   QB_reversed_throttle = 4,
+
+  // Right stick uses Z and Rz inputs.
+  QB_rstick_from_z = 8,
+
+  // Axes on the right stick are swapped, using x for y and vice versa.
+  QB_right_axes_swapped = 64,
 };
 
 // Some nonstandard gamepads have different button mappings.
@@ -42,12 +48,17 @@ static const struct DeviceMapping {
   int quirks;
   const char *buttons[16];
 } mapping_presets[] = {
-  // SNES-style USB gamepad
+  // SNES-style USB gamepad, or cheap unbranded USB gamepad with no sticks
+  // ABXY are mapped based on their position, not based on their label.
   {0x0810, 0xe501, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers,
-    {"face_x", "face_a", "face_b", "face_y", "lshoulder", "rshoulder", "none", "none", "back", "start"}
+    {"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start"}
+  },
+  // Unbranded generic cheap USB gamepad
+  {0x0810, 0x0001, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_no_analog_triggers | QB_right_axes_swapped,
+    {"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start", "lstick", "rstick"}
   },
-  // SPEED Link SL-6535-SBK-01
-  {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, QB_no_analog_triggers,
+  // Trust GXT 24 / SPEED Link SL-6535-SBK-01
+  {0x0079, 0x0006, InputDevice::DeviceClass::gamepad, QB_rstick_from_z | QB_no_analog_triggers,
     {"face_y", "face_b", "face_a", "face_x", "lshoulder", "rshoulder", "ltrigger", "rtrigger", "back", "start", "lstick", "rstick"}
   },
   // T.Flight Hotas X
@@ -56,7 +67,7 @@ static const struct DeviceMapping {
   },
   // NVIDIA Shield Controller
   {0x0955, 0x7214, InputDevice::DeviceClass::gamepad, 0,
-    {"face_a", "face_b", "n", "face_x", "face_y", "rshoulder", "lshoulder", "rshoulder", "e", "f", "g", "start", "h", "lstick", "rstick", "i"}
+    {"face_a", "face_b", 0, "face_x", "face_y", "rshoulder", "lshoulder", "rshoulder", 0, 0, 0, "start", 0, "lstick", "rstick", 0}
   },
   {0},
 };
@@ -422,7 +433,14 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
           break;
         case HID_USAGE_GENERIC_Z:
           if (_device_class == DeviceClass::gamepad) {
-            if ((quirks & QB_no_analog_triggers) == 0) {
+            if (quirks & QB_rstick_from_z) {
+              if (quirks & QB_right_axes_swapped) {
+                axis = InputDevice::Axis::right_y;
+                swap(cap.LogicalMin, cap.LogicalMax);
+              } else {
+                axis = InputDevice::Axis::right_x;
+              }
+            } else if ((quirks & QB_no_analog_triggers) == 0) {
               axis = Axis::left_trigger;
             }
           } else if (_device_class == DeviceClass::flight_stick) {
@@ -455,7 +473,14 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
           break;
         case HID_USAGE_GENERIC_RZ:
           if (_device_class == DeviceClass::gamepad) {
-            if ((quirks & QB_no_analog_triggers) == 0) {
+            if (quirks & QB_rstick_from_z) {
+              if (quirks & QB_right_axes_swapped) {
+                axis = InputDevice::Axis::right_x;
+              } else {
+                axis = InputDevice::Axis::right_y;
+                swap(cap.LogicalMin, cap.LogicalMax);
+              }
+            } else if ((quirks & QB_no_analog_triggers) == 0) {
               axis = Axis::right_trigger;
             }
           } else {
@@ -481,6 +506,16 @@ on_arrival(HANDLE handle, const RID_DEVICE_INFO &info, std::string name) {
         break;
       }
 
+      // If this axis already exists, don't double-map it, but take the first
+      // one.  This is important for the Trust GXT 24 / SL-6535-SBK-01 which
+      // have a weird extra Z axis with DataIndex 2 that should be ignored.
+      for (size_t i = 0; i < _axes.size(); ++i) {
+        if (_axes[i].axis == axis) {
+          axis = Axis::none;
+          break;
+        }
+      }
+
       int axis_index;
       if (!is_signed) {
         // All axes on the weird XInput-style mappings go from -1 to 1

+ 0 - 8
panda/src/linmath/lmatrix4_src.I

@@ -484,13 +484,9 @@ get_col(int col) const {
  */
 INLINE_LINMATH FLOATNAME(LVecBase3) FLOATNAME(LMatrix4)::
 get_row3(int row) const {
-#ifdef HAVE_EIGEN
-  return FLOATNAME(LVecBase3)(_m.block<1, 3>(row, 0));
-#else
   return FLOATNAME(LVecBase3)((*this)(row, 0),
                               (*this)(row, 1),
                               (*this)(row, 2));
-#endif  // HAVE_EIGEN
 }
 
 /**
@@ -514,13 +510,9 @@ get_row3(FLOATNAME(LVecBase3) &result_vec,int row) const {
  */
 INLINE_LINMATH FLOATNAME(LVecBase3) FLOATNAME(LMatrix4)::
 get_col3(int col) const {
-#ifdef HAVE_EIGEN
-  return FLOATNAME(LVecBase3)(_m.block<1, 3>(0, col));
-#else
   return FLOATNAME(LVecBase3)((*this)(0, col),
                               (*this)(1, col),
                               (*this)(2, col));
-#endif  // HAVE_EIGEN
 }
 
 /**

+ 1 - 1
panda/src/movies/vorbisAudioCursor.cxx

@@ -104,7 +104,7 @@ seek(double t) {
   if (result == OV_ENOSEEK && t == 0.0) {
     std::istream *stream = (std::istream *)_ov.datasource;
 
-    if (stream->rdbuf()->pubseekpos(0, std::ios::in) == 0) {
+    if (stream->rdbuf()->pubseekpos(0, std::ios::in) == (std::streampos)0) {
       // Back up the callbacks, then destroy the stream, making sure to first
       // unset the datasource so that it won't close the file.
       ov_callbacks callbacks = _ov.callbacks;

+ 1 - 1
panda/src/movies/wavAudioCursor.cxx

@@ -315,7 +315,7 @@ seek(double t) {
     } else if (pos < current) {
       // Can we seek to the beginning?  Some streams, such as ZStream, let us
       // rewind the stream.
-      if (buf->pubseekpos(0, std::ios::in) == 0) {
+      if (buf->pubseekpos(0, std::ios::in) == (std::streampos)0) {
         if (pos > _data_start && movies_cat.is_info()) {
           Filename fn = get_source()->get_filename();
           movies_cat.info()

+ 36 - 0
tests/linmath/test_lmatrix4.py

@@ -87,3 +87,39 @@ def test_mat4_invert_correct(type):
 
     assert (mat * inv).is_identity()
     assert (inv * mat).is_identity()
+
+
[email protected]("type", (core.LMatrix4d, core.LMatrix4f))
+def test_mat4_rows(type):
+    mat = type((1, 2, 3, 4,
+                5, 6, 7, 8,
+                9, 10, 11, 12,
+                13, 14, 15, 16))
+
+    assert mat.rows[0] == (1, 2, 3, 4)
+    assert mat.rows[1] == (5, 6, 7, 8)
+    assert mat.rows[2] == (9, 10, 11, 12)
+    assert mat.rows[3] == (13, 14, 15, 16)
+
+    assert mat.get_row3(0) == (1, 2, 3)
+    assert mat.get_row3(1) == (5, 6, 7)
+    assert mat.get_row3(2) == (9, 10, 11)
+    assert mat.get_row3(3) == (13, 14, 15)
+
+
[email protected]("type", (core.LMatrix4d, core.LMatrix4f))
+def test_mat4_cols(type):
+    mat = type((1, 5, 9, 13,
+                2, 6, 10, 14,
+                3, 7, 11, 15,
+                4, 8, 12, 16))
+
+    assert mat.cols[0] == (1, 2, 3, 4)
+    assert mat.cols[1] == (5, 6, 7, 8)
+    assert mat.cols[2] == (9, 10, 11, 12)
+    assert mat.cols[3] == (13, 14, 15, 16)
+
+    assert mat.get_col3(0) == (1, 2, 3)
+    assert mat.get_col3(1) == (5, 6, 7)
+    assert mat.get_col3(2) == (9, 10, 11)
+    assert mat.get_col3(3) == (13, 14, 15)