Browse Source

Merge remote-tracking branch 'origin/release/1.10.x'

rdb 5 years ago
parent
commit
707ea089cb

+ 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) == '\\') {
     if ((*pi) == quote_mark || (*pi) == '\\') {
       out << '\\' << (*pi);
       out << '\\' << (*pi);
 
 
-    } else if (!isprint(*pi)) {
+    } else if (!isprint(*pi) || (*pi) == '\t') {
       char buffer[10];
       char buffer[10];
       sprintf(buffer, "%02x", (unsigned char)(*pi));
       sprintf(buffer, "%02x", (unsigned char)(*pi));
       out << "\\x" << buffer;
       out << "\\x" << buffer;

+ 2 - 19
direct/src/fsm/State.py

@@ -33,15 +33,13 @@ class State(DirectObject):
                     if enterFunc.__func__ == oldFunction:
                     if enterFunc.__func__ == oldFunction:
                         # print 'found: ', enterFunc, oldFunction
                         # print 'found: ', enterFunc, oldFunction
                         state.setEnterFunc(types.MethodType(newFunction,
                         state.setEnterFunc(types.MethodType(newFunction,
-                                                            enterFunc.__self__,
-                                                            enterFunc.__self__.__class__))
+                                                            enterFunc.__self__))
                         count += 1
                         count += 1
                 if type(exitFunc) == types.MethodType:
                 if type(exitFunc) == types.MethodType:
                     if exitFunc.__func__ == oldFunction:
                     if exitFunc.__func__ == oldFunction:
                         # print 'found: ', exitFunc, oldFunction
                         # print 'found: ', exitFunc, oldFunction
                         state.setExitFunc(types.MethodType(newFunction,
                         state.setExitFunc(types.MethodType(newFunction,
-                                                           exitFunc.__self__,
-                                                           exitFunc.__self__.__class__))
+                                                           exitFunc.__self__))
                         count += 1
                         count += 1
             return count
             return count
 
 
@@ -215,18 +213,3 @@ class State(DirectObject):
     def __str__(self):
     def __str__(self):
         return "State: name = %s, enter = %s, exit = %s, trans = %s, children = %s" %\
         return "State: name = %s, enter = %s, exit = %s, trans = %s, children = %s" %\
                (self.__name, self.__enterFunc, self.__exitFunc, self.__transitions, self.__FSMList)
                (self.__name, self.__enterFunc, self.__exitFunc, self.__transitions, self.__FSMList)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

+ 1 - 2
direct/src/interval/FunctionInterval.py

@@ -37,8 +37,7 @@ class FunctionInterval(Interval.Interval):
                     if ival.function.__func__ == oldFunction:
                     if ival.function.__func__ == oldFunction:
                         # print 'found: ', ival.function, oldFunction
                         # print 'found: ', ival.function, oldFunction
                         ival.function = types.MethodType(newFunction,
                         ival.function = types.MethodType(newFunction,
-                                                         ival.function.__self__,
-                                                         ival.function.__self__.__class__)
+                                                         ival.function.__self__)
                         count += 1
                         count += 1
             return count
             return count
 
 

+ 1 - 2
direct/src/showbase/Messenger.py

@@ -464,8 +464,7 @@ class Messenger:
                 #       'oldMethod: ' + repr(oldMethod) + '\n' +
                 #       'oldMethod: ' + repr(oldMethod) + '\n' +
                 #       'newFunction: ' + repr(newFunction) + '\n')
                 #       'newFunction: ' + repr(newFunction) + '\n')
                 if (function == oldMethod):
                 if (function == oldMethod):
-                    newMethod = types.MethodType(
-                        newFunction, method.__self__, method.__self__.__class__)
+                    newMethod = types.MethodType(newFunction, method.__self__)
                     params[0] = newMethod
                     params[0] = newMethod
                     # Found it retrun true
                     # Found it retrun true
                     retFlag += 1
                     retFlag += 1

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

@@ -1561,7 +1561,7 @@ def appendStr(obj, st):
             return s
             return s
         oldStr = Functor(stringer, str(obj))
         oldStr = Functor(stringer, str(obj))
         stringer = None
         stringer = None
-    obj.__str__ = types.MethodType(Functor(appendedStr, oldStr, st), obj, obj.__class__)
+    obj.__str__ = types.MethodType(Functor(appendedStr, oldStr, st), obj)
     appendedStr = None
     appendedStr = None
     return obj
     return obj
 
 

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

@@ -593,9 +593,7 @@ class TaskManager:
         else:
         else:
             function = method
             function = method
         if (function == oldMethod):
         if (function == oldMethod):
-            newMethod = types.MethodType(newFunction,
-                                         method.__self__,
-                                         method.__self__.__class__)
+            newMethod = types.MethodType(newFunction, method.__self__)
             task.setFunction(newMethod)
             task.setFunction(newMethod)
             # Found a match
             # Found a match
             return 1
             return 1

+ 4 - 0
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -8047,6 +8047,10 @@ output_quoted(ostream &out, int indent_level, const std::string &str,
         << '"';
         << '"';
       continue;
       continue;
 
 
+    case '\t':
+      out << "\\t";
+      break;
+
     default:
     default:
       if (!isprint(*si)) {
       if (!isprint(*si)) {
         out << "\\" << oct << std::setw(3) << std::setfill('0') << (unsigned int)(*si)
         out << "\\" << oct << std::setw(3) << std::setfill('0') << (unsigned int)(*si)

+ 6 - 3
dtool/src/prckeys/makePrcKey.cxx

@@ -93,10 +93,13 @@ output_c_string(std::ostream &out, const string &string_name,
         last_nl = false;
         last_nl = false;
       }
       }
 
 
-      if (isprint(data_ptr[i])) {
+      if (data_ptr[i] == '\t') {
+        out << "\\t";
+      }
+      else if (isprint(data_ptr[i])) {
         out << data_ptr[i];
         out << data_ptr[i];
-
-      } else {
+      }
+      else {
         out << "\\x" << std::hex << std::setw(2) << std::setfill('0')
         out << "\\x" << std::hex << std::setw(2) << std::setfill('0')
             << (unsigned int)(unsigned char)data_ptr[i] << std::dec;
             << (unsigned int)(unsigned char)data_ptr[i] << std::dec;
       }
       }

+ 50 - 38
panda/src/audiotraits/fmodAudioSound.cxx

@@ -123,50 +123,62 @@ FmodAudioSound(AudioManager *manager, VirtualFile *file, bool positional) {
           << "Reading " << _file_name << " into memory (" << sound_info.length
           << "Reading " << _file_name << " into memory (" << sound_info.length
           << " bytes)\n";
           << " bytes)\n";
       }
       }
+      result =
+        _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound);
+    }
+    else {
+      result = FMOD_ERR_FILE_BAD;
+
+      if (file->get_system_info(info)) {
+        // The file exists on disk (or it's part of a multifile that exists on
+        // disk), so we can have FMod read the file directly.  This is also
+        // safe, because FMod uses its own IO operations that don't involve
+        // Panda, so this can safely happen in an FMod thread.
+        os_filename = info.get_filename().to_os_specific();
+        name_or_data = os_filename.c_str();
+        sound_info.fileoffset = (unsigned int)info.get_start();
+        sound_info.length = (unsigned int)info.get_size();
+        flags |= FMOD_CREATESTREAM;
+        if (fmodAudio_cat.is_debug()) {
+          fmodAudio_cat.debug()
+            << "Streaming " << _file_name << " from disk (" << name_or_data
+            << ", " << sound_info.fileoffset << ", " << sound_info.length << ")\n";
+        }
 
 
-    } else if (file->get_system_info(info)) {
-      // The file exists on disk (or it's part of a multifile that exists on
-      // disk), so we can have FMod read the file directly.  This is also
-      // safe, because FMod uses its own IO operations that don't involve
-      // Panda, so this can safely happen in an FMod thread.
-      os_filename = info.get_filename().to_os_specific();
-      name_or_data = os_filename.c_str();
-      sound_info.fileoffset = (unsigned int)info.get_start();
-      sound_info.length = (unsigned int)info.get_size();
-      flags |= FMOD_CREATESTREAM;
-      if (fmodAudio_cat.is_debug()) {
-        fmodAudio_cat.debug()
-          << "Streaming " << _file_name << " from disk (" << name_or_data
-          << ", " << sound_info.fileoffset << ", " << sound_info.length << ")\n";
+        result =
+          _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound);
       }
       }
 
 
-    } else {
-#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
-      // Otherwise, if the Panda threading system is compiled in, we can
-      // assign callbacks to read the file through the VFS.
-      name_or_data = (const char *)file;
-      sound_info.length = (unsigned int)info.get_size();
-      sound_info.useropen = open_callback;
-      sound_info.userclose = close_callback;
-      sound_info.userread = read_callback;
-      sound_info.userseek = seek_callback;
-      flags |= FMOD_CREATESTREAM;
-      if (fmodAudio_cat.is_debug()) {
-        fmodAudio_cat.debug()
-          << "Streaming " << _file_name << " from disk using callbacks\n";
-      }
+      // If FMOD can't directly read the file (eg. if Panda is locking it for
+      // write, or it's compressed) we have to use the callback interface.
+      if (result == FMOD_ERR_FILE_BAD) {
+  #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
+        // Otherwise, if the Panda threading system is compiled in, we can
+        // assign callbacks to read the file through the VFS.
+        name_or_data = (const char *)file;
+        sound_info.fileoffset = 0;
+        sound_info.length = (unsigned int)info.get_size();
+        sound_info.useropen = open_callback;
+        sound_info.userclose = close_callback;
+        sound_info.userread = read_callback;
+        sound_info.userseek = seek_callback;
+        flags |= FMOD_CREATESTREAM;
+        if (fmodAudio_cat.is_debug()) {
+          fmodAudio_cat.debug()
+            << "Streaming " << _file_name << " from disk using callbacks\n";
+        }
+        result =
+          _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound);
 
 
-#else  // HAVE_THREADS && !SIMPLE_THREADS
-      // Without threads, we can't safely read this file.
-      name_or_data = "";
+  #else  // HAVE_THREADS && !SIMPLE_THREADS
+        // Without threads, we can't safely read this file.
+        name_or_data = "";
 
 
-      fmodAudio_cat.warning()
-        << "Cannot stream " << _file_name << "; file is not literally on disk.\n";
-#endif
+        fmodAudio_cat.warning()
+          << "Cannot stream " << _file_name << "; file is not literally on disk.\n";
+  #endif
+      }
     }
     }
-
-    result =
-      _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound);
   }
   }
 
 
   if (result != FMOD_OK) {
   if (result != FMOD_OK) {

+ 35 - 7
panda/src/mathutil/triangulator.cxx

@@ -150,11 +150,7 @@ triangulate() {
   }
   }
   */
   */
 
 
-  int attempts = 0;
-
   while (construct_trapezoids(num_segments) != 0) {
   while (construct_trapezoids(num_segments) != 0) {
-    nassertv_always(attempts++ < 100);
-
     // If there's an error, re-shuffle the index and try again.
     // If there's an error, re-shuffle the index and try again.
     Randomizer randomizer;
     Randomizer randomizer;
     for (i = 0; i < num_segments; ++i) {
     for (i = 0; i < num_segments; ++i) {
@@ -263,7 +259,7 @@ cleanup_polygon_indices(vector_int &polygon) {
       ++pi;
       ++pi;
     } else {
     } else {
       // This index is out-of-bounds; remove it.
       // This index is out-of-bounds; remove it.
-      polygon.erase(_polygon.begin() + pi);
+      polygon.erase(polygon.begin() + pi);
     }
     }
   }
   }
 
 
@@ -275,14 +271,46 @@ cleanup_polygon_indices(vector_int &polygon) {
       ++pi;
       ++pi;
     } else {
     } else {
       // This vertex repeats the previous one; remove it.
       // This vertex repeats the previous one; remove it.
-      polygon.erase(_polygon.begin() + pi);
+      polygon.erase(polygon.begin() + pi);
     }
     }
   }
   }
 
 
-  if (polygon.size() > 1 && _vertices[polygon.back()] == _vertices[_polygon.front()]) {
+  if (polygon.size() > 1 && _vertices[polygon.back()] == _vertices[polygon.front()]) {
     // The last vertex repeats the first one; remove it.
     // The last vertex repeats the first one; remove it.
     polygon.pop_back();
     polygon.pop_back();
   }
   }
+
+  // Another pass over the polygons, this time removing any "tails".
+  while (polygon.size() >= 3) {
+    bool removed_any = false;
+
+    int prevprev = polygon[polygon.size() - 2];
+    int prev = polygon[polygon.size() - 1];
+
+    for (size_t i = 0; i < polygon.size(); ++i) {
+      int cur = polygon[i];
+      if (_vertices[prevprev] == _vertices[cur]) {
+        // Cut off the tail.
+        removed_any = true;
+        polygon.erase(polygon.begin() + i);
+        if (i == 0) {
+          polygon.pop_back();
+        } else {
+          polygon.erase(polygon.begin() + i - 1);
+        }
+        break;
+      }
+
+      prevprev = prev;
+      prev = cur;
+    }
+
+    // This might have been the tip of a longer tail, so if we removed
+    // something, go again.
+    if (!removed_any) {
+      break;
+    }
+  }
 }
 }
 
 
 
 

+ 1 - 6
panda/src/pgui/pgEntry.cxx

@@ -203,11 +203,6 @@ press(const MouseWatcherParameter &param, bool background) {
 
 
       ButtonHandle button = param.get_button();
       ButtonHandle button = param.get_button();
 
 
-      if (button == KeyboardButton::tab()) {
-        // Tab. Ignore the entry.
-        return;
-      }
-
       if (button == MouseButton::one() ||
       if (button == MouseButton::one() ||
           button == MouseButton::two() ||
           button == MouseButton::two() ||
           button == MouseButton::three() ||
           button == MouseButton::three() ||
@@ -326,7 +321,7 @@ keystroke(const MouseWatcherParameter &param, bool background) {
 
 
       int keycode = param.get_keycode();
       int keycode = param.get_keycode();
 
 
-      if (!isascii(keycode) || isprint(keycode)) {
+      if ((!isascii(keycode) || isprint(keycode)) && keycode != '\t') {
         // A normal visible character.  Add a new character to the text entry,
         // A normal visible character.  Add a new character to the text entry,
         // if there's room.
         // if there's room.
         if (!_candidate_wtext.empty()) {
         if (!_candidate_wtext.empty()) {

+ 1 - 1
pandatool/src/maya/maya_funcs.cxx

@@ -519,8 +519,8 @@ describe_compound_attribute(MObject &node) {
   for (size_t i = 0; i < comp_attr.numChildren(); i++) {
   for (size_t i = 0; i < comp_attr.numChildren(); i++) {
     MObject child = comp_attr.child(i, &status);
     MObject child = comp_attr.child(i, &status);
     if (child.apiType() == MFn::kAttribute3Float){
     if (child.apiType() == MFn::kAttribute3Float){
-      LRGBColor color;
       /*
       /*
+      LRGBColor color;
       if (get_vec3_attribute(child, "color", color)) {
       if (get_vec3_attribute(child, "color", color)) {
         maya_cat.info() << "color: " << color << endl;
         maya_cat.info() << "color: " << color << endl;
       }
       }

+ 2 - 1
pandatool/src/mayaegg/mayaEggLoader.cxx

@@ -1572,7 +1572,8 @@ void MayaEggLoader::TraverseEggNode(EggNode *node, EggGroup *context, string del
         mayaloader_cat.debug() << delim+delstring << "found an EggTable: " << node->get_name() << endl;
         mayaloader_cat.debug() << delim+delstring << "found an EggTable: " << node->get_name() << endl;
       }
       }
     } else if (node->is_of_type(EggXfmSAnim::get_class_type())) {
     } else if (node->is_of_type(EggXfmSAnim::get_class_type())) {
-      //MayaAnim *anim = GetAnim(DCAST(EggXfmSAnim, node));
+      // Create a MayaAnim equivalent of the EggXfmSAnim
+      GetAnim(DCAST(EggXfmSAnim, node));
       //anim->PrintData();
       //anim->PrintData();
       if (mayaloader_cat.is_debug()) {
       if (mayaloader_cat.is_debug()) {
         mayaloader_cat.debug() << delim+delstring << "found an EggXfmSAnim: " << node->get_name() << endl;
         mayaloader_cat.debug() << delim+delstring << "found an EggXfmSAnim: " << node->get_name() << endl;

+ 73 - 0
tests/mathutil/test_triangulator.py

@@ -0,0 +1,73 @@
+from panda3d.core import Triangulator
+
+
+def triangulate(vertices):
+    t = Triangulator()
+    for i, v in enumerate(vertices):
+        t.add_vertex(v)
+        t.add_polygon_vertex(i)
+
+    t.triangulate()
+
+    # Make sure that the result is consistent by starting each triangle with
+    # the lowest index value.  That makes it easier to use predetermined values
+    # in the test cases.
+    result = set()
+
+    for n in range(t.get_num_triangles()):
+        # Switch to lowest matching index value in case of duplicates.
+        v0 = vertices.index(vertices[t.get_triangle_v0(n)])
+        v1 = vertices.index(vertices[t.get_triangle_v1(n)])
+        v2 = vertices.index(vertices[t.get_triangle_v2(n)])
+        if v1 < v0:
+            v0, v1, v2 = v1, v2, v0
+        if v1 < v0:
+            v0, v1, v2 = v1, v2, v0
+        result.add((v0, v1, v2))
+
+    return result
+
+
+def test_triangulator_degenerate():
+    assert not triangulate([])
+    assert not triangulate([(0, 0)])
+    assert not triangulate([(0, 0), (0, 0)])
+    assert not triangulate([(0, 0), (1, 0)])
+    assert not triangulate([(0, 0), (0, 0), (0, 0)])
+    assert not triangulate([(0, 0), (1, 0), (1, 0)])
+    assert not triangulate([(1, 0), (1, 0), (1, 0)])
+    assert not triangulate([(1, 0), (0, 0), (1, 0)])
+    assert not triangulate([(0, 0), (0, 0), (0, 0), (0, 0)])
+
+
+def test_triangulator_triangle():
+    assert triangulate([(0, 0), (1, 0), (1, 1)]) == {(0, 1, 2)}
+
+
+def test_triangulator_tail():
+    # This triangle has a long "tail" where the polygon retraces its vertices.
+    assert triangulate([
+        (0, -1),
+        (0, 1),
+        (1, 0),
+        (2, 0),
+        (3, 1),
+        (4, 0),
+        (5, 0),
+        (4, 0),
+        (3, 1),
+        (2, 0),
+        (1, 0),
+    ]) == {(0, 2, 1)}
+
+
+def test_triangulator_hourglass():
+    # Two triangles with touching tips, effectively.
+    assert triangulate([
+        (-1, 1),
+        (-1, -1),
+        (0, 0),
+        (1, -1),
+        (1, 1),
+        (0, 0),
+    ]) == {(0, 1, 2), (2, 3, 4)}