浏览代码

Merge branch 'master' into deploy-ng

rdb 7 年之前
父节点
当前提交
01915a36db
共有 100 个文件被更改,包括 2834 次插入1966 次删除
  1. 0 12
      direct/src/actor/Actor.py
  2. 72 0
      direct/src/dcparser/dcArrayParameter.cxx
  3. 4 0
      direct/src/dcparser/dcArrayParameter.h
  4. 5 5
      direct/src/dcparser/dcAtomicField.cxx
  5. 1 1
      direct/src/dcparser/dcAtomicField.h
  6. 9 2
      direct/src/dcparser/dcClass.cxx
  7. 1 1
      direct/src/dcparser/dcClass.h
  8. 3 3
      direct/src/dcparser/dcField.I
  9. 10 9
      direct/src/dcparser/dcField.cxx
  10. 6 6
      direct/src/dcparser/dcField.h
  11. 160 142
      direct/src/dcparser/dcLexer.cxx.prebuilt
  12. 31 31
      direct/src/dcparser/dcLexer.lxx
  13. 107 9
      direct/src/dcparser/dcPacker.I
  14. 9 9
      direct/src/dcparser/dcPacker.cxx
  15. 13 6
      direct/src/dcparser/dcPacker.h
  16. 16 0
      direct/src/dcparser/dcPackerInterface.cxx
  17. 7 2
      direct/src/dcparser/dcPackerInterface.h
  18. 434 547
      direct/src/dcparser/dcParser.cxx.prebuilt
  19. 66 63
      direct/src/dcparser/dcParser.h.prebuilt
  20. 28 27
      direct/src/dcparser/dcParser.yxx
  21. 2 0
      direct/src/dcparser/dcParserDefs.h
  22. 119 0
      direct/src/dcparser/dcSimpleParameter.cxx
  23. 4 0
      direct/src/dcparser/dcSimpleParameter.h
  24. 8 7
      direct/src/dcparser/dcSwitch.cxx
  25. 6 6
      direct/src/dcparser/dcSwitch.h
  26. 12 0
      direct/src/dcparser/hashGenerator.cxx
  27. 2 0
      direct/src/dcparser/hashGenerator.h
  28. 16 2
      direct/src/directscripts/Doxyfile.cxx
  29. 3 1
      direct/src/directscripts/Doxyfile.python
  30. 79 51
      direct/src/directscripts/extract_docs.py
  31. 1 1
      direct/src/distributed/cConnectionRepository.cxx
  32. 0 10
      direct/src/stdpy/thread.py
  33. 155 125
      dtool/metalibs/dtoolconfig/pydtool.cxx
  34. 58 2
      dtool/src/cppparser/cppExpression.cxx
  35. 1 0
      dtool/src/cppparser/cppExpression.h
  36. 161 161
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  37. 4 1
      dtool/src/interrogate/interrogate.cxx
  38. 1 0
      dtool/src/interrogate/interrogateBuilder.cxx
  39. 12 0
      dtool/src/interrogatedb/interrogate_interface.cxx
  40. 3 0
      dtool/src/interrogatedb/interrogate_interface.h
  41. 274 172
      makepanda/installer.nsi
  42. 25 24
      makepanda/installpanda.py
  43. 165 75
      makepanda/makepackage.py
  44. 25 10
      makepanda/makepanda.py
  45. 3 3
      makepanda/makepanda.vcproj
  46. 65 0
      makepanda/makepandacore.py
  47. 6 6
      panda/src/bullet/bulletBodyNode.cxx
  48. 5 5
      panda/src/bullet/bulletCapsuleShape.cxx
  49. 2 2
      panda/src/bullet/bulletCapsuleShape.h
  50. 8 0
      panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h
  51. 68 4
      panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm
  52. 3 0
      panda/src/cocoadisplay/cocoaGraphicsWindow.h
  53. 14 0
      panda/src/cocoadisplay/cocoaGraphicsWindow.mm
  54. 10 10
      panda/src/collide/collisionBox.cxx
  55. 1 1
      panda/src/collide/collisionBox.h
  56. 19 19
      panda/src/collide/collisionCapsule.I
  57. 74 74
      panda/src/collide/collisionCapsule.cxx
  58. 161 0
      panda/src/collide/collisionCapsule.h
  59. 1 1
      panda/src/collide/collisionHandlerFluidPusher.cxx
  60. 11 11
      panda/src/collide/collisionPlane.cxx
  61. 1 1
      panda/src/collide/collisionPlane.h
  62. 4 4
      panda/src/collide/collisionSolid.cxx
  63. 2 2
      panda/src/collide/collisionSolid.h
  64. 10 10
      panda/src/collide/collisionSphere.cxx
  65. 1 1
      panda/src/collide/collisionSphere.h
  66. 2 2
      panda/src/collide/collisionTraverser.cxx
  67. 8 141
      panda/src/collide/collisionTube.h
  68. 7 3
      panda/src/collide/config_collide.cxx
  69. 1 2
      panda/src/collide/p3collide_composite1.cxx
  70. 2 1
      panda/src/collide/p3collide_composite2.cxx
  71. 4 4
      panda/src/display/graphicsEngine.cxx
  72. 2 2
      panda/src/display/graphicsEngine.h
  73. 1 1
      panda/src/doc/collisionFlags.txt
  74. 20 27
      panda/src/dxml/config_dxml.cxx
  75. 2 1
      panda/src/egg/eggGroup.cxx
  76. 10 10
      panda/src/egg2pg/eggLoader.cxx
  77. 2 2
      panda/src/egg2pg/eggLoader.h
  78. 15 15
      panda/src/egg2pg/eggSaver.cxx
  79. 2 2
      panda/src/ffmpeg/ffmpegVideoCursor.cxx
  80. 3 2
      panda/src/glstuff/glBufferContext_src.I
  81. 2 1
      panda/src/glstuff/glBufferContext_src.h
  82. 1 1
      panda/src/glstuff/glGraphicsBuffer_src.cxx
  83. 16 2
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  84. 12 2
      panda/src/glstuff/glShaderContext_src.cxx
  85. 8 0
      panda/src/gobj/bufferContext.I
  86. 2 1
      panda/src/gobj/bufferContext.cxx
  87. 11 1
      panda/src/gobj/bufferContext.h
  88. 7 8
      panda/src/gobj/indexBufferContext.I
  89. 0 3
      panda/src/gobj/indexBufferContext.h
  90. 25 18
      panda/src/gobj/preparedGraphicsObjects.cxx
  91. 25 0
      panda/src/gobj/shaderBuffer.cxx
  92. 5 0
      panda/src/gobj/shaderBuffer.h
  93. 9 10
      panda/src/gobj/textureContext.I
  94. 0 3
      panda/src/gobj/textureContext.h
  95. 19 0
      panda/src/gobj/texturePeeker.cxx
  96. 1 0
      panda/src/gobj/texturePeeker.h
  97. 7 8
      panda/src/gobj/vertexBufferContext.I
  98. 0 4
      panda/src/gobj/vertexBufferContext.h
  99. 4 7
      panda/src/pgraph/camera.cxx
  100. 12 5
      panda/src/pgraph/pandaNode.cxx

+ 0 - 12
direct/src/actor/Actor.py

@@ -653,15 +653,11 @@ class Actor(DirectObject, NodePath):
         """
         """
         # make sure we don't call this twice in a row
         # make sure we don't call this twice in a row
         # and pollute the the switches dictionary
         # and pollute the the switches dictionary
-##         sortedKeys = list(self.switches.keys())
-##         sortedKeys.sort()
         child = self.__LODNode.find(str(lodName))
         child = self.__LODNode.find(str(lodName))
         index = self.__LODNode.node().findChild(child.node())
         index = self.__LODNode.node().findChild(child.node())
         self.__LODNode.node().forceSwitch(index)
         self.__LODNode.node().forceSwitch(index)
 
 
     def printLOD(self):
     def printLOD(self):
-##         sortedKeys = list(self.switches.keys())
-##         sortedKeys.sort()
         sortedKeys = self.__sortedLODNames
         sortedKeys = self.__sortedLODNames
         for eachLod in sortedKeys:
         for eachLod in sortedKeys:
             print("python switches for %s: in: %d, out %d" % (eachLod,
             print("python switches for %s: in: %d, out %d" % (eachLod,
@@ -679,12 +675,6 @@ class Actor(DirectObject, NodePath):
         """
         """
         Restore all switch distance info (usually after a useLOD call)"""
         Restore all switch distance info (usually after a useLOD call)"""
         self.__LODNode.node().clearForceSwitch()
         self.__LODNode.node().clearForceSwitch()
-##         sortedKeys = list(self.switches.keys())
-##         sortedKeys.sort()
-##         for eachLod in sortedKeys:
-##             index = sortedKeys.index(eachLod)
-##             self.__LODNode.node().setSwitch(index, self.switches[eachLod][0],
-##                                      self.switches[eachLod][1])
 
 
     def addLOD(self, lodName, inDist=0, outDist=0, center=None):
     def addLOD(self, lodName, inDist=0, outDist=0, center=None):
         """addLOD(self, string)
         """addLOD(self, string)
@@ -706,8 +696,6 @@ class Actor(DirectObject, NodePath):
         # save the switch distance info
         # save the switch distance info
         self.switches[lodName] = [inDist, outDist]
         self.switches[lodName] = [inDist, outDist]
         # add the switch distance info
         # add the switch distance info
-##         sortedKeys = list(self.switches.keys())
-##         sortedKeys.sort()
         self.__LODNode.node().setSwitch(self.getLODIndex(lodName), inDist, outDist)
         self.__LODNode.node().setSwitch(self.getLODIndex(lodName), inDist, outDist)
 
 
     def getLODIndex(self, lodName):
     def getLODIndex(self, lodName):

+ 72 - 0
direct/src/dcparser/dcArrayParameter.cxx

@@ -262,6 +262,38 @@ pack_string(DCPackData &pack_data, const string &value,
   }
   }
 }
 }
 
 
+/**
+ * Packs the indicated numeric or string value into the stream.
+ */
+void DCArrayParameter::
+pack_blob(DCPackData &pack_data, const vector_uchar &value,
+          bool &pack_error, bool &range_error) const {
+  // We can only pack a string if the array element type is char or int8.
+  DCSimpleParameter *simple_type = _element_type->as_simple_parameter();
+  if (simple_type == nullptr) {
+    pack_error = true;
+    return;
+  }
+
+  size_t blob_size = value.size();
+
+  switch (simple_type->get_type()) {
+  case ST_char:
+  case ST_uint8:
+  case ST_int8:
+    _array_size_range.validate(blob_size, range_error);
+    if (_num_length_bytes != 0) {
+      nassertv(_num_length_bytes == 2);
+      do_pack_uint16(pack_data.get_write_pointer(2), blob_size);
+    }
+    pack_data.append_data((const char *)value.data(), blob_size);
+    break;
+
+  default:
+    pack_error = true;
+  }
+}
+
 /**
 /**
  * Packs the arrayParameter's specified default value (or a sensible default
  * Packs the arrayParameter's specified default value (or a sensible default
  * if no value is specified) into the stream.  Returns true if the default
  * if no value is specified) into the stream.  Returns true if the default
@@ -339,6 +371,46 @@ unpack_string(const char *data, size_t length, size_t &p, string &value,
   }
   }
 }
 }
 
 
+/**
+ * Unpacks the current numeric or string value from the stream.
+ */
+void DCArrayParameter::
+unpack_blob(const char *data, size_t length, size_t &p, vector_uchar &value,
+            bool &pack_error, bool &range_error) const {
+  // We can only unpack a string if the array element type is char or int8.
+  DCSimpleParameter *simple_type = _element_type->as_simple_parameter();
+  if (simple_type == nullptr) {
+    pack_error = true;
+    return;
+  }
+
+  size_t blob_size;
+
+  switch (simple_type->get_type()) {
+  case ST_char:
+  case ST_uint8:
+  case ST_int8:
+    if (_num_length_bytes != 0) {
+      blob_size = do_unpack_uint16(data + p);
+      p += 2;
+    } else {
+      nassertv(_array_size >= 0);
+      blob_size = _array_size;
+    }
+    if (p + blob_size > length) {
+      pack_error = true;
+      return;
+    }
+    value = vector_uchar((const unsigned char *)data + p,
+                         (const unsigned char *)data + p + blob_size);
+    p += blob_size;
+    break;
+
+  default:
+    pack_error = true;
+  }
+}
+
 /**
 /**
  * Returns true if the other interface is bitwise the same as this one--that
  * Returns true if the other interface is bitwise the same as this one--that
  * is, a uint32 only matches a uint32, etc.  Names of components, and range
  * is, a uint32 only matches a uint32, etc.  Names of components, and range

+ 4 - 0
direct/src/dcparser/dcArrayParameter.h

@@ -51,9 +51,13 @@ public:
   virtual void generate_hash(HashGenerator &hashgen) const;
   virtual void generate_hash(HashGenerator &hashgen) const;
   virtual void pack_string(DCPackData &pack_data, const std::string &value,
   virtual void pack_string(DCPackData &pack_data, const std::string &value,
                            bool &pack_error, bool &range_error) const;
                            bool &pack_error, bool &range_error) const;
+  virtual void pack_blob(DCPackData &pack_data, const vector_uchar &value,
+                         bool &pack_error, bool &range_error) const;
   virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const;
   virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const;
   virtual void unpack_string(const char *data, size_t length, size_t &p,
   virtual void unpack_string(const char *data, size_t length, size_t &p,
                              std::string &value, bool &pack_error, bool &range_error) const;
                              std::string &value, bool &pack_error, bool &range_error) const;
+  virtual void unpack_blob(const char *data, size_t length, size_t &p,
+                           vector_uchar &value, bool &pack_error, bool &range_error) const;
 
 
 protected:
 protected:
   virtual bool do_check_match(const DCPackerInterface *other) const;
   virtual bool do_check_match(const DCPackerInterface *other) const;

+ 5 - 5
direct/src/dcparser/dcAtomicField.cxx

@@ -88,11 +88,11 @@ get_element(int n) const {
  * If the element is an array-type element, the returned value will include
  * If the element is an array-type element, the returned value will include
  * the two-byte length preceding the array data.
  * the two-byte length preceding the array data.
  *
  *
- * This is deprecated; use get_element() instead.
+ * @deprecated use get_element() instead.
  */
  */
-string DCAtomicField::
+vector_uchar DCAtomicField::
 get_element_default(int n) const {
 get_element_default(int n) const {
-  nassertr(n >= 0 && n < (int)_elements.size(), string());
+  nassertr(n >= 0 && n < (int)_elements.size(), vector_uchar());
   return _elements[n]->get_default_value();
   return _elements[n]->get_default_value();
 }
 }
 
 
@@ -100,7 +100,7 @@ get_element_default(int n) const {
  * Returns true if the nth element of the field has a default value specified,
  * Returns true if the nth element of the field has a default value specified,
  * false otherwise.
  * false otherwise.
  *
  *
- * This is deprecated; use get_element() instead.
+ * @deprecated use get_element() instead.
  */
  */
 bool DCAtomicField::
 bool DCAtomicField::
 has_element_default(int n) const {
 has_element_default(int n) const {
@@ -113,7 +113,7 @@ has_element_default(int n) const {
  * for documentary purposes; it does not generally affect operation.  If a
  * for documentary purposes; it does not generally affect operation.  If a
  * name is not specified, this will be the empty string.
  * name is not specified, this will be the empty string.
  *
  *
- * This method is deprecated; use get_element()->get_name() instead.
+ * @deprecated use get_element()->get_name() instead.
  */
  */
 string DCAtomicField::
 string DCAtomicField::
 get_element_name(int n) const {
 get_element_name(int n) const {

+ 1 - 1
direct/src/dcparser/dcAtomicField.h

@@ -40,7 +40,7 @@ PUBLISHED:
   DCParameter *get_element(int n) const;
   DCParameter *get_element(int n) const;
 
 
   // These five methods are deprecated and will be removed soon.
   // These five methods are deprecated and will be removed soon.
-  std::string get_element_default(int n) const;
+  vector_uchar get_element_default(int n) const;
   bool has_element_default(int n) const;
   bool has_element_default(int n) const;
   std::string get_element_name(int n) const;
   std::string get_element_name(int n) const;
   DCSubatomicType get_element_type(int n) const;
   DCSubatomicType get_element_type(int n) const;

+ 9 - 2
direct/src/dcparser/dcClass.cxx

@@ -587,7 +587,7 @@ receive_update_other(PyObject *distobj, DatagramIterator &di) const {
  */
  */
 void DCClass::
 void DCClass::
 direct_update(PyObject *distobj, const string &field_name,
 direct_update(PyObject *distobj, const string &field_name,
-              const string &value_blob) {
+              const vector_uchar &value_blob) {
   DCField *field = get_field_by_name(field_name);
   DCField *field = get_field_by_name(field_name);
   nassertv_always(field != nullptr);
   nassertv_always(field != nullptr);
 
 
@@ -606,7 +606,14 @@ direct_update(PyObject *distobj, const string &field_name,
 void DCClass::
 void DCClass::
 direct_update(PyObject *distobj, const string &field_name,
 direct_update(PyObject *distobj, const string &field_name,
               const Datagram &datagram) {
               const Datagram &datagram) {
-  direct_update(distobj, field_name, datagram.get_message());
+  DCField *field = get_field_by_name(field_name);
+  nassertv_always(field != nullptr);
+
+  DCPacker packer;
+  packer.set_unpack_data((const char *)datagram.get_data(), datagram.get_length(), false);
+  packer.begin_unpack(field);
+  field->receive_update(packer, distobj);
+  packer.end_unpack();
 }
 }
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON
 
 

+ 1 - 1
direct/src/dcparser/dcClass.h

@@ -95,7 +95,7 @@ PUBLISHED:
   void receive_update_other(PyObject *distobj, DatagramIterator &di) const;
   void receive_update_other(PyObject *distobj, DatagramIterator &di) const;
 
 
   void direct_update(PyObject *distobj, const std::string &field_name,
   void direct_update(PyObject *distobj, const std::string &field_name,
-                     const std::string &value_blob);
+                     const vector_uchar &value_blob);
   void direct_update(PyObject *distobj, const std::string &field_name,
   void direct_update(PyObject *distobj, const std::string &field_name,
                      const Datagram &datagram);
                      const Datagram &datagram);
   bool pack_required_field(Datagram &datagram, PyObject *distobj,
   bool pack_required_field(Datagram &datagram, PyObject *distobj,

+ 3 - 3
direct/src/dcparser/dcField.I

@@ -42,7 +42,7 @@ has_default_value() const {
  * explicitly set (e.g.  has_default_value() returns true), returns that
  * explicitly set (e.g.  has_default_value() returns true), returns that
  * value; otherwise, returns an implicit default for the field.
  * value; otherwise, returns an implicit default for the field.
  */
  */
-INLINE const std::string &DCField::
+INLINE const vector_uchar &DCField::
 get_default_value() const {
 get_default_value() const {
   if (_default_value_stale) {
   if (_default_value_stale) {
     ((DCField *)this)->refresh_default_value();
     ((DCField *)this)->refresh_default_value();
@@ -172,8 +172,8 @@ set_class(DCClass *dclass) {
  * Establishes a default value for this field.
  * Establishes a default value for this field.
  */
  */
 INLINE void DCField::
 INLINE void DCField::
-set_default_value(const std::string &default_value) {
-  _default_value = default_value;
+set_default_value(vector_uchar default_value) {
+  _default_value = std::move(default_value);
   _has_default_value = true;
   _has_default_value = true;
   _default_value_stale = false;
   _default_value_stale = false;
 }
 }

+ 10 - 9
direct/src/dcparser/dcField.cxx

@@ -162,7 +162,7 @@ as_parameter() const {
  * is an error.
  * is an error.
  */
  */
 string DCField::
 string DCField::
-format_data(const string &packed_data, bool show_field_names) {
+format_data(const vector_uchar &packed_data, bool show_field_names) {
   DCPacker packer;
   DCPacker packer;
   packer.set_unpack_data(packed_data);
   packer.set_unpack_data(packed_data);
   packer.begin_unpack(this);
   packer.begin_unpack(this);
@@ -178,20 +178,20 @@ format_data(const string &packed_data, bool show_field_names) {
  * above) that represents the value of this field, parse the string and return
  * above) that represents the value of this field, parse the string and return
  * the corresponding packed data.  Returns empty string if there is an error.
  * the corresponding packed data.  Returns empty string if there is an error.
  */
  */
-string DCField::
+vector_uchar DCField::
 parse_string(const string &formatted_string) {
 parse_string(const string &formatted_string) {
   DCPacker packer;
   DCPacker packer;
   packer.begin_pack(this);
   packer.begin_pack(this);
   if (!packer.parse_and_pack(formatted_string)) {
   if (!packer.parse_and_pack(formatted_string)) {
     // Parse error.
     // Parse error.
-    return string();
+    return vector_uchar();
   }
   }
   if (!packer.end_pack()) {
   if (!packer.end_pack()) {
     // Data type mismatch.
     // Data type mismatch.
-    return string();
+    return vector_uchar();
   }
   }
 
 
-  return packer.get_string();
+  return packer.get_bytes();
 }
 }
 
 
 /**
 /**
@@ -200,7 +200,7 @@ parse_string(const string &formatted_string) {
  * record.  Returns true if all fields are valid, false otherwise.
  * record.  Returns true if all fields are valid, false otherwise.
  */
  */
 bool DCField::
 bool DCField::
-validate_ranges(const string &packed_data) const {
+validate_ranges(const vector_uchar &packed_data) const {
   DCPacker packer;
   DCPacker packer;
   packer.set_unpack_data(packed_data);
   packer.set_unpack_data(packed_data);
   packer.begin_unpack(this);
   packer.begin_unpack(this);
@@ -209,7 +209,7 @@ validate_ranges(const string &packed_data) const {
     return false;
     return false;
   }
   }
 
 
-  return (packer.get_num_unpacked_bytes() == packed_data.length());
+  return (packer.get_num_unpacked_bytes() == packed_data.size());
 }
 }
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
@@ -488,7 +488,7 @@ pack_default_value(DCPackData &pack_data, bool &) const {
   // The default behavior is to pack the default value if we got it;
   // The default behavior is to pack the default value if we got it;
   // otherwise, to return false and let the packer visit our nested elements.
   // otherwise, to return false and let the packer visit our nested elements.
   if (!_default_value_stale) {
   if (!_default_value_stale) {
-    pack_data.append_data(_default_value.data(), _default_value.length());
+    pack_data.append_data((const char *)_default_value.data(), _default_value.size());
     return true;
     return true;
   }
   }
 
 
@@ -566,7 +566,8 @@ refresh_default_value() {
   if (!packer.end_pack()) {
   if (!packer.end_pack()) {
     std::cerr << "Error while packing default value for " << get_name() << "\n";
     std::cerr << "Error while packing default value for " << get_name() << "\n";
   } else {
   } else {
-    _default_value.assign(packer.get_data(), packer.get_length());
+    const unsigned char *data = (const unsigned char *)packer.get_data();
+    _default_value = vector_uchar(data, data + packer.get_length());
   }
   }
   _default_value_stale = false;
   _default_value_stale = false;
 }
 }

+ 6 - 6
direct/src/dcparser/dcField.h

@@ -53,13 +53,13 @@ PUBLISHED:
   virtual DCParameter *as_parameter();
   virtual DCParameter *as_parameter();
   virtual const DCParameter *as_parameter() const;
   virtual const DCParameter *as_parameter() const;
 
 
-  std::string format_data(const std::string &packed_data, bool show_field_names = true);
-  std::string parse_string(const std::string &formatted_string);
+  std::string format_data(const vector_uchar &packed_data, bool show_field_names = true);
+  vector_uchar parse_string(const std::string &formatted_string);
 
 
-  bool validate_ranges(const std::string &packed_data) const;
+  bool validate_ranges(const vector_uchar &packed_data) const;
 
 
   INLINE bool has_default_value() const;
   INLINE bool has_default_value() const;
-  INLINE const std::string &get_default_value() const;
+  INLINE const vector_uchar &get_default_value() const;
 
 
   INLINE bool is_bogus_field() const;
   INLINE bool is_bogus_field() const;
 
 
@@ -98,7 +98,7 @@ public:
 
 
   INLINE void set_number(int number);
   INLINE void set_number(int number);
   INLINE void set_class(DCClass *dclass);
   INLINE void set_class(DCClass *dclass);
-  INLINE void set_default_value(const std::string &default_value);
+  INLINE void set_default_value(vector_uchar default_value);
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
   static std::string get_pystr(PyObject *value);
   static std::string get_pystr(PyObject *value);
@@ -115,7 +115,7 @@ protected:
   bool _bogus_field;
   bool _bogus_field;
 
 
 private:
 private:
-  std::string _default_value;
+  vector_uchar _default_value;
 
 
 #ifdef WITHIN_PANDA
 #ifdef WITHIN_PANDA
   PStatCollector _field_update_pcollector;
   PStatCollector _field_update_pcollector;

文件差异内容过多而无法显示
+ 160 - 142
direct/src/dcparser/dcLexer.cxx.prebuilt


+ 31 - 31
direct/src/dcparser/dcLexer.lxx

@@ -1,7 +1,7 @@
 /*
 /*
 // Filename: dcLexer.lxx
 // Filename: dcLexer.lxx
 // Created by:  drose (05Oct00)
 // Created by:  drose (05Oct00)
-// 
+//
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 */
 */
 
 
@@ -103,12 +103,12 @@ dcyyerror(const std::string &msg) {
   if (!dc_filename.empty()) {
   if (!dc_filename.empty()) {
     cerr << " in " << dc_filename;
     cerr << " in " << dc_filename;
   }
   }
-  cerr 
+  cerr
     << " at line " << line_number << ", column " << col_number << ":\n"
     << " at line " << line_number << ", column " << col_number << ":\n"
     << current_line << "\n";
     << current_line << "\n";
-  indent(cerr, col_number-1) 
+  indent(cerr, col_number-1)
     << "^\n" << msg << "\n\n";
     << "^\n" << msg << "\n\n";
-  
+
   error_count++;
   error_count++;
 }
 }
 
 
@@ -120,10 +120,10 @@ dcyywarning(const std::string &msg) {
   if (!dc_filename.empty()) {
   if (!dc_filename.empty()) {
     cerr << " in " << dc_filename;
     cerr << " in " << dc_filename;
   }
   }
-  cerr 
+  cerr
     << " at line " << line_number << ", column " << col_number << ":\n"
     << " at line " << line_number << ", column " << col_number << ":\n"
     << current_line << "\n";
     << current_line << "\n";
-  indent(cerr, col_number-1) 
+  indent(cerr, col_number-1)
     << "^\n" << msg << "\n\n";
     << "^\n" << msg << "\n\n";
 
 
   warning_count++;
   warning_count++;
@@ -313,9 +313,9 @@ scan_quoted_string(char quote_mark) {
 
 
 // scan_hex_string reads a string of hexadecimal digits delimited by
 // scan_hex_string reads a string of hexadecimal digits delimited by
 // angle brackets and returns the representative string.
 // angle brackets and returns the representative string.
-static std::string
+static vector_uchar
 scan_hex_string() {
 scan_hex_string() {
-  std::string result;
+  vector_uchar result;
 
 
   // We don't touch the current line number and column number during
   // We don't touch the current line number and column number during
   // scanning, so that if we detect an error while scanning the string
   // scanning, so that if we detect an error while scanning the string
@@ -345,24 +345,24 @@ scan_hex_string() {
       line_number = line;
       line_number = line;
       col_number = col;
       col_number = col;
       dcyyerror("Invalid hex digit.");
       dcyyerror("Invalid hex digit.");
-      return std::string();
+      return vector_uchar();
     }
     }
 
 
     odd = !odd;
     odd = !odd;
     if (odd) {
     if (odd) {
       last = value;
       last = value;
     } else {
     } else {
-      result += (char)((last << 4) | value);
+      result.push_back((unsigned char)((last << 4) | value));
     }
     }
     c = read_char(line, col);
     c = read_char(line, col);
   }
   }
 
 
   if (c == EOF) {
   if (c == EOF) {
     dcyyerror("This hex string is unterminated.");
     dcyyerror("This hex string is unterminated.");
-    return std::string();
+    return vector_uchar();
   } else if (odd) {
   } else if (odd) {
     dcyyerror("Odd number of hex digits.");
     dcyyerror("Odd number of hex digits.");
-    return std::string();
+    return vector_uchar();
   }
   }
 
 
   line_number = line;
   line_number = line;
@@ -382,7 +382,7 @@ eat_c_comment() {
   int col = col_number;
   int col = col_number;
 
 
   int c, last_c;
   int c, last_c;
-  
+
   last_c = '\0';
   last_c = '\0';
   c = read_char(line, col);
   c = read_char(line, col);
   while (c != EOF && !(last_c == '*' && c == '/')) {
   while (c != EOF && !(last_c == '*' && c == '/')) {
@@ -440,12 +440,12 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
   yyless(1);
   yyless(1);
 }
 }
 
 
-[ \t\r] { 
+[ \t\r] {
   // Eat whitespace.
   // Eat whitespace.
   accept();
   accept();
 }
 }
 
 
-"//".* { 
+"//".* {
   // Eat C++-style comments.
   // Eat C++-style comments.
   accept();
   accept();
 }
 }
@@ -453,7 +453,7 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
 "/*" {
 "/*" {
   // Eat C-style comments.
   // Eat C-style comments.
   accept();
   accept();
-  eat_c_comment(); 
+  eat_c_comment();
 }
 }
 
 
 
 
@@ -607,7 +607,7 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
   return KW_CHAR;
   return KW_CHAR;
 }
 }
 
 
-{UNSIGNED_INTEGERNUM} { 
+{UNSIGNED_INTEGERNUM} {
   // An unsigned integer number.
   // An unsigned integer number.
   accept();
   accept();
 
 
@@ -626,11 +626,11 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
     dcyylval.u.uint64 = next_value + (*p - '0');
     dcyylval.u.uint64 = next_value + (*p - '0');
     ++p;
     ++p;
   }
   }
-  
+
   return UNSIGNED_INTEGER;
   return UNSIGNED_INTEGER;
 }
 }
 
 
-{SIGNED_INTEGERNUM} { 
+{SIGNED_INTEGERNUM} {
   // A signed integer number.
   // A signed integer number.
   accept();
   accept();
 
 
@@ -671,14 +671,14 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
       dcyyerror("Number out of range.");
       dcyyerror("Number out of range.");
       dcyylval.u.int64 = 1;
       dcyylval.u.int64 = 1;
     }
     }
-  }    
-  
+  }
+
   return SIGNED_INTEGER;
   return SIGNED_INTEGER;
 }
 }
 
 
 {UNSIGNED_HEXNUM} {
 {UNSIGNED_HEXNUM} {
   // A hexadecimal integer number.
   // A hexadecimal integer number.
-  accept(); 
+  accept();
 
 
   // As above, we'll decode the hex string by hand.
   // As above, we'll decode the hex string by hand.
   dcyylval.str = dcyytext;
   dcyylval.str = dcyytext;
@@ -700,15 +700,15 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
     ++p;
     ++p;
   }
   }
 
 
-  return UNSIGNED_INTEGER; 
+  return UNSIGNED_INTEGER;
 }
 }
 
 
-{REALNUM} { 
+{REALNUM} {
   // A floating-point number.
   // A floating-point number.
-  accept(); 
-  dcyylval.u.real = patof(dcyytext); 
+  accept();
+  dcyylval.u.real = patof(dcyytext);
   dcyylval.str = dcyytext;
   dcyylval.str = dcyytext;
-  return REAL; 
+  return REAL;
 }
 }
 
 
 ["] {
 ["] {
@@ -728,11 +728,11 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
 [<] {
 [<] {
   // Long hex string.
   // Long hex string.
   accept();
   accept();
-  dcyylval.str = scan_hex_string();
+  dcyylval.bytes = scan_hex_string();
   return HEX_STRING;
   return HEX_STRING;
 }
 }
 
 
-[A-Za-z_][A-Za-z_0-9]* { 
+[A-Za-z_][A-Za-z_0-9]* {
   // Identifier or keyword.
   // Identifier or keyword.
   accept();
   accept();
   dcyylval.str = dcyytext;
   dcyylval.str = dcyytext;
@@ -750,7 +750,7 @@ REALNUM              ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
 
 
 . {
 . {
   // Send any other printable character as itself.
   // Send any other printable character as itself.
-  accept(); 
+  accept();
   return dcyytext[0];
   return dcyytext[0];
 }
 }
-  
+

+ 107 - 9
direct/src/dcparser/dcPacker.I

@@ -216,17 +216,31 @@ pack_string(const std::string &value) {
   }
   }
 }
 }
 
 
+/**
+ * Packs the indicated numeric or string value into the stream.
+ */
+INLINE void DCPacker::
+pack_blob(const vector_uchar &value) {
+  nassertv(_mode == M_pack || _mode == M_repack);
+  if (_current_field == nullptr) {
+    _pack_error = true;
+  } else {
+    _current_field->pack_blob(_pack_data, value, _pack_error, _range_error);
+    advance();
+  }
+}
+
 /**
 /**
  * Adds the indicated string value into the stream, representing a single pre-
  * Adds the indicated string value into the stream, representing a single pre-
  * packed field element, or a whole group of field elements at once.
  * packed field element, or a whole group of field elements at once.
  */
  */
 INLINE void DCPacker::
 INLINE void DCPacker::
-pack_literal_value(const std::string &value) {
+pack_literal_value(const vector_uchar &value) {
   nassertv(_mode == M_pack || _mode == M_repack);
   nassertv(_mode == M_pack || _mode == M_repack);
   if (_current_field == nullptr) {
   if (_current_field == nullptr) {
     _pack_error = true;
     _pack_error = true;
   } else {
   } else {
-    _pack_data.append_data(value.data(), value.length());
+    _pack_data.append_data((const char *)value.data(), value.size());
     advance();
     advance();
   }
   }
 }
 }
@@ -345,16 +359,36 @@ unpack_string() {
   return value;
   return value;
 }
 }
 
 
+/**
+ * Unpacks the current binary data value from the stream.
+ */
+INLINE vector_uchar DCPacker::
+unpack_blob() {
+  vector_uchar value;
+  nassertr(_mode == M_unpack, value);
+  if (_current_field == nullptr) {
+    _pack_error = true;
+
+  } else {
+    _current_field->unpack_blob(_unpack_data, _unpack_length, _unpack_p,
+                                 value, _pack_error, _range_error);
+    advance();
+  }
+
+  return value;
+}
+
 /**
 /**
  * Returns the literal string that represents the packed value of the current
  * Returns the literal string that represents the packed value of the current
  * field, and advances the field pointer.
  * field, and advances the field pointer.
  */
  */
-INLINE std::string DCPacker::
+INLINE vector_uchar DCPacker::
 unpack_literal_value() {
 unpack_literal_value() {
   size_t start = _unpack_p;
   size_t start = _unpack_p;
   unpack_skip();
   unpack_skip();
-  nassertr(_unpack_p >= start, std::string());
-  return std::string(_unpack_data + start, _unpack_p - start);
+  nassertr(_unpack_p >= start, vector_uchar());
+  return vector_uchar((const unsigned char *)_unpack_data + start,
+                      (const unsigned char *)_unpack_data + _unpack_p);
 }
 }
 
 
 /**
 /**
@@ -453,16 +487,33 @@ unpack_string(std::string &value) {
   }
   }
 }
 }
 
 
+/**
+ * Unpacks the current numeric or string value from the stream.
+ */
+INLINE void DCPacker::
+unpack_blob(vector_uchar &value) {
+  nassertv(_mode == M_unpack);
+  if (_current_field == nullptr) {
+    _pack_error = true;
+
+  } else {
+    _current_field->unpack_blob(_unpack_data, _unpack_length, _unpack_p,
+                                value, _pack_error, _range_error);
+    advance();
+  }
+}
+
 /**
 /**
  * Returns the literal string that represents the packed value of the current
  * Returns the literal string that represents the packed value of the current
  * field, and advances the field pointer.
  * field, and advances the field pointer.
  */
  */
 INLINE void DCPacker::
 INLINE void DCPacker::
-unpack_literal_value(std::string &value) {
+unpack_literal_value(vector_uchar &value) {
   size_t start = _unpack_p;
   size_t start = _unpack_p;
   unpack_skip();
   unpack_skip();
   nassertv(_unpack_p >= start);
   nassertv(_unpack_p >= start);
-  value.assign(_unpack_data + start, _unpack_p - start);
+  value = vector_uchar((const unsigned char *)_unpack_data + start,
+                       (const unsigned char *)_unpack_data + _unpack_p);
 }
 }
 
 
 /**
 /**
@@ -541,6 +592,15 @@ get_string() const {
   return _pack_data.get_string();
   return _pack_data.get_string();
 }
 }
 
 
+/**
+ * Returns the packed data buffer as a bytes object.  Also see get_data().
+ */
+INLINE vector_uchar DCPacker::
+get_bytes() const {
+  const unsigned char *p = (const unsigned char *)_pack_data.get_data();
+  return vector_uchar(p, p + _pack_data.get_length());
+}
+
 /**
 /**
  * Returns the total number of bytes in the unpack data buffer.  This is the
  * Returns the total number of bytes in the unpack data buffer.  This is the
  * buffer used when unpacking; it is separate from the pack data returned by
  * buffer used when unpacking; it is separate from the pack data returned by
@@ -601,9 +661,9 @@ take_data() {
  * between packing sessions.
  * between packing sessions.
  */
  */
 INLINE void DCPacker::
 INLINE void DCPacker::
-append_data(const char *buffer, size_t size) {
+append_data(const unsigned char *buffer, size_t size) {
   nassertv(_mode == M_idle);
   nassertv(_mode == M_idle);
-  _pack_data.append_data(buffer, size);
+  _pack_data.append_data((const char *)buffer, size);
 }
 }
 
 
 /**
 /**
@@ -728,6 +788,16 @@ raw_pack_string(const std::string &value) {
   _pack_data.append_data(value.data(), value.length());
   _pack_data.append_data(value.data(), value.length());
 }
 }
 
 
+/**
+ * Packs the data into the buffer between packing sessions.
+ */
+INLINE void DCPacker::
+raw_pack_blob(const vector_uchar &value) {
+  nassertv(_mode == M_idle);
+  DCPackerInterface::do_pack_uint16(_pack_data.get_write_pointer(2), value.size());
+  _pack_data.append_data((const char *)value.data(), value.size());
+}
+
 /**
 /**
  * Unpacks the data from the buffer between unpacking sessions.
  * Unpacks the data from the buffer between unpacking sessions.
  */
  */
@@ -870,6 +940,16 @@ raw_unpack_string() {
   return value;
   return value;
 }
 }
 
 
+/**
+ * Unpacks the data from the buffer between unpacking sessions.
+ */
+INLINE vector_uchar DCPacker::
+raw_unpack_blob() {
+  vector_uchar value;
+  raw_unpack_blob(value);
+  return value;
+}
+
 /**
 /**
  * Unpacks the data from the buffer between unpacking sessions.
  * Unpacks the data from the buffer between unpacking sessions.
  */
  */
@@ -971,6 +1051,24 @@ raw_unpack_string(std::string &value) {
   _unpack_p += string_length;
   _unpack_p += string_length;
 }
 }
 
 
+/**
+ * Unpacks the data from the buffer between unpacking sessions.
+ */
+INLINE void DCPacker::
+raw_unpack_blob(vector_uchar &value) {
+  nassertv(_mode == M_idle && _unpack_data != nullptr);
+  unsigned int blob_size = raw_unpack_uint16();
+
+  if (_unpack_p + blob_size > _unpack_length) {
+    _pack_error = true;
+    return;
+  }
+
+  const unsigned char *p = (const unsigned char *)_unpack_data + _unpack_p;
+  value = vector_uchar(p, p + blob_size);
+  _unpack_p += blob_size;
+}
+
 /**
 /**
  * Advances to the next field after a call to pack_value() or pop().
  * Advances to the next field after a call to pack_value() or pop().
  */
  */

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

@@ -114,12 +114,12 @@ end_pack() {
  * version of begin_unpack() that takes only one parameter.
  * version of begin_unpack() that takes only one parameter.
  */
  */
 void DCPacker::
 void DCPacker::
-set_unpack_data(const string &data) {
+set_unpack_data(const vector_uchar &data) {
   nassertv(_mode == M_idle);
   nassertv(_mode == M_idle);
 
 
-  char *buffer = new char[data.length()];
-  memcpy(buffer, data.data(), data.length());
-  set_unpack_data(buffer, data.length(), true);
+  char *buffer = new char[data.size()];
+  memcpy(buffer, data.data(), data.size());
+  set_unpack_data(buffer, data.size(), true);
 }
 }
 
 
 /**
 /**
@@ -721,11 +721,11 @@ pack_object(PyObject *object) {
       pack_string(string(buffer, length));
       pack_string(string(buffer, length));
     }
     }
   } else if (PyBytes_Check(object)) {
   } else if (PyBytes_Check(object)) {
-    char *buffer;
+    const unsigned char *buffer;
     Py_ssize_t length;
     Py_ssize_t length;
-    PyBytes_AsStringAndSize(object, &buffer, &length);
+    PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
     if (buffer) {
     if (buffer) {
-      pack_string(string(buffer, length));
+      pack_blob(vector_uchar(buffer, buffer + length));
     }
     }
 #else
 #else
   } else if (PyString_Check(object) || PyUnicode_Check(object)) {
   } else if (PyString_Check(object) || PyUnicode_Check(object)) {
@@ -1118,9 +1118,9 @@ enquote_string(ostream &out, char quote_mark, const string &str) {
  * Outputs the indicated string as a hex constant.
  * Outputs the indicated string as a hex constant.
  */
  */
 void DCPacker::
 void DCPacker::
-output_hex_string(ostream &out, const string &str) {
+output_hex_string(ostream &out, const vector_uchar &str) {
   out << '<';
   out << '<';
-  for (string::const_iterator pi = str.begin();
+  for (vector_uchar::const_iterator pi = str.begin();
        pi != str.end();
        pi != str.end();
        ++pi) {
        ++pi) {
     char buffer[10];
     char buffer[10];

+ 13 - 6
direct/src/dcparser/dcPacker.h

@@ -41,7 +41,7 @@ PUBLISHED:
   void begin_pack(const DCPackerInterface *root);
   void begin_pack(const DCPackerInterface *root);
   bool end_pack();
   bool end_pack();
 
 
-  void set_unpack_data(const std::string &data);
+  void set_unpack_data(const vector_uchar &data);
 public:
 public:
   void set_unpack_data(const char *unpack_data, size_t unpack_length,
   void set_unpack_data(const char *unpack_data, size_t unpack_length,
                        bool owns_unpack_data);
                        bool owns_unpack_data);
@@ -75,7 +75,8 @@ PUBLISHED:
   INLINE void pack_int64(int64_t value);
   INLINE void pack_int64(int64_t value);
   INLINE void pack_uint64(uint64_t value);
   INLINE void pack_uint64(uint64_t value);
   INLINE void pack_string(const std::string &value);
   INLINE void pack_string(const std::string &value);
-  INLINE void pack_literal_value(const std::string &value);
+  INLINE void pack_blob(const vector_uchar &value);
+  INLINE void pack_literal_value(const vector_uchar &value);
   void pack_default_value();
   void pack_default_value();
 
 
   INLINE double unpack_double();
   INLINE double unpack_double();
@@ -84,7 +85,8 @@ PUBLISHED:
   INLINE int64_t unpack_int64();
   INLINE int64_t unpack_int64();
   INLINE uint64_t unpack_uint64();
   INLINE uint64_t unpack_uint64();
   INLINE std::string unpack_string();
   INLINE std::string unpack_string();
-  INLINE std::string unpack_literal_value();
+  INLINE vector_uchar unpack_blob();
+  INLINE vector_uchar unpack_literal_value();
   void unpack_validate();
   void unpack_validate();
   void unpack_skip();
   void unpack_skip();
 
 
@@ -97,7 +99,8 @@ public:
   INLINE void unpack_int64(int64_t &value);
   INLINE void unpack_int64(int64_t &value);
   INLINE void unpack_uint64(uint64_t &value);
   INLINE void unpack_uint64(uint64_t &value);
   INLINE void unpack_string(std::string &value);
   INLINE void unpack_string(std::string &value);
-  INLINE void unpack_literal_value(std::string &value);
+  INLINE void unpack_blob(vector_uchar &value);
+  INLINE void unpack_literal_value(vector_uchar &value);
 
 
 PUBLISHED:
 PUBLISHED:
 
 
@@ -119,6 +122,7 @@ PUBLISHED:
 
 
   INLINE size_t get_length() const;
   INLINE size_t get_length() const;
   INLINE std::string get_string() const;
   INLINE std::string get_string() const;
+  INLINE vector_uchar get_bytes() const;
   INLINE size_t get_unpack_length() const;
   INLINE size_t get_unpack_length() const;
   INLINE std::string get_unpack_string() const;
   INLINE std::string get_unpack_string() const;
 public:
 public:
@@ -126,7 +130,7 @@ public:
   INLINE const char *get_data() const;
   INLINE const char *get_data() const;
   INLINE char *take_data();
   INLINE char *take_data();
 
 
-  INLINE void append_data(const char *buffer, size_t size);
+  INLINE void append_data(const unsigned char *buffer, size_t size);
   INLINE char *get_write_pointer(size_t size);
   INLINE char *get_write_pointer(size_t size);
 
 
   INLINE const char *get_unpack_data() const;
   INLINE const char *get_unpack_data() const;
@@ -148,6 +152,7 @@ PUBLISHED:
   INLINE void raw_pack_uint64(uint64_t value);
   INLINE void raw_pack_uint64(uint64_t value);
   INLINE void raw_pack_float64(double value);
   INLINE void raw_pack_float64(double value);
   INLINE void raw_pack_string(const std::string &value);
   INLINE void raw_pack_string(const std::string &value);
+  INLINE void raw_pack_blob(const vector_uchar &value);
 
 
 // this is a hack to allw me to get in and out of 32bit Mode Faster need to
 // this is a hack to allw me to get in and out of 32bit Mode Faster need to
 // agree with channel_type in dcbase.h
 // agree with channel_type in dcbase.h
@@ -165,6 +170,7 @@ PUBLISHED:
   INLINE uint64_t raw_unpack_uint64();
   INLINE uint64_t raw_unpack_uint64();
   INLINE double raw_unpack_float64();
   INLINE double raw_unpack_float64();
   INLINE std::string raw_unpack_string();
   INLINE std::string raw_unpack_string();
+  INLINE vector_uchar raw_unpack_blob();
 
 
 public:
 public:
   INLINE void raw_unpack_int8(int &value);
   INLINE void raw_unpack_int8(int &value);
@@ -177,10 +183,11 @@ public:
   INLINE void raw_unpack_uint64(uint64_t &value);
   INLINE void raw_unpack_uint64(uint64_t &value);
   INLINE void raw_unpack_float64(double &value);
   INLINE void raw_unpack_float64(double &value);
   INLINE void raw_unpack_string(std::string &value);
   INLINE void raw_unpack_string(std::string &value);
+  INLINE void raw_unpack_blob(vector_uchar &value);
 
 
 public:
 public:
   static void enquote_string(std::ostream &out, char quote_mark, const std::string &str);
   static void enquote_string(std::ostream &out, char quote_mark, const std::string &str);
-  static void output_hex_string(std::ostream &out, const std::string &str);
+  static void output_hex_string(std::ostream &out, const vector_uchar &str);
 
 
 private:
 private:
   INLINE void advance();
   INLINE void advance();

+ 16 - 0
direct/src/dcparser/dcPackerInterface.cxx

@@ -248,6 +248,14 @@ pack_string(DCPackData &, const string &, bool &pack_error, bool &) const {
   pack_error = true;
   pack_error = true;
 }
 }
 
 
+/**
+ * Packs the indicated numeric or string value into the stream.
+ */
+void DCPackerInterface::
+pack_blob(DCPackData &, const vector_uchar &, bool &pack_error, bool &) const {
+  pack_error = true;
+}
+
 /**
 /**
  * Packs the field's specified default value (or a sensible default if no
  * Packs the field's specified default value (or a sensible default if no
  * value is specified) into the stream.  Returns true if the default value is
  * value is specified) into the stream.  Returns true if the default value is
@@ -306,6 +314,14 @@ unpack_string(const char *, size_t, size_t &, string &, bool &pack_error, bool &
   pack_error = true;
   pack_error = true;
 }
 }
 
 
+/**
+ * Unpacks the current numeric or string value from the stream.
+ */
+void DCPackerInterface::
+unpack_blob(const char *, size_t, size_t &, vector_uchar &, bool &pack_error, bool &) const {
+  pack_error = true;
+}
+
 /**
 /**
  * Internally unpacks the current numeric or string value and validates it
  * Internally unpacks the current numeric or string value and validates it
  * against the type range limits, but does not return the value.  Returns true
  * against the type range limits, but does not return the value.  Returns true

+ 7 - 2
direct/src/dcparser/dcPackerInterface.h

@@ -16,6 +16,7 @@
 
 
 #include "dcbase.h"
 #include "dcbase.h"
 #include "dcSubatomicType.h"
 #include "dcSubatomicType.h"
+#include "vector_uchar.h"
 
 
 class DCFile;
 class DCFile;
 class DCField;
 class DCField;
@@ -37,8 +38,8 @@ enum DCPackType {
 
 
   // These PackTypes are all fundamental types, and should be packed (or
   // These PackTypes are all fundamental types, and should be packed (or
   // unpacked) with the corresponding call to pack_double(), pack_int(), etc.
   // unpacked) with the corresponding call to pack_double(), pack_int(), etc.
-  // PT_blob is the same as PT_string, but implies that the string contains
-  // binary data.
+  // PT_blob is similar to PT_string, except that it contains arbitrary binary
+  // data instead of just UTF-8 text.
   PT_double,
   PT_double,
   PT_int,
   PT_int,
   PT_uint,
   PT_uint,
@@ -113,6 +114,8 @@ public:
                            bool &pack_error, bool &range_error) const;
                            bool &pack_error, bool &range_error) const;
   virtual void pack_string(DCPackData &pack_data, const std::string &value,
   virtual void pack_string(DCPackData &pack_data, const std::string &value,
                            bool &pack_error, bool &range_error) const;
                            bool &pack_error, bool &range_error) const;
+  virtual void pack_blob(DCPackData &pack_data, const vector_uchar &value,
+                         bool &pack_error, bool &range_error) const;
   virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const;
   virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const;
 
 
   virtual void unpack_double(const char *data, size_t length, size_t &p,
   virtual void unpack_double(const char *data, size_t length, size_t &p,
@@ -127,6 +130,8 @@ public:
                              uint64_t &value, bool &pack_error, bool &range_error) const;
                              uint64_t &value, bool &pack_error, bool &range_error) const;
   virtual void unpack_string(const char *data, size_t length, size_t &p,
   virtual void unpack_string(const char *data, size_t length, size_t &p,
                              std::string &value, bool &pack_error, bool &range_error) const;
                              std::string &value, bool &pack_error, bool &range_error) const;
+  virtual void unpack_blob(const char *data, size_t length, size_t &p,
+                           vector_uchar &value, bool &pack_error, bool &range_error) const;
   virtual bool unpack_validate(const char *data, size_t length, size_t &p,
   virtual bool unpack_validate(const char *data, size_t length, size_t &p,
                                bool &pack_error, bool &range_error) const;
                                bool &pack_error, bool &range_error) const;
   virtual bool unpack_skip(const char *data, size_t length, size_t &p,
   virtual bool unpack_skip(const char *data, size_t length, size_t &p,

文件差异内容过多而无法显示
+ 434 - 547
direct/src/dcparser/dcParser.cxx.prebuilt


+ 66 - 63
direct/src/dcparser/dcParser.h.prebuilt

@@ -1,20 +1,19 @@
-/* A Bison parser, made by GNU Bison 2.4.2.  */
+/* A Bison parser, made by GNU Bison 3.1.  */
+
+/* Bison interface for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc.
 
 
-/* Skeleton interface for Bison's Yacc-like parsers in C
-   
-      Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software
-   Foundation, Inc.
-   
    This program is free software: you can redistribute it and/or modify
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 
@@ -27,66 +26,74 @@
    special exception, which will cause the skeleton and the resulting
    special exception, which will cause the skeleton and the resulting
    Bison output files to be licensed under the GNU General Public
    Bison output files to be licensed under the GNU General Public
    License without this special exception.
    License without this special exception.
-   
+
    This special exception was added by the Free Software Foundation in
    This special exception was added by the Free Software Foundation in
    version 2.2 of Bison.  */
    version 2.2 of Bison.  */
 
 
+#ifndef YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED
+# define YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int dcyydebug;
+#endif
 
 
-/* Tokens.  */
+/* Token type.  */
 #ifndef YYTOKENTYPE
 #ifndef YYTOKENTYPE
 # define YYTOKENTYPE
 # define YYTOKENTYPE
-   /* Put the tokens into the symbol table, so that GDB and other debuggers
-      know about them.  */
-   enum yytokentype {
-     UNSIGNED_INTEGER = 258,
-     SIGNED_INTEGER = 259,
-     REAL = 260,
-     STRING = 261,
-     HEX_STRING = 262,
-     IDENTIFIER = 263,
-     KEYWORD = 264,
-     KW_DCLASS = 265,
-     KW_STRUCT = 266,
-     KW_FROM = 267,
-     KW_IMPORT = 268,
-     KW_TYPEDEF = 269,
-     KW_KEYWORD = 270,
-     KW_SWITCH = 271,
-     KW_CASE = 272,
-     KW_DEFAULT = 273,
-     KW_BREAK = 274,
-     KW_INT8 = 275,
-     KW_INT16 = 276,
-     KW_INT32 = 277,
-     KW_INT64 = 278,
-     KW_UINT8 = 279,
-     KW_UINT16 = 280,
-     KW_UINT32 = 281,
-     KW_UINT64 = 282,
-     KW_FLOAT64 = 283,
-     KW_STRING = 284,
-     KW_BLOB = 285,
-     KW_BLOB32 = 286,
-     KW_INT8ARRAY = 287,
-     KW_INT16ARRAY = 288,
-     KW_INT32ARRAY = 289,
-     KW_UINT8ARRAY = 290,
-     KW_UINT16ARRAY = 291,
-     KW_UINT32ARRAY = 292,
-     KW_UINT32UINT8ARRAY = 293,
-     KW_CHAR = 294,
-     START_DC = 295,
-     START_PARAMETER_VALUE = 296,
-     START_PARAMETER_DESCRIPTION = 297
-   };
+  enum yytokentype
+  {
+    UNSIGNED_INTEGER = 258,
+    SIGNED_INTEGER = 259,
+    REAL = 260,
+    STRING = 261,
+    IDENTIFIER = 262,
+    HEX_STRING = 263,
+    KEYWORD = 264,
+    KW_DCLASS = 265,
+    KW_STRUCT = 266,
+    KW_FROM = 267,
+    KW_IMPORT = 268,
+    KW_TYPEDEF = 269,
+    KW_KEYWORD = 270,
+    KW_SWITCH = 271,
+    KW_CASE = 272,
+    KW_DEFAULT = 273,
+    KW_BREAK = 274,
+    KW_INT8 = 275,
+    KW_INT16 = 276,
+    KW_INT32 = 277,
+    KW_INT64 = 278,
+    KW_UINT8 = 279,
+    KW_UINT16 = 280,
+    KW_UINT32 = 281,
+    KW_UINT64 = 282,
+    KW_FLOAT64 = 283,
+    KW_STRING = 284,
+    KW_BLOB = 285,
+    KW_BLOB32 = 286,
+    KW_INT8ARRAY = 287,
+    KW_INT16ARRAY = 288,
+    KW_INT32ARRAY = 289,
+    KW_UINT8ARRAY = 290,
+    KW_UINT16ARRAY = 291,
+    KW_UINT32ARRAY = 292,
+    KW_UINT32UINT8ARRAY = 293,
+    KW_CHAR = 294,
+    START_DC = 295,
+    START_PARAMETER_VALUE = 296,
+    START_PARAMETER_DESCRIPTION = 297
+  };
 #endif
 #endif
 /* Tokens.  */
 /* Tokens.  */
 #define UNSIGNED_INTEGER 258
 #define UNSIGNED_INTEGER 258
 #define SIGNED_INTEGER 259
 #define SIGNED_INTEGER 259
 #define REAL 260
 #define REAL 260
 #define STRING 261
 #define STRING 261
-#define HEX_STRING 262
-#define IDENTIFIER 263
+#define IDENTIFIER 262
+#define HEX_STRING 263
 #define KEYWORD 264
 #define KEYWORD 264
 #define KW_DCLASS 265
 #define KW_DCLASS 265
 #define KW_STRUCT 266
 #define KW_STRUCT 266
@@ -122,15 +129,11 @@
 #define START_PARAMETER_VALUE 296
 #define START_PARAMETER_VALUE 296
 #define START_PARAMETER_DESCRIPTION 297
 #define START_PARAMETER_DESCRIPTION 297
 
 
+/* Value type.  */
 
 
 
 
-
-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
-
-# define yystype YYSTYPE /* obsolescent; will be withdrawn */
-# define YYSTYPE_IS_DECLARED 1
-#endif
-
 extern YYSTYPE dcyylval;
 extern YYSTYPE dcyylval;
 
 
+int dcyyparse (void);
 
 
+#endif /* !YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED  */

+ 28 - 27
direct/src/dcparser/dcParser.yxx

@@ -57,7 +57,7 @@ dc_init_parser(istream &in, const string &filename, DCFile &file) {
 }
 }
 
 
 void
 void
-dc_init_parser_parameter_value(istream &in, const string &filename, 
+dc_init_parser_parameter_value(istream &in, const string &filename,
                                DCPacker &packer) {
                                DCPacker &packer) {
   dc_file = nullptr;
   dc_file = nullptr;
   current_packer = &packer;
   current_packer = &packer;
@@ -89,15 +89,16 @@ dc_cleanup_parser() {
 %token <u.uint64> UNSIGNED_INTEGER
 %token <u.uint64> UNSIGNED_INTEGER
 %token <u.int64> SIGNED_INTEGER
 %token <u.int64> SIGNED_INTEGER
 %token <u.real> REAL
 %token <u.real> REAL
-%token <str> STRING HEX_STRING IDENTIFIER
+%token <str> STRING IDENTIFIER
+%token <bytes> HEX_STRING
 %token <u.keyword> KEYWORD
 %token <u.keyword> KEYWORD
 
 
-%token KW_DCLASS 
-%token KW_STRUCT 
-%token KW_FROM 
-%token KW_IMPORT 
-%token KW_TYPEDEF 
-%token KW_KEYWORD 
+%token KW_DCLASS
+%token KW_STRUCT
+%token KW_FROM
+%token KW_IMPORT
+%token KW_TYPEDEF
+%token KW_KEYWORD
 %token KW_SWITCH
 %token KW_SWITCH
 %token KW_CASE
 %token KW_CASE
 %token KW_DEFAULT
 %token KW_DEFAULT
@@ -224,7 +225,7 @@ import:
 {
 {
   dc_file->add_import_module($2);
   dc_file->add_import_module($2);
 }
 }
-        | KW_FROM import_identifier KW_IMPORT 
+        | KW_FROM import_identifier KW_IMPORT
 {
 {
   dc_file->add_import_module($2);
   dc_file->add_import_module($2);
 }
 }
@@ -255,7 +256,7 @@ typedef_decl:
 {
 {
   if ($2 != nullptr) {
   if ($2 != nullptr) {
     DCTypedef *dtypedef = new DCTypedef($2);
     DCTypedef *dtypedef = new DCTypedef($2);
-    
+
     if (!dc_file->add_typedef(dtypedef)) {
     if (!dc_file->add_typedef(dtypedef)) {
       DCTypedef *old_typedef = dc_file->get_typedef_by_name(dtypedef->get_name());
       DCTypedef *old_typedef = dc_file->get_typedef_by_name(dtypedef->get_name());
       if (old_typedef->is_bogus_typedef()) {
       if (old_typedef->is_bogus_typedef()) {
@@ -294,7 +295,7 @@ dclass_or_struct:
         ;
         ;
 
 
 dclass:
 dclass:
-        KW_DCLASS optional_name 
+        KW_DCLASS optional_name
 {
 {
   current_class = new DCClass(dc_file, $2, false, false);
   current_class = new DCClass(dc_file, $2, false, false);
 }
 }
@@ -322,7 +323,7 @@ dclass_name:
     if (dclass->is_struct()) {
     if (dclass->is_struct()) {
       yyerror("struct name not allowed");
       yyerror("struct name not allowed");
     }
     }
-  
+
     $$ = dclass;
     $$ = dclass;
   }
   }
 }
 }
@@ -398,7 +399,7 @@ dclass_field:
         ;
         ;
 
 
 struct:
 struct:
-        KW_STRUCT optional_name 
+        KW_STRUCT optional_name
 {
 {
   current_class = new DCClass(dc_file, $2, true, false);
   current_class = new DCClass(dc_file, $2, true, false);
 }
 }
@@ -426,7 +427,7 @@ struct_name:
     if (!dstruct->is_struct()) {
     if (!dstruct->is_struct()) {
       yyerror("struct name required");
       yyerror("struct name required");
     }
     }
-  
+
     $$ = dstruct;
     $$ = dstruct;
   }
   }
 }
 }
@@ -538,7 +539,7 @@ unnamed_parameter:
 
 
 named_parameter_with_default:
 named_parameter_with_default:
         named_parameter
         named_parameter
-        | named_parameter '=' 
+        | named_parameter '='
 {
 {
   current_packer = &default_packer;
   current_packer = &default_packer;
   current_packer->clear_data();
   current_packer->clear_data();
@@ -553,7 +554,7 @@ named_parameter_with_default:
     is_valid = $1->is_valid();
     is_valid = $1->is_valid();
   }
   }
   if (current_packer->end_pack()) {
   if (current_packer->end_pack()) {
-    $1->set_default_value(current_packer->get_string());
+    $1->set_default_value(current_packer->get_bytes());
 
 
   } else {
   } else {
     if (is_valid) {
     if (is_valid) {
@@ -568,7 +569,7 @@ named_parameter_with_default:
 
 
 unnamed_parameter_with_default:
 unnamed_parameter_with_default:
         unnamed_parameter
         unnamed_parameter
-        | unnamed_parameter '=' 
+        | unnamed_parameter '='
 {
 {
   current_packer = &default_packer;
   current_packer = &default_packer;
   current_packer->clear_data();
   current_packer->clear_data();
@@ -583,7 +584,7 @@ unnamed_parameter_with_default:
     is_valid = $1->is_valid();
     is_valid = $1->is_valid();
   }
   }
   if (current_packer->end_pack()) {
   if (current_packer->end_pack()) {
-    $1->set_default_value(current_packer->get_string());
+    $1->set_default_value(current_packer->get_bytes());
 
 
   } else {
   } else {
     if (is_valid) {
     if (is_valid) {
@@ -663,7 +664,7 @@ simple_type_name:
   $$ = simple_param;
   $$ = simple_param;
 }
 }
         | simple_type_name '%' number
         | simple_type_name '%' number
-{ 
+{
   DCSimpleParameter *simple_param = $1->as_simple_parameter();
   DCSimpleParameter *simple_param = $1->as_simple_parameter();
   nassertr(simple_param != nullptr, 0);
   nassertr(simple_param != nullptr, 0);
   if (!simple_param->is_numeric_type()) {
   if (!simple_param->is_numeric_type()) {
@@ -703,10 +704,10 @@ type_name:
           dtypedef = new DCTypedef($1);
           dtypedef = new DCTypedef($1);
         }
         }
       }
       }
-      
+
       dc_file->add_typedef(dtypedef);
       dc_file->add_typedef(dtypedef);
     }
     }
-    
+
     $$ = dtypedef->make_new_parameter();
     $$ = dtypedef->make_new_parameter();
   }
   }
 }
 }
@@ -974,7 +975,7 @@ parameter_value:
 {
 {
   if ($1 != current_packer->get_current_field_name()) {
   if ($1 != current_packer->get_current_field_name()) {
     ostringstream strm;
     ostringstream strm;
-    strm << "Got '" << $1 << "', expected '" 
+    strm << "Got '" << $1 << "', expected '"
          << current_packer->get_current_field_name() << "'";
          << current_packer->get_current_field_name() << "'";
     yyerror(strm.str());
     yyerror(strm.str());
   }
   }
@@ -1005,7 +1006,7 @@ parameter_actual_value:
 {
 {
   current_packer->pack_literal_value($1);
   current_packer->pack_literal_value($1);
 }
 }
-        | '{' 
+        | '{'
 {
 {
   current_packer->push();
   current_packer->push();
 }
 }
@@ -1013,7 +1014,7 @@ parameter_actual_value:
 {
 {
   current_packer->pop();
   current_packer->pop();
 }
 }
-        | '[' 
+        | '['
 {
 {
   current_packer->push();
   current_packer->push();
 }
 }
@@ -1021,7 +1022,7 @@ parameter_actual_value:
 {
 {
   current_packer->pop();
   current_packer->pop();
 }
 }
-        | '(' 
+        | '('
 {
 {
   current_packer->push();
   current_packer->push();
 }
 }
@@ -1223,7 +1224,7 @@ molecular_atom_list:
   if ($3 != nullptr) {
   if ($3 != nullptr) {
     current_molecular->add_atomic($3);
     current_molecular->add_atomic($3);
     if (!$3->is_bogus_field() && !current_molecular->compare_keywords(*$3)) {
     if (!$3->is_bogus_field() && !current_molecular->compare_keywords(*$3)) {
-      yyerror("Mismatched keywords in molecule between " + 
+      yyerror("Mismatched keywords in molecule between " +
               current_molecular->get_atomic(0)->get_name() + " and " +
               current_molecular->get_atomic(0)->get_name() + " and " +
               $3->get_name());
               $3->get_name());
     }
     }
@@ -1283,7 +1284,7 @@ switch_case:
     yyerror("Invalid value for switch parameter");
     yyerror("Invalid value for switch parameter");
     current_switch->add_invalid_case();
     current_switch->add_invalid_case();
   } else {
   } else {
-    int case_index = current_switch->add_case(current_packer->get_string());
+    int case_index = current_switch->add_case(current_packer->get_bytes());
     if (case_index == -1) {
     if (case_index == -1) {
       yyerror("Duplicate case value");
       yyerror("Duplicate case value");
     }
     }

+ 2 - 0
direct/src/dcparser/dcParserDefs.h

@@ -16,6 +16,7 @@
 
 
 #include "dcbase.h"
 #include "dcbase.h"
 #include "dcSubatomicType.h"
 #include "dcSubatomicType.h"
+#include "vector_uchar.h"
 
 
 class DCFile;
 class DCFile;
 class DCClass;
 class DCClass;
@@ -61,6 +62,7 @@ public:
     const DCKeyword *keyword;
     const DCKeyword *keyword;
   } u;
   } u;
   std::string str;
   std::string str;
+  vector_uchar bytes;
 };
 };
 
 
 // The yacc-generated code expects to use the symbol 'YYSTYPE' to refer to the
 // The yacc-generated code expects to use the symbol 'YYSTYPE' to refer to the

+ 119 - 0
direct/src/dcparser/dcSimpleParameter.cxx

@@ -1069,6 +1069,52 @@ pack_string(DCPackData &pack_data, const string &value,
   }
   }
 }
 }
 
 
+/**
+ * Packs the indicated numeric or string value into the stream.
+ */
+void DCSimpleParameter::
+pack_blob(DCPackData &pack_data, const vector_uchar &value,
+          bool &pack_error, bool &range_error) const {
+  size_t blob_size = value.size();
+
+  switch (_type) {
+  case ST_char:
+  case ST_uint8:
+  case ST_int8:
+    if (blob_size == 0) {
+      pack_error = true;
+    } else {
+      if (blob_size != 1) {
+        range_error = true;
+      }
+      _uint_range.validate((unsigned int)value[0], range_error);
+      do_pack_uint8(pack_data.get_write_pointer(1), (unsigned int)value[0]);
+    }
+    break;
+
+  case ST_string:
+  case ST_blob:
+    _uint_range.validate(blob_size, range_error);
+    validate_uint_limits(blob_size, 16, range_error);
+    if (_num_length_bytes != 0) {
+      do_pack_uint16(pack_data.get_write_pointer(2), blob_size);
+    }
+    pack_data.append_data((const char *)value.data(), blob_size);
+    break;
+
+  case ST_blob32:
+    _uint_range.validate(blob_size, range_error);
+    if (_num_length_bytes != 0) {
+      do_pack_uint32(pack_data.get_write_pointer(4), blob_size);
+    }
+    pack_data.append_data((const char *)value.data(), blob_size);
+    break;
+
+  default:
+    pack_error = true;
+  }
+}
+
 /**
 /**
  * Packs the simpleParameter's specified default value (or a sensible default
  * Packs the simpleParameter's specified default value (or a sensible default
  * if no value is specified) into the stream.  Returns true if the default
  * if no value is specified) into the stream.  Returns true if the default
@@ -1937,6 +1983,79 @@ unpack_string(const char *data, size_t length, size_t &p, string &value,
   return;
   return;
 }
 }
 
 
+/**
+ * Unpacks the current numeric or string value from the stream.
+ */
+void DCSimpleParameter::
+unpack_blob(const char *data, size_t length, size_t &p, vector_uchar &value,
+            bool &pack_error, bool &range_error) const {
+  // If the type is a single byte, unpack it into a string of length 1.
+  switch (_type) {
+  case ST_char:
+  case ST_int8:
+  case ST_uint8:
+    {
+      if (p + 1 > length) {
+        pack_error = true;
+        return;
+      }
+      unsigned int int_value = do_unpack_uint8(data + p);
+      _uint_range.validate(int_value, range_error);
+      value.resize(1);
+      value[0] = int_value;
+      p++;
+    }
+    return;
+
+  default:
+    break;
+  }
+
+  size_t blob_size;
+
+  if (_num_length_bytes == 0) {
+    blob_size = _fixed_byte_size;
+
+  } else {
+    switch (_type) {
+    case ST_string:
+    case ST_blob:
+      if (p + 2 > length) {
+        pack_error = true;
+        return;
+      }
+      blob_size = do_unpack_uint16(data + p);
+      p += 2;
+      break;
+
+    case ST_blob32:
+      if (p + 4 > length) {
+        pack_error = true;
+        return;
+      }
+      blob_size = do_unpack_uint32(data + p);
+      p += 4;
+      break;
+
+    default:
+      pack_error = true;
+      return;
+    }
+  }
+
+  _uint_range.validate(blob_size, range_error);
+
+  if (p + blob_size > length) {
+    pack_error = true;
+    return;
+  }
+  value = vector_uchar((const unsigned char *)data + p,
+                       (const unsigned char *)data + p + blob_size);
+  p += blob_size;
+
+  return;
+}
+
 /**
 /**
  * Internally unpacks the current numeric or string value and validates it
  * Internally unpacks the current numeric or string value and validates it
  * against the type range limits, but does not return the value.  Returns true
  * against the type range limits, but does not return the value.  Returns true

+ 4 - 0
direct/src/dcparser/dcSimpleParameter.h

@@ -62,6 +62,8 @@ public:
                            bool &pack_error, bool &range_error) const;
                            bool &pack_error, bool &range_error) const;
   virtual void pack_string(DCPackData &pack_data, const std::string &value,
   virtual void pack_string(DCPackData &pack_data, const std::string &value,
                            bool &pack_error, bool &range_error) const;
                            bool &pack_error, bool &range_error) const;
+  virtual void pack_blob(DCPackData &pack_data, const vector_uchar &value,
+                         bool &pack_error, bool &range_error) const;
   virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const;
   virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const;
 
 
   virtual void unpack_double(const char *data, size_t length, size_t &p,
   virtual void unpack_double(const char *data, size_t length, size_t &p,
@@ -76,6 +78,8 @@ public:
                              uint64_t &value, bool &pack_error, bool &range_error) const;
                              uint64_t &value, bool &pack_error, bool &range_error) const;
   virtual void unpack_string(const char *data, size_t length, size_t &p,
   virtual void unpack_string(const char *data, size_t length, size_t &p,
                              std::string &value, bool &pack_error, bool &range_error) const;
                              std::string &value, bool &pack_error, bool &range_error) const;
+  virtual void unpack_blob(const char *data, size_t length, size_t &p,
+                           vector_uchar &value, bool &pack_error, bool &range_error) const;
   virtual bool unpack_validate(const char *data, size_t length, size_t &p,
   virtual bool unpack_validate(const char *data, size_t length, size_t &p,
                                bool &pack_error, bool &range_error) const;
                                bool &pack_error, bool &range_error) const;
   virtual bool unpack_skip(const char *data, size_t length, size_t &p,
   virtual bool unpack_skip(const char *data, size_t length, size_t &p,

+ 8 - 7
direct/src/dcparser/dcSwitch.cxx

@@ -109,7 +109,7 @@ get_num_cases() const {
  * if no case has this value.
  * if no case has this value.
  */
  */
 int DCSwitch::
 int DCSwitch::
-get_case_by_value(const string &case_value) const {
+get_case_by_value(const vector_uchar &case_value) const {
   CasesByValue::const_iterator vi;
   CasesByValue::const_iterator vi;
   vi = _cases_by_value.find(case_value);
   vi = _cases_by_value.find(case_value);
   if (vi != _cases_by_value.end()) {
   if (vi != _cases_by_value.end()) {
@@ -140,9 +140,9 @@ get_default_case() const {
 /**
 /**
  * Returns the packed value associated with the indicated case.
  * Returns the packed value associated with the indicated case.
  */
  */
-string DCSwitch::
+vector_uchar DCSwitch::
 get_value(int case_index) const {
 get_value(int case_index) const {
-  nassertr(case_index >= 0 && case_index < (int)_cases.size(), string());
+  nassertr(case_index >= 0 && case_index < (int)_cases.size(), vector_uchar());
   return _cases[case_index]->_value;
   return _cases[case_index]->_value;
 }
 }
 
 
@@ -198,7 +198,7 @@ is_field_valid() const {
  * -1. This is normally called only by the parser.
  * -1. This is normally called only by the parser.
  */
  */
 int DCSwitch::
 int DCSwitch::
-add_case(const string &value) {
+add_case(const vector_uchar &value) {
   int case_index = (int)_cases.size();
   int case_index = (int)_cases.size();
   if (!_cases_by_value.insert(CasesByValue::value_type(value, case_index)).second) {
   if (!_cases_by_value.insert(CasesByValue::value_type(value, case_index)).second) {
     add_invalid_case();
     add_invalid_case();
@@ -283,7 +283,8 @@ add_break() {
 const DCPackerInterface *DCSwitch::
 const DCPackerInterface *DCSwitch::
 apply_switch(const char *value_data, size_t length) const {
 apply_switch(const char *value_data, size_t length) const {
   CasesByValue::const_iterator vi;
   CasesByValue::const_iterator vi;
-  vi = _cases_by_value.find(string(value_data, length));
+  vi = _cases_by_value.find(vector_uchar((const unsigned char *)value_data,
+                                         (const unsigned char *)value_data + length));
   if (vi != _cases_by_value.end()) {
   if (vi != _cases_by_value.end()) {
     return _cases[(*vi).second]->_fields;
     return _cases[(*vi).second]->_fields;
   }
   }
@@ -421,7 +422,7 @@ generate_hash(HashGenerator &hashgen) const {
   Cases::const_iterator ci;
   Cases::const_iterator ci;
   for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
   for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
     const SwitchCase *dcase = (*ci);
     const SwitchCase *dcase = (*ci);
-    hashgen.add_string(dcase->_value);
+    hashgen.add_blob(dcase->_value);
 
 
     const SwitchFields *fields = dcase->_fields;
     const SwitchFields *fields = dcase->_fields;
     hashgen.add_int(fields->_fields.size());
     hashgen.add_int(fields->_fields.size());
@@ -702,7 +703,7 @@ do_check_match(const DCPackerInterface *) const {
  *
  *
  */
  */
 DCSwitch::SwitchCase::
 DCSwitch::SwitchCase::
-SwitchCase(const string &value, DCSwitch::SwitchFields *fields) :
+SwitchCase(const vector_uchar &value, DCSwitch::SwitchFields *fields) :
   _value(value),
   _value(value),
   _fields(fields)
   _fields(fields)
 {
 {

+ 6 - 6
direct/src/dcparser/dcSwitch.h

@@ -40,18 +40,18 @@ PUBLISHED:
   DCField *get_key_parameter() const;
   DCField *get_key_parameter() const;
 
 
   int get_num_cases() const;
   int get_num_cases() const;
-  int get_case_by_value(const std::string &case_value) const;
+  int get_case_by_value(const vector_uchar &case_value) const;
   DCPackerInterface *get_case(int n) const;
   DCPackerInterface *get_case(int n) const;
   DCPackerInterface *get_default_case() const;
   DCPackerInterface *get_default_case() const;
 
 
-  std::string get_value(int case_index) const;
+  vector_uchar get_value(int case_index) const;
   int get_num_fields(int case_index) const;
   int get_num_fields(int case_index) const;
   DCField *get_field(int case_index, int n) const;
   DCField *get_field(int case_index, int n) const;
   DCField *get_field_by_name(int case_index, const std::string &name) const;
   DCField *get_field_by_name(int case_index, const std::string &name) const;
 
 
 public:
 public:
   bool is_field_valid() const;
   bool is_field_valid() const;
-  int add_case(const std::string &value);
+  int add_case(const vector_uchar &value);
   void add_invalid_case();
   void add_invalid_case();
   bool add_default();
   bool add_default();
   bool add_field(DCField *field);
   bool add_field(DCField *field);
@@ -98,13 +98,13 @@ public:
 
 
   class SwitchCase {
   class SwitchCase {
   public:
   public:
-    SwitchCase(const std::string &value, SwitchFields *fields);
+    SwitchCase(const vector_uchar &value, SwitchFields *fields);
     ~SwitchCase();
     ~SwitchCase();
 
 
     bool do_check_match_switch_case(const SwitchCase *other) const;
     bool do_check_match_switch_case(const SwitchCase *other) const;
 
 
   public:
   public:
-    std::string _value;
+    vector_uchar _value;
     SwitchFields *_fields;
     SwitchFields *_fields;
   };
   };
 
 
@@ -137,7 +137,7 @@ private:
   bool _fields_added;
   bool _fields_added;
 
 
   // This map indexes into the _cases vector, above.
   // This map indexes into the _cases vector, above.
-  typedef pmap<std::string, int> CasesByValue;
+  typedef pmap<vector_uchar, int> CasesByValue;
   CasesByValue _cases_by_value;
   CasesByValue _cases_by_value;
 };
 };
 
 

+ 12 - 0
direct/src/dcparser/hashGenerator.cxx

@@ -56,6 +56,18 @@ add_string(const std::string &str) {
   }
   }
 }
 }
 
 
+/**
+ * Adds a blob to the hash, by breaking it down into a sequence of integers.
+ */
+void HashGenerator::
+add_blob(const vector_uchar &bytes) {
+  add_int(bytes.size());
+  vector_uchar::const_iterator bi;
+  for (bi = bytes.begin(); bi != bytes.end(); ++bi) {
+    add_int(*bi);
+  }
+}
+
 /**
 /**
  * Returns the hash number generated.
  * Returns the hash number generated.
  */
  */

+ 2 - 0
direct/src/dcparser/hashGenerator.h

@@ -16,6 +16,7 @@
 
 
 #include "dcbase.h"
 #include "dcbase.h"
 #include "primeNumberGenerator.h"
 #include "primeNumberGenerator.h"
+#include "vector_uchar.h"
 
 
 /**
 /**
  * This class generates an arbitrary hash number from a sequence of ints.
  * This class generates an arbitrary hash number from a sequence of ints.
@@ -26,6 +27,7 @@ public:
 
 
   void add_int(int num);
   void add_int(int num);
   void add_string(const std::string &str);
   void add_string(const std::string &str);
+  void add_blob(const vector_uchar &bytes);
 
 
   unsigned long get_hash() const;
   unsigned long get_hash() const;
 
 

+ 16 - 2
direct/src/directscripts/Doxyfile.cxx

@@ -797,7 +797,15 @@ EXCLUDE                = dtool/src/parser-inc \
                          panda/src/linmath/fltnames.h \
                          panda/src/linmath/fltnames.h \
                          panda/src/linmath/dblnames.h \
                          panda/src/linmath/dblnames.h \
                          panda/src/linmath/dbl2fltnames.h \
                          panda/src/linmath/dbl2fltnames.h \
-                         panda/src/linmath/flt2dblnames.h
+                         panda/src/linmath/flt2dblnames.h \
+                         panda/src/android \
+                         panda/src/iphone \
+                         panda/src/tinydisplay \
+                         panda/src/movies/dr_flac.h \
+                         dtool/src/dtoolbase/pdtoa.cxx \
+                         dtool/src/dtoolutil/panda_getopt_long.h \
+                         dtool/src/dtoolutil/panda_getopt_impl.h \
+                         dtool/src/dtoolutil/panda_getopt_impl.cxx
 
 
 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
 # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
 # directories that are symbolic links (a Unix file system feature) are excluded
 # directories that are symbolic links (a Unix file system feature) are excluded
@@ -1972,7 +1980,13 @@ PREDEFINED             = TVOLATILE= \
                          EXTEND= \
                          EXTEND= \
                          ALLOC_DELETED_CHAIN(x)= \
                          ALLOC_DELETED_CHAIN(x)= \
                          BLOCKING= \
                          BLOCKING= \
-                         TYPENAME=typename
+                         TYPENAME=typename \
+                         MAKE_PROPERTY(x)= \
+                         MAKE_PROPERTY2(x)= \
+                         MAKE_SEQ(x)= \
+                         MAKE_SEQ_PROPERTY(x)= \
+                         MAKE_MAP_PROPERTY(x)= \
+                         MAKE_MAP_KEYS_SEQ(x)=
 
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
 # tag can be used to specify a list of macro names that should be expanded. The

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

@@ -665,7 +665,9 @@ EXCLUDE_SYMLINKS       = NO
 # for example use the pattern */test/*
 # for example use the pattern */test/*
 
 
 EXCLUDE_PATTERNS       = */Opt*-*/* \
 EXCLUDE_PATTERNS       = */Opt*-*/* \
-                         */CVS/*
+                         */CVS/* \
+                         */.git/* \
+                         */__pycache__/*
 
 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
 # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
 # (namespaces, classes, functions, etc.) that should be excluded from the 
 # (namespaces, classes, functions, etc.) that should be excluded from the 

+ 79 - 51
direct/src/directscripts/extract_docs.py

@@ -9,9 +9,20 @@ from __future__ import print_function
 
 
 __all__ = []
 __all__ = []
 
 
-import os
+import os, sys
+from distutils import sysconfig
 import panda3d, pandac
 import panda3d, pandac
-from panda3d.dtoolconfig import *
+from panda3d.interrogatedb import *
+
+
+if 'interrogate_element_is_sequence' not in globals():
+    def interrogate_element_is_sequence(element):
+        return False
+
+if 'interrogate_element_is_mapping' not in globals():
+    def interrogate_element_is_mapping(element):
+        return False
+
 
 
 LICENSE = """PANDA 3D SOFTWARE
 LICENSE = """PANDA 3D SOFTWARE
 Copyright (c) Carnegie Mellon University.  All rights reserved.
 Copyright (c) Carnegie Mellon University.  All rights reserved.
@@ -19,6 +30,16 @@ 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
 license.  You should have received a copy of this license along
 with this source code in a file named \"LICENSE.\"""".split("\n")
 with this source code in a file named \"LICENSE.\"""".split("\n")
 
 
+MAINPAGE = """@mainpage Panda3D Python API Reference
+Welcome to the Panda3D API reference.
+
+Use the links at the top of this page to browse through the list of modules or
+the list of classes.
+
+This reference is automatically generated from comments in the source code.
+"""
+
+
 def comment(code):
 def comment(code):
     if not code:
     if not code:
         return ""
         return ""
@@ -45,50 +66,17 @@ def comment(code):
         return ''
         return ''
 
 
 def block_comment(code):
 def block_comment(code):
-    if not code:
-        return ""
+    code = code.strip()
 
 
-    lines = code.split("\n")
-    newlines = []
-    indent = 0
-    reading_desc = False
-
-    for line in lines:
-        if line.startswith("////"):
-            continue
-
-        line = line.rstrip()
-        strline = line.lstrip('/ \t')
-
-        if ':' in strline:
-            pre, post = strline.split(':', 1)
-            pre = pre.rstrip()
-            if pre == "Description":
-                strline = post.lstrip()
-            elif pre in ("Class", "Access", "Function", "Created by", "Enum"):
-                continue
-
-        if strline or len(newlines) > 0:
-            newlines.append('/// ' + strline)
-
-        #if reading_desc:
-        #    newlines.append('/// ' + line[min(indent, len(line) - len(strline)):])
-        #else:
-        #    # A "Description:" text starts the description.
-        #    if strline.startswith("Description"):
-        #        strline = strline[11:].lstrip(': \t')
-        #        indent = len(line) - len(strline)
-        #        reading_desc = True
-        #        newlines.append('/// ' + strline)
-        #    else:
-        #        print line
-
-    newcode = '\n'.join(newlines)
-    if len(newcode) > 0:
-        return newcode
-    else:
+    if not code.startswith('///<') and '@verbatim' not in code:
+        code = code.replace('<', '\\<').replace('>', '\\>')
+
+    if not code or code[0] != '/':
+        # Not really a comment; get rid of it.
         return ""
         return ""
 
 
+    return code
+
 def translateFunctionName(name):
 def translateFunctionName(name):
     if name.startswith("__"):
     if name.startswith("__"):
         return name
         return name
@@ -139,11 +127,17 @@ def translated_type_name(type, scoped=True):
         return "object"
         return "object"
     elif typename == "PN_stdfloat":
     elif typename == "PN_stdfloat":
         return "float"
         return "float"
+    elif typename == "size_t":
+        return "int"
 
 
     if interrogate_type_is_atomic(type):
     if interrogate_type_is_atomic(type):
         token = interrogate_type_atomic_token(type)
         token = interrogate_type_atomic_token(type)
         if token == 7:
         if token == 7:
             return 'str'
             return 'str'
+        elif token == 8:
+            return 'long'
+        elif token == 9:
+            return 'NoneType'
         else:
         else:
             return typename
             return typename
 
 
@@ -156,12 +150,25 @@ def translated_type_name(type, scoped=True):
     else:
     else:
         return typename
         return typename
 
 
+
 def processElement(handle, element):
 def processElement(handle, element):
     if interrogate_element_has_comment(element):
     if interrogate_element_has_comment(element):
         print(comment(interrogate_element_comment(element)), file=handle)
         print(comment(interrogate_element_comment(element)), file=handle)
+    elif interrogate_element_has_getter(element):
+        # If the property has no comment, use the comment of the getter.
+        getter = interrogate_element_getter(element)
+        if interrogate_function_has_comment(getter):
+            print(block_comment(interrogate_function_comment(getter)), file=handle)
+
+    if interrogate_element_is_mapping(element) or \
+       interrogate_element_is_sequence(element):
+        suffix = "[]"
+    else:
+        suffix = ""
 
 
     print(translated_type_name(interrogate_element_type(element)), end=' ', file=handle)
     print(translated_type_name(interrogate_element_type(element)), end=' ', file=handle)
-    print(interrogate_element_name(element) + ';', file=handle)
+    print(interrogate_element_name(element) + suffix + ';', file=handle)
+
 
 
 def processFunction(handle, function, isConstructor = False):
 def processFunction(handle, function, isConstructor = False):
     for i_wrapper in range(interrogate_function_number_of_python_wrappers(function)):
     for i_wrapper in range(interrogate_function_number_of_python_wrappers(function)):
@@ -195,6 +202,7 @@ def processFunction(handle, function, isConstructor = False):
 
 
         print(");", file=handle)
         print(");", file=handle)
 
 
+
 def processType(handle, type):
 def processType(handle, type):
     typename = translated_type_name(type, scoped=False)
     typename = translated_type_name(type, scoped=False)
     derivations = [ translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type)) ]
     derivations = [ translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type)) ]
@@ -211,8 +219,10 @@ def processType(handle, type):
             print(interrogate_type_enum_value_name(type, i_value), "=", interrogate_type_enum_value(type, i_value), ",", file=handle)
             print(interrogate_type_enum_value_name(type, i_value), "=", interrogate_type_enum_value(type, i_value), ",", file=handle)
 
 
     elif interrogate_type_is_typedef(type):
     elif interrogate_type_is_typedef(type):
-        wrapped_type = translated_type_name(interrogate_type_wrapped_type(type))
-        print("typedef %s %s;" % (wrapped_type, typename), file=handle)
+        wrapped_type = interrogate_type_wrapped_type(type)
+        if interrogate_type_is_global(wrapped_type):
+            wrapped_type_name = translated_type_name(wrapped_type)
+            print("typedef %s %s;" % (wrapped_type_name, typename), file=handle)
         return
         return
     else:
     else:
         if interrogate_type_is_struct(type):
         if interrogate_type_is_struct(type):
@@ -249,6 +259,7 @@ def processType(handle, type):
     print("};", file=handle)
     print("};", file=handle)
 
 
 def processModule(handle, package):
 def processModule(handle, package):
+    print("Processing module %s" % (package))
     print("namespace %s {" % package, file=handle)
     print("namespace %s {" % package, file=handle)
 
 
     if package != "core":
     if package != "core":
@@ -280,22 +291,39 @@ def processModule(handle, package):
 if __name__ == "__main__":
 if __name__ == "__main__":
     handle = open("pandadoc.hpp", "w")
     handle = open("pandadoc.hpp", "w")
 
 
+    mainpage = MAINPAGE.strip()
+    if mainpage:
+        print("/**\n * " + mainpage.replace('\n', '\n * ') + '\n */', file=handle)
+
     print(comment("Panda3D modules that are implemented in C++."), file=handle)
     print(comment("Panda3D modules that are implemented in C++."), file=handle)
     print("namespace panda3d {", file=handle)
     print("namespace panda3d {", file=handle)
 
 
     # Determine the path to the interrogatedb files
     # Determine the path to the interrogatedb files
-    interrogate_add_search_directory(os.path.join(os.path.dirname(pandac.__file__), "..", "..", "etc"))
-    interrogate_add_search_directory(os.path.join(os.path.dirname(pandac.__file__), "input"))
+    pandac = os.path.dirname(pandac.__file__)
+    interrogate_add_search_directory(os.path.join(pandac, "..", "..", "etc"))
+    interrogate_add_search_directory(os.path.join(pandac, "input"))
 
 
     import panda3d.core
     import panda3d.core
     processModule(handle, "core")
     processModule(handle, "core")
 
 
+    # Determine the suffix for the extension modules.
+    if sys.version_info >= (3, 0):
+        import _imp
+        ext_suffix = _imp.extension_suffixes()[0]
+    elif sys.platform == "win32":
+        ext_suffix = ".pyd"
+    else:
+        ext_suffix = ".so"
+
     for lib in os.listdir(os.path.dirname(panda3d.__file__)):
     for lib in os.listdir(os.path.dirname(panda3d.__file__)):
-        if lib.endswith(('.pyd', '.so')) and not lib.startswith('core.'):
-            module_name = os.path.splitext(lib)[0]
+        if lib.endswith(ext_suffix) and not lib.startswith('core.'):
+            module_name = lib[:-len(ext_suffix)]
             __import__("panda3d." + module_name)
             __import__("panda3d." + module_name)
             processModule(handle, module_name)
             processModule(handle, module_name)
 
 
-
     print("}", file=handle)
     print("}", file=handle)
     handle.close()
     handle.close()
+
+    print("Wrote output to pandadoc.hpp.  You can now run:")
+    print()
+    print("  doxygen built/direct/directscripts/Doxyfile.python")

+ 1 - 1
direct/src/distributed/cConnectionRepository.cxx

@@ -891,7 +891,7 @@ describe_message(std::ostream &out, const string &prefix,
                  const Datagram &dg) const {
                  const Datagram &dg) const {
   DCPacker packer;
   DCPacker packer;
 
 
-  packer.set_unpack_data(dg.get_message());
+  packer.set_unpack_data((const char *)dg.get_data(), dg.get_length(), false);
   CHANNEL_TYPE do_id;
   CHANNEL_TYPE do_id;
   int msg_type;
   int msg_type;
   bool is_update = false;
   bool is_update = false;

+ 0 - 10
direct/src/stdpy/thread.py

@@ -277,13 +277,6 @@ class _local(object):
         d = _get_thread_locals(core.Thread.getCurrentThread(), id(self))
         d = _get_thread_locals(core.Thread.getCurrentThread(), id(self))
         d[key] = value
         d[key] = value
 
 
-##     def __getattr__(self, key):
-##         d = _get_thread_locals(core.Thread.getCurrentThread(), id(self))
-##         try:
-##             return d[key]
-##         except KeyError:
-##             raise AttributeError
-
     def __getattribute__(self, key):
     def __getattribute__(self, key):
         d = _get_thread_locals(core.Thread.getCurrentThread(), id(self))
         d = _get_thread_locals(core.Thread.getCurrentThread(), id(self))
         if key == '__dict__':
         if key == '__dict__':
@@ -292,6 +285,3 @@ class _local(object):
             return d[key]
             return d[key]
         except KeyError:
         except KeyError:
             return object.__getattribute__(self, key)
             return object.__getattribute__(self, key)
-
-
-

文件差异内容过多而无法显示
+ 155 - 125
dtool/metalibs/dtoolconfig/pydtool.cxx


+ 58 - 2
dtool/src/cppparser/cppExpression.cxx

@@ -25,6 +25,7 @@
 #include "cppFunctionGroup.h"
 #include "cppFunctionGroup.h"
 #include "cppFunctionType.h"
 #include "cppFunctionType.h"
 #include "cppClosureType.h"
 #include "cppClosureType.h"
+#include "cppReferenceType.h"
 #include "cppStructType.h"
 #include "cppStructType.h"
 #include "cppBison.h"
 #include "cppBison.h"
 #include "pdtoa.h"
 #include "pdtoa.h"
@@ -260,12 +261,12 @@ CPPExpression(CPPIdentifier *ident, CPPScope *current_scope,
       _u._variable = inst;
       _u._variable = inst;
       return;
       return;
     }
     }
-    CPPFunctionGroup *fgroup = decl->as_function_group();
+    /*CPPFunctionGroup *fgroup = decl->as_function_group();
     if (fgroup != nullptr) {
     if (fgroup != nullptr) {
       _type = T_function;
       _type = T_function;
       _u._fgroup = fgroup;
       _u._fgroup = fgroup;
       return;
       return;
-    }
+    }*/
   }
   }
 
 
   _type = T_unknown_ident;
   _type = T_unknown_ident;
@@ -1232,6 +1233,61 @@ determine_type() const {
   return nullptr;  // Compiler kludge; can't get here.
   return nullptr;  // Compiler kludge; can't get here.
 }
 }
 
 
+/**
+ * Returns true if this is an lvalue expression.
+ */
+bool CPPExpression::
+is_lvalue() const {
+  switch (_type) {
+  case T_variable:
+  case T_function:
+  case T_unknown_ident:
+    return true;
+
+  case T_typecast:
+  case T_static_cast:
+  case T_dynamic_cast:
+  case T_const_cast:
+  case T_reinterpret_cast:
+    {
+      CPPReferenceType *ref_type = _u._typecast._to->as_reference_type();
+      return ref_type != nullptr && ref_type->_value_category == CPPReferenceType::VC_lvalue;
+    }
+
+  case T_unary_operation:
+    if (_u._op._operator == 'f') {
+      // A function returning an lvalue reference.
+      CPPType *return_type = determine_type();
+      if (return_type != nullptr) {
+        CPPReferenceType *ref_type = return_type->as_reference_type();
+        return ref_type != nullptr && ref_type->_value_category == CPPReferenceType::VC_lvalue;
+      }
+    }
+    return _u._op._operator == PLUSPLUS
+        || _u._op._operator == MINUSMINUS
+        || _u._op._operator == '*';
+
+  case T_binary_operation:
+    if (_u._op._operator == ',') {
+      CPPReferenceType *ref_type = _u._op._op2->as_reference_type();
+      return ref_type != nullptr && ref_type->_value_category == CPPReferenceType::VC_lvalue;
+    }
+    return (_u._op._operator == POINTSAT || _u._op._operator == ',');
+
+  case T_trinary_operation:
+    return _u._op._op2->is_lvalue() && _u._op._op3->is_lvalue();
+
+  case T_literal:
+  case T_raw_literal:
+    return true;
+
+  default:
+    break;
+  }
+
+  return false;
+}
+
 /**
 /**
  * Returns true if this declaration is an actual, factual declaration, or
  * Returns true if this declaration is an actual, factual declaration, or
  * false if some part of the declaration depends on a template parameter which
  * false if some part of the declaration depends on a template parameter which

+ 1 - 0
dtool/src/cppparser/cppExpression.h

@@ -133,6 +133,7 @@ public:
 
 
   Result evaluate() const;
   Result evaluate() const;
   CPPType *determine_type() const;
   CPPType *determine_type() const;
+  bool is_lvalue() const;
   bool is_tbd() const;
   bool is_tbd() const;
 
 
   virtual bool is_fully_specified() const;
   virtual bool is_fully_specified() const;

+ 161 - 161
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -4078,13 +4078,9 @@ write_coerce_constructor(ostream &out, Object *obj, bool is_const) {
 
 
 /**
 /**
  * Special case optimization: if the last map is a subset of the map before
  * Special case optimization: if the last map is a subset of the map before
- * it, and the last parameter is only a simple parameter type (that we have
- * special default argument handling for), we can merge the cases.  When this
- * happens, we can make use of a special feature of PyArg_ParseTuple for
- * handling of these last few default arguments.  This doesn't work well for
- * all types of default expressions, though, hence the need for this elaborate
- * checking mechanism down here, which goes in parallel with the actual
- * optional arg handling logic in write_function_instance.
+ * it, we can merge the cases.  When this happens, we can make use of a
+ * special feature of PyArg_ParseTuple for handling of these last few default
+ * arguments.
  *
  *
  * This isn't just to help reduce the amount of generated code; it also
  * This isn't just to help reduce the amount of generated code; it also
  * enables arbitrary selection of keyword arguments for many functions, ie.
  * enables arbitrary selection of keyword arguments for many functions, ie.
@@ -4095,14 +4091,6 @@ write_coerce_constructor(ostream &out, Object *obj, bool is_const) {
  * Thanks to this mechanism, we can call it like so:
  * Thanks to this mechanism, we can call it like so:
  *
  *
  * func(c=True, d=".")
  * func(c=True, d=".")
- *
- * The return value is the minimum of the number of maximum arguments.
- *
- * Sorry, let me try that again: it returns the largest number of arguments
- * for which the overloads will be separated out rather than handled via the
- * special default handling mechanism.  Or something.
- *
- * Please don't hate me.
  */
  */
 int InterfaceMakerPythonNative::
 int InterfaceMakerPythonNative::
 collapse_default_remaps(std::map<int, std::set<FunctionRemap *> > &map_sets,
 collapse_default_remaps(std::map<int, std::set<FunctionRemap *> > &map_sets,
@@ -4118,70 +4106,8 @@ collapse_default_remaps(std::map<int, std::set<FunctionRemap *> > &map_sets,
     if (std::includes(rmi_next->second.begin(), rmi_next->second.end(),
     if (std::includes(rmi_next->second.begin(), rmi_next->second.end(),
                       rmi->second.begin(), rmi->second.end())) {
                       rmi->second.begin(), rmi->second.end())) {
 
 
-      // Check if the nth argument is something we can easily create a default
-      // for.
-      std::set<FunctionRemap *>::iterator sii;
-      for (sii = rmi->second.begin(); sii != rmi->second.end(); ++sii) {
-        FunctionRemap *remap = (*sii);
-        size_t pn = (size_t)rmi->first;
-        if (!remap->_has_this || remap->_type == FunctionRemap::T_constructor) {
-          --pn;
-        }
-        nassertd(pn < remap->_parameters.size()) goto abort_iteration;
-
-        ParameterRemap *param = remap->_parameters[pn]._remap;
-        CPPType *type = param->get_new_type();
-
-        if (param->new_type_is_atomic_string()) {
-          CPPType *orig_type = param->get_orig_type();
-          if (TypeManager::is_char_pointer(orig_type)) {
-          } else if (TypeManager::is_wchar_pointer(orig_type)) {
-            goto abort_iteration;
-          } else if (TypeManager::is_wstring(orig_type)) {
-            goto abort_iteration;
-          } else if (TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) {
-            goto abort_iteration;
-          } else {
-            // Regular strings are OK if the default argument is a string
-            // literal or the default string constructor, since those are
-            // trivial to handle.  This actually covers almost all of the
-            // cases of default string args.
-            CPPExpression::Type expr_type = param->get_default_value()->_type;
-            if (expr_type != CPPExpression::T_default_construct &&
-                expr_type != CPPExpression::T_string) {
-              goto abort_iteration;
-            }
-          }
-        } else if (TypeManager::is_integer(type)) {
-        } else if (TypeManager::is_float(type)) {
-        } else if (TypeManager::is_const_char_pointer(type)) {
-        } else if (TypeManager::is_pointer_to_PyTypeObject(type)) {
-        } else if (TypeManager::is_pointer_to_PyStringObject(type)) {
-        } else if (TypeManager::is_pointer_to_PyUnicodeObject(type)) {
-        } else if (TypeManager::is_pointer_to_PyObject(type)) {
-        } else if (TypeManager::is_pointer_to_Py_buffer(type)) {
-          goto abort_iteration;
-        } else if (TypeManager::is_pointer_to_simple(type)) {
-          goto abort_iteration;
-        } else if (TypeManager::is_pointer(type)) {
-          // I'm allowing other pointer types, but only if the expression
-          // happens to evaluate to a numeric constant (which will likely only
-          // be NULL). There are too many issues to resolve right now with
-          // allowing more complex default expressions, including issues in
-          // the C++ parser (but the reader is welcome to give it a try!)
-          CPPExpression::Result res = param->get_default_value()->evaluate();
-          if (res._type != CPPExpression::RT_integer &&
-              res._type != CPPExpression::RT_pointer) {
-            goto abort_iteration;
-          }
-        } else {
-          goto abort_iteration;
-        }
-      }
-
       // rmi_next has a superset of the remaps in rmi, and we are going to
       // rmi_next has a superset of the remaps in rmi, and we are going to
-      // erase rmi_next, so put all the remaps in rmi.  rmi->second =
-      // rmi_next->second;
+      // erase rmi_next, so put all the remaps in rmi.
 
 
       max_required_args = rmi_next->first;
       max_required_args = rmi_next->first;
       rmi = rmi_next;
       rmi = rmi_next;
@@ -4191,7 +4117,6 @@ collapse_default_remaps(std::map<int, std::set<FunctionRemap *> > &map_sets,
     }
     }
   }
   }
 
 
-abort_iteration:
   // Now erase the other remap sets.  Reverse iterators are weird, we first
   // Now erase the other remap sets.  Reverse iterators are weird, we first
   // need to get forward iterators and decrement them by one.
   // need to get forward iterators and decrement them by one.
   std::map<int, std::set<FunctionRemap *> >::iterator erase_begin, erase_end;
   std::map<int, std::set<FunctionRemap *> >::iterator erase_begin, erase_end;
@@ -4738,6 +4663,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       "(" + orig_type->get_local_name(&parser) + ")" + param_name;
       "(" + orig_type->get_local_name(&parser) + ")" + param_name;
 
 
     string default_expr;
     string default_expr;
+    const char *null_assign = "";
 
 
     if (is_optional) {
     if (is_optional) {
       // If this is an optional argument, PyArg_ParseTuple will leave the
       // If this is an optional argument, PyArg_ParseTuple will leave the
@@ -4747,6 +4673,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       default_expr_str << " = ";
       default_expr_str << " = ";
       default_value->output(default_expr_str, 0, &parser, false);
       default_value->output(default_expr_str, 0, &parser, false);
       default_expr = default_expr_str.str();
       default_expr = default_expr_str.str();
+      null_assign = " = nullptr";
 
 
       // We should only ever have to consider optional arguments for functions
       // We should only ever have to consider optional arguments for functions
       // taking a variable number of arguments.
       // taking a variable number of arguments.
@@ -4776,23 +4703,40 @@ write_function_instance(ostream &out, FunctionRemap *remap,
         expected_params += "str";
         expected_params += "str";
 
 
       } else if (TypeManager::is_wchar_pointer(orig_type)) {
       } else if (TypeManager::is_wchar_pointer(orig_type)) {
-        indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n";
-        indent(out, indent_level) << "PyObject *" << param_name << ";\n";
-        indent(out, indent_level) << "#else\n";
-        indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
-        indent(out, indent_level) << "#endif\n";
+        out << "#if PY_VERSION_HEX >= 0x03020000\n";
+        indent(out, indent_level) << "PyObject *" << param_name << null_assign << ";\n";
+        out << "#else\n";
+        indent(out, indent_level) << "PyUnicodeObject *" << param_name << null_assign << ";\n";
+        out << "#endif\n";
         format_specifiers += "U";
         format_specifiers += "U";
         parameter_list += ", &" + param_name;
         parameter_list += ", &" + param_name;
 
 
-        extra_convert
-          << "#if PY_VERSION_HEX >= 0x03030000\n"
-          << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString(" << param_name << ", nullptr);\n"
-          << "#else"
-          << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n"
-          << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n"
-          << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n"
-          << param_name << "_str[" << param_name << "_len] = 0;\n"
-          << "#endif\n";
+        if (is_optional) {
+          extra_convert
+            << "wchar_t *" << param_name << "_str;\n"
+            << "if (" << param_name << " != nullptr) {\n"
+            << "#if PY_VERSION_HEX >= 0x03030000\n"
+            << "  " << param_name << "_str = PyUnicode_AsWideCharString(" << param_name << ", nullptr);\n"
+            << "#else"
+            << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n"
+            << "  " << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n"
+            << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n"
+            << param_name << "_str[" << param_name << "_len] = 0;\n"
+            << "#endif\n"
+            << "} else {\n"
+            << "  " << param_name << "_str" << default_expr << ";\n"
+            << "}\n";
+        } else {
+          extra_convert
+            << "#if PY_VERSION_HEX >= 0x03030000\n"
+            << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString(" << param_name << ", nullptr);\n"
+            << "#else"
+            << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n"
+            << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n"
+            << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n"
+            << param_name << "_str[" << param_name << "_len] = 0;\n"
+            << "#endif\n";
+        }
 
 
         pexpr_string = param_name + "_str";
         pexpr_string = param_name + "_str";
 
 
@@ -4803,56 +4747,48 @@ write_function_instance(ostream &out, FunctionRemap *remap,
 
 
         expected_params += "unicode";
         expected_params += "unicode";
 
 
-      } else if (TypeManager::is_wstring(orig_type)) {
-        indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n";
-        indent(out, indent_level) << "PyObject *" << param_name << ";\n";
-        indent(out, indent_level) << "#else\n";
-        indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
-        indent(out, indent_level) << "#endif\n";
-        format_specifiers += "U";
-        parameter_list += ", &" + param_name;
-
-        extra_convert
-          << "#if PY_VERSION_HEX >= 0x03030000\n"
-          << "Py_ssize_t " << param_name << "_len;\n"
-          << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString("
-          << param_name << ", &" << param_name << "_len);\n"
-          << "#else\n"
-          << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n"
-          << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n"
-          << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n"
-          << "#endif\n";
-
-        pexpr_string = param_name + "_str, " + param_name + "_len";
-
-        extra_cleanup
-          << "#if PY_VERSION_HEX >= 0x03030000\n"
-          << "PyMem_Free(" << param_name << "_str);\n"
-          << "#endif\n";
-
-        expected_params += "unicode";
-
-      } else if (TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) {
-        indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n";
-        indent(out, indent_level) << "PyObject *" << param_name << ";\n";
-        indent(out, indent_level) << "#else\n";
-        indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
-        indent(out, indent_level) << "#endif\n";
+      } else if (TypeManager::is_wstring(orig_type) ||
+                 TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) {
+        out << "#if PY_VERSION_HEX >= 0x03020000\n";
+        indent(out, indent_level) << "PyObject *" << param_name << null_assign << ";\n";
+        out << "#else\n";
+        indent(out, indent_level) << "PyUnicodeObject *" << param_name << null_assign << ";\n";
+        out << "#endif\n";
         format_specifiers += "U";
         format_specifiers += "U";
         parameter_list += ", &" + param_name;
         parameter_list += ", &" + param_name;
 
 
-        extra_convert
-          << "#if PY_VERSION_HEX >= 0x03030000\n"
-          << "Py_ssize_t " << param_name << "_len;\n"
-          << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString("
-          << param_name << ", &" << param_name << "_len);\n"
-          << "#else\n"
-          << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n"
-          << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n"
-          << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n"
-          << "#endif\n";
-
-        pexpr_string = param_name + "_str, " + param_name + "_len";
+        if (is_optional) {
+          extra_convert
+            << "Py_ssize_t " << param_name << "_len;\n"
+            << "wchar_t *" << param_name << "_str;\n"
+            << "std::wstring " << param_name << "_wstr;\n"
+            << "if (" << param_name << " != nullptr) {\n"
+            << "#if PY_VERSION_HEX >= 0x03030000\n"
+            << "  " << param_name << "_str = PyUnicode_AsWideCharString("
+            << param_name << ", &" << param_name << "_len);\n"
+            << "#else\n"
+            << "  " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n"
+            << "  " << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n"
+            << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n"
+            << "#endif\n"
+            << "  " << param_name << "_wstr.assign(" << param_name << "_str, " << param_name << "_len);\n"
+            << "} else {\n"
+            << "  " << param_name << "_wstr" << default_expr << ";\n"
+            << "}\n";
+          pexpr_string = "std::move(" + param_name + "_wstr)";
+        } else {
+          extra_convert
+            << "#if PY_VERSION_HEX >= 0x03030000\n"
+            << "Py_ssize_t " << param_name << "_len;\n"
+            << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString("
+            << param_name << ", &" << param_name << "_len);\n"
+            << "#else\n"
+            << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n"
+            << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n"
+            << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n"
+            << "#endif\n";
+          pexpr_string = param_name + "_str, " + param_name + "_len";
+        }
 
 
         extra_cleanup
         extra_cleanup
           << "#if PY_VERSION_HEX >= 0x03030000\n"
           << "#if PY_VERSION_HEX >= 0x03030000\n"
@@ -5019,11 +4955,11 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       only_pyobjects = false;
       only_pyobjects = false;
 
 
     } else if (TypeManager::is_wchar(type)) {
     } else if (TypeManager::is_wchar(type)) {
-      indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n";
+      out << "#if PY_VERSION_HEX >= 0x03020000\n";
       indent(out, indent_level) << "PyObject *" << param_name << ";\n";
       indent(out, indent_level) << "PyObject *" << param_name << ";\n";
-      indent(out, indent_level) << "#else\n";
+      out << "#else\n";
       indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
       indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
-      indent(out, indent_level) << "#endif\n";
+      out << "#endif\n";
       format_specifiers += "U";
       format_specifiers += "U";
       parameter_list += ", &" + param_name;
       parameter_list += ", &" + param_name;
 
 
@@ -5348,17 +5284,36 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       if (args_type == AT_single_arg) {
       if (args_type == AT_single_arg) {
         param_name = "arg";
         param_name = "arg";
       } else {
       } else {
-        indent(out, indent_level) << "PyObject *" << param_name << ";\n";
+        indent(out, indent_level) << "PyObject *" << param_name << null_assign << ";\n";
         format_specifiers += "O";
         format_specifiers += "O";
         parameter_list += ", &" + param_name;
         parameter_list += ", &" + param_name;
       }
       }
       indent(out, indent_level) << "Py_buffer " << param_name << "_view;\n";
       indent(out, indent_level) << "Py_buffer " << param_name << "_view;\n";
 
 
-      extra_param_check << " && PyObject_GetBuffer("
-                        << param_name << ", &"
-                        << param_name << "_view, PyBUF_FULL) == 0";
-      pexpr_string = "&" + param_name + "_view";
-      extra_cleanup << "PyBuffer_Release(&" << param_name << "_view);\n";
+      if (is_optional) {
+        indent(out, indent_level) << "Py_buffer *" << param_name << "_viewp;\n";
+
+        extra_convert
+          << "bool " << param_name << "_success;\n"
+          << "if (" << param_name << " != nullptr) {\n"
+          << "  " << param_name << "_success = (PyObject_GetBuffer("
+          << param_name << ", &" << param_name << "_view, PyBUF_FULL) == 0);\n"
+          << "  " << param_name << "_viewp = &" << param_name << "_view;\n"
+          << "} else {\n"
+          << "  " << param_name << "_viewp" << default_expr << ";\n"
+          << "  " << param_name << "_success = true;\n"
+          << "}\n";
+
+        extra_param_check << " && " << param_name << "_success";
+        pexpr_string = param_name + "_viewp";
+        extra_cleanup << "if (" << param_name << " != nullptr) PyBuffer_Release(&" << param_name << "_view);\n";
+      } else {
+        extra_param_check << " && PyObject_GetBuffer("
+                          << param_name << ", &"
+                          << param_name << "_view, PyBUF_FULL) == 0";
+        pexpr_string = "&" + param_name + "_view";
+        extra_cleanup << "PyBuffer_Release(&" << param_name << "_view);\n";
+      }
       expected_params += "buffer";
       expected_params += "buffer";
       may_raise_typeerror = true;
       may_raise_typeerror = true;
       clear_error = true;
       clear_error = true;
@@ -5367,7 +5322,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       if (args_type == AT_single_arg) {
       if (args_type == AT_single_arg) {
         param_name = "arg";
         param_name = "arg";
       } else {
       } else {
-        indent(out, indent_level) << "PyObject *" << param_name << ";\n";
+        indent(out, indent_level) << "PyObject *" << param_name << null_assign << ";\n";
         format_specifiers += "O";
         format_specifiers += "O";
         parameter_list += ", &" + param_name;
         parameter_list += ", &" + param_name;
       }
       }
@@ -5496,18 +5451,15 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       if (args_type == AT_single_arg) {
       if (args_type == AT_single_arg) {
         param_name = "arg";
         param_name = "arg";
       } else {
       } else {
-        indent(out, indent_level) << "PyObject *" << param_name;
-        if (is_optional) {
-          out << " = nullptr";
-        }
-        out << ";\n";
+        indent(out, indent_level) << "PyObject *" << param_name << null_assign << ";\n";
         format_specifiers += "O";
         format_specifiers += "O";
         parameter_list += ", &" + param_name;
         parameter_list += ", &" + param_name;
       }
       }
 
 
       // If the default value is NULL, we also accept a None value.
       // If the default value is NULL, we also accept a None value.
       bool maybe_none = false;
       bool maybe_none = false;
-      if (default_value != nullptr && (return_flags & RF_coerced) == 0) {
+      if (default_value != nullptr && (return_flags & RF_coerced) == 0 &&
+          TypeManager::is_pointer(orig_type)) {
         CPPExpression::Result res = param->get_default_value()->evaluate();
         CPPExpression::Result res = param->get_default_value()->evaluate();
         if (res._type == CPPExpression::RT_integer ||
         if (res._type == CPPExpression::RT_integer ||
             res._type == CPPExpression::RT_pointer) {
             res._type == CPPExpression::RT_pointer) {
@@ -5576,8 +5528,11 @@ write_function_instance(ostream &out, FunctionRemap *remap,
               << "if (" << param_name << " != nullptr && " << param_name << " != Py_None) {\n"
               << "if (" << param_name << " != nullptr && " << param_name << " != Py_None) {\n"
               << "  " << param_name << "_this";
               << "  " << param_name << "_this";
           } else if (is_optional) {
           } else if (is_optional) {
+            if (TypeManager::is_pointer(orig_type)) {
+              extra_convert << default_expr;
+            }
             extra_convert
             extra_convert
-              << default_expr << ";\n"
+              << ";\n"
               << "if (" << param_name << " != nullptr) {\n"
               << "if (" << param_name << " != nullptr) {\n"
               << "  " << param_name << "_this";
               << "  " << param_name << "_this";
           } else if (maybe_none) {
           } else if (maybe_none) {
@@ -5590,7 +5545,13 @@ write_function_instance(ostream &out, FunctionRemap *remap,
           extra_convert << " = Dtool_Coerce_" + make_safe_name(class_name) +
           extra_convert << " = Dtool_Coerce_" + make_safe_name(class_name) +
             "(" + param_name + ", " + param_name + "_local);\n";
             "(" + param_name + ", " + param_name + "_local);\n";
 
 
-          if (is_optional || maybe_none) {
+          if (is_optional && !TypeManager::is_pointer(orig_type)) {
+            extra_convert
+              << "} else {\n"
+              << "  " << param_name << "_local" << default_expr << ";\n"
+              << "  " << param_name << "_this = &" << param_name << "_local;\n"
+              << "}\n";
+          } else if (is_optional || maybe_none) {
             extra_convert << "}\n";
             extra_convert << "}\n";
           }
           }
 
 
@@ -5645,21 +5606,60 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       } else { // The regular, non-coercion case.
       } else { // The regular, non-coercion case.
         type->output_instance(extra_convert, param_name + "_this", &parser);
         type->output_instance(extra_convert, param_name + "_this", &parser);
         if (is_optional && maybe_none) {
         if (is_optional && maybe_none) {
+          // This parameter has a default value of nullptr, so we need to also
+          // allow passing in None.
           extra_convert
           extra_convert
             << default_expr << ";\n"
             << default_expr << ";\n"
             << "if (" << param_name << " != nullptr && " << param_name << " != Py_None) {\n"
             << "if (" << param_name << " != nullptr && " << param_name << " != Py_None) {\n"
             << "  " << param_name << "_this";
             << "  " << param_name << "_this";
-        } else if (is_optional) {
+        }
+        else if (is_optional && !TypeManager::is_pointer(orig_type) && !default_value->is_lvalue()) {
+          // Most annoying case, where we have to use an rvalue reference to
+          // extend the lifetime of the default argument.  In this case, the
+          // default expression is invoked even if not used.
+          extra_convert << ";\n";
+          if (TypeManager::is_const_pointer_to_anything(type)) {
+            extra_convert << "const ";
+            obj_type->output_instance(extra_convert, "&" + param_name + "_ref", &parser);
+          } else {
+            obj_type->output_instance(extra_convert, "&&" + param_name + "_ref", &parser);
+          }
           extra_convert
           extra_convert
             << default_expr << ";\n"
             << default_expr << ";\n"
-            << "if (" << param_name << " != nullptr) {\n"
+            << "if (" << param_name << " == nullptr) {\n"
+            << "  " << param_name << "_this = &" << param_name << "_ref;\n"
+            << "} else {\n"
             << "  " << param_name << "_this";
             << "  " << param_name << "_this";
-        } else if (maybe_none) {
+        }
+        else if (is_optional) {
+          // General case where the default argument is either an lvalue or a
+          // pointer.
+          extra_convert
+            << ";\n"
+            << "if (" << param_name << " == nullptr) {\n"
+            << "  " << param_name << "_this = ";
+          if (TypeManager::is_pointer(orig_type)) {
+            default_value->output(extra_convert, 0, &parser, false);
+            extra_convert << ";\n";
+          } else {
+            // The rvalue case was handled above, so this is an lvalue, which
+            // means we can safely take a reference to it.
+            extra_convert << "&(";
+            default_value->output(extra_convert, 0, &parser, false);
+            extra_convert << ");\n";
+          }
+          extra_convert
+            << "} else {\n"
+            << "  " << param_name << "_this";
+        }
+        else if (maybe_none) {
+          // No default argument, but we still need to check for None.
           extra_convert
           extra_convert
             << " = nullptr;\n"
             << " = nullptr;\n"
             << "if (" << param_name << " != Py_None) {\n"
             << "if (" << param_name << " != Py_None) {\n"
             << "  " << param_name << "_this";
             << "  " << param_name << "_this";
         }
         }
+
         if (const_ok && !report_errors) {
         if (const_ok && !report_errors) {
           // This function does the same thing in this case and is slightly
           // This function does the same thing in this case and is slightly
           // simpler.  But maybe we should just reorganize these functions
           // simpler.  But maybe we should just reorganize these functions

+ 4 - 1
dtool/src/interrogate/interrogate.cxx

@@ -312,7 +312,10 @@ main(int argc, char **argv) {
   string command_line;
   string command_line;
   int i;
   int i;
   for (i = 0; i < argc; i++) {
   for (i = 0; i < argc; i++) {
-    command_line += string(argv[i]) + " ";
+    if (i > 0) {
+      command_line += ' ';
+    }
+    command_line += string(argv[i]);
   }
   }
 
 
   Filename fn;
   Filename fn;

+ 1 - 0
dtool/src/interrogate/interrogateBuilder.cxx

@@ -2024,6 +2024,7 @@ get_make_property(CPPMakeProperty *make_property, CPPStructType *struct_type, CP
     if (iproperty._type != 0 && iproperty._type != return_index) {
     if (iproperty._type != 0 && iproperty._type != return_index) {
       cerr << "Property " << property_name << " has inconsistent element type!\n";
       cerr << "Property " << property_name << " has inconsistent element type!\n";
     }
     }
+    iproperty._type = return_index;
   } else {
   } else {
     iproperty._type = 0;
     iproperty._type = 0;
   }
   }

+ 12 - 0
dtool/src/interrogatedb/interrogate_interface.cxx

@@ -176,6 +176,18 @@ interrogate_element_setter(ElementIndex element) {
   return InterrogateDatabase::get_ptr()->get_element(element).get_setter();
   return InterrogateDatabase::get_ptr()->get_element(element).get_setter();
 }
 }
 
 
+bool
+interrogate_element_is_sequence(ElementIndex element) {
+  // cerr << "interrogate_element_is_sequence(" << element << ")\n";
+  return InterrogateDatabase::get_ptr()->get_element(element).is_sequence();
+}
+
+bool
+interrogate_element_is_mapping(ElementIndex element) {
+  // cerr << "interrogate_element_is_mapping(" << element << ")\n";
+  return InterrogateDatabase::get_ptr()->get_element(element).is_mapping();
+}
+
 int
 int
 interrogate_number_of_globals() {
 interrogate_number_of_globals() {
   // cerr << "interrogate_number_of_globals()\n";
   // cerr << "interrogate_number_of_globals()\n";

+ 3 - 0
dtool/src/interrogatedb/interrogate_interface.h

@@ -153,6 +153,9 @@ EXPCL_INTERROGATEDB FunctionIndex interrogate_element_getter(ElementIndex elemen
 EXPCL_INTERROGATEDB bool interrogate_element_has_setter(ElementIndex element);
 EXPCL_INTERROGATEDB bool interrogate_element_has_setter(ElementIndex element);
 EXPCL_INTERROGATEDB FunctionIndex interrogate_element_setter(ElementIndex element);
 EXPCL_INTERROGATEDB FunctionIndex interrogate_element_setter(ElementIndex element);
 
 
+EXPCL_INTERROGATEDB bool interrogate_element_is_sequence(ElementIndex element);
+EXPCL_INTERROGATEDB bool interrogate_element_is_mapping(ElementIndex element);
+
 // Global Data
 // Global Data
 
 
 // This is the list of global data elements.
 // This is the list of global data elements.

+ 274 - 172
makepanda/installer.nsi

@@ -14,7 +14,7 @@
 ;
 ;
 ;   BUILT         - location of panda install tree.
 ;   BUILT         - location of panda install tree.
 ;   SOURCE        - location of the panda source-tree if available, OR location of panda install tree.
 ;   SOURCE        - location of the panda source-tree if available, OR location of panda install tree.
-;   PYVER         - version of Python that Panda was built with (ie, "2.7")
+;   INCLUDE_PYVER - version of Python that Panda was built with (eg. "2.7", "3.5-32")
 ;   REGVIEW       - either 32 or 64, depending on the build architecture.
 ;   REGVIEW       - either 32 or 64, depending on the build architecture.
 ;
 ;
 
 
@@ -46,7 +46,9 @@ SetCompressor ${COMPRESSOR}
 !insertmacro MUI_PAGE_LICENSE "${SOURCE}/doc/LICENSE"
 !insertmacro MUI_PAGE_LICENSE "${SOURCE}/doc/LICENSE"
 !insertmacro MUI_PAGE_DIRECTORY
 !insertmacro MUI_PAGE_DIRECTORY
 
 
+!ifdef INCLUDE_PYVER
 !define MUI_PAGE_CUSTOMFUNCTION_LEAVE ConfirmPythonSelection
 !define MUI_PAGE_CUSTOMFUNCTION_LEAVE ConfirmPythonSelection
+!endif
 !insertmacro MUI_PAGE_COMPONENTS
 !insertmacro MUI_PAGE_COMPONENTS
 
 
 !insertmacro MUI_PAGE_INSTFILES
 !insertmacro MUI_PAGE_INSTFILES
@@ -64,8 +66,9 @@ ShowUninstDetails hide
 
 
 LicenseData "${SOURCE}/doc/LICENSE"
 LicenseData "${SOURCE}/doc/LICENSE"
 
 
-InstType "Full (Recommended)"
-InstType "Minimal"
+InstType "Auto (Recommended)"
+InstType "Full"
+InstType "Light"
 
 
 LangString DESC_SecCore ${LANG_ENGLISH} "The Panda3D core libraries, configuration files and models/textures that are needed to use Panda3D."
 LangString DESC_SecCore ${LANG_ENGLISH} "The Panda3D core libraries, configuration files and models/textures that are needed to use Panda3D."
 LangString DESC_SecOpenGL ${LANG_ENGLISH} "The OpenGL graphics back-end is the most well-supported renderer."
 LangString DESC_SecOpenGL ${LANG_ENGLISH} "The OpenGL graphics back-end is the most well-supported renderer."
@@ -78,8 +81,9 @@ LangString DESC_SecODE ${LANG_ENGLISH} "Support for the Open Dynamics Engine to
 LangString DESC_SecPhysX ${LANG_ENGLISH} "Support for NVIDIA PhysX to implement physics."
 LangString DESC_SecPhysX ${LANG_ENGLISH} "Support for NVIDIA PhysX to implement physics."
 LangString DESC_SecRocket ${LANG_ENGLISH} "Support for the libRocket GUI library.  This is an optional library that offers an HTML/CSS-like approach to creating user interfaces."
 LangString DESC_SecRocket ${LANG_ENGLISH} "Support for the libRocket GUI library.  This is an optional library that offers an HTML/CSS-like approach to creating user interfaces."
 LangString DESC_SecTools ${LANG_ENGLISH} "Useful tools and model converters to help with Panda3D development.  Recommended."
 LangString DESC_SecTools ${LANG_ENGLISH} "Useful tools and model converters to help with Panda3D development.  Recommended."
-LangString DESC_SecPyBindings ${LANG_ENGLISH} "Contains the Python modules that allow use of Panda3D using Python.  These will only work with a ${REGVIEW}-bit version of Python ${PYVER}."
-LangString DESC_SecPython ${LANG_ENGLISH} "Contains a ${REGVIEW}-bit copy of Python ${PYVER} preconfigured to make use of Panda3D."
+LangString DESC_SecGroupPython ${LANG_ENGLISH} "Contains modules that provide Python support for Panda3D."
+LangString DESC_SecPyShared ${LANG_ENGLISH} "Contains the common Python code used by the Panda3D Python bindings."
+LangString DESC_SecPython ${LANG_ENGLISH} "Contains a ${REGVIEW}-bit copy of Python ${INCLUDE_PYVER} preconfigured to make use of Panda3D."
 LangString DESC_SecHeadersLibs ${LANG_ENGLISH} "Headers and libraries needed for C++ development with Panda3D."
 LangString DESC_SecHeadersLibs ${LANG_ENGLISH} "Headers and libraries needed for C++ development with Panda3D."
 LangString DESC_SecSamples ${LANG_ENGLISH} "The sample programs demonstrate how to make Python applications with Panda3D."
 LangString DESC_SecSamples ${LANG_ENGLISH} "The sample programs demonstrate how to make Python applications with Panda3D."
 LangString DESC_SecMaxPlugins ${LANG_ENGLISH} "Plug-ins for Autodesk 3ds Max (${REGVIEW}-bit) that can be used to export models to Panda3D."
 LangString DESC_SecMaxPlugins ${LANG_ENGLISH} "Plug-ins for Autodesk 3ds Max (${REGVIEW}-bit) that can be used to export models to Panda3D."
@@ -112,26 +116,109 @@ var MANPAGE
 !insertmacro !defineifexist HAVE_ODE "${BUILT}\bin\libpandaode.dll"
 !insertmacro !defineifexist HAVE_ODE "${BUILT}\bin\libpandaode.dll"
 !insertmacro !defineifexist HAVE_PHYSX "${BUILT}\bin\libpandaphysx.dll"
 !insertmacro !defineifexist HAVE_PHYSX "${BUILT}\bin\libpandaphysx.dll"
 !insertmacro !defineifexist HAVE_ROCKET "${BUILT}\bin\libp3rocket.dll"
 !insertmacro !defineifexist HAVE_ROCKET "${BUILT}\bin\libp3rocket.dll"
-!insertmacro !defineifexist HAVE_PYTHON "${BUILT}\python"
 !insertmacro !defineifexist HAVE_SAMPLES "${SOURCE}\samples"
 !insertmacro !defineifexist HAVE_SAMPLES "${SOURCE}\samples"
 !insertmacro !defineifexist HAVE_MAX_PLUGINS "${BUILT}\plugins\*.dlo"
 !insertmacro !defineifexist HAVE_MAX_PLUGINS "${BUILT}\plugins\*.dlo"
 !insertmacro !defineifexist HAVE_MAYA_PLUGINS "${BUILT}\plugins\*.mll"
 !insertmacro !defineifexist HAVE_MAYA_PLUGINS "${BUILT}\plugins\*.mll"
 
 
+!macro RemovePythonPath PYVER
+    ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D" ""
+    StrCmp $0 "$INSTDIR" 0 +2
+    DeleteRegKey HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D"
+!macroend
+
+!macro PyBindingSection PYVER EXT_SUFFIX
+    LangString DESC_SecPyBindings${PYVER} ${LANG_ENGLISH} "Contains the Python modules that allow use of Panda3D using a ${REGVIEW}-bit version of Python ${PYVER}."
+
+    !insertmacro !defineifexist _present "${BUILT}\panda3d\core${EXT_SUFFIX}"
+    !ifdef _present
+    Section "${PYVER} bindings" SecPyBindings${PYVER}
+        !if "${PYVER}" == "${INCLUDE_PYVER}"
+            SectionIn 1 2 3
+        !else
+            !if "${PYVER}" == "2.7"
+                SectionIn 1 2
+            !else
+                ; See .onInit function where this is dynamically enabled.
+                SectionIn 2
+            !endif
+        !endif
+
+        SetDetailsPrint both
+        DetailPrint "Installing Panda3D bindings for Python ${PYVER}..."
+        SetDetailsPrint listonly
+
+        SetOutPath $INSTDIR\panda3d
+
+        File /nonfatal /r "${BUILT}\panda3d\core${EXT_SUFFIX}"
+        File /nonfatal /r "${BUILT}\panda3d\ai${EXT_SUFFIX}"
+        File /nonfatal /r "${BUILT}\panda3d\direct${EXT_SUFFIX}"
+        File /nonfatal /r "${BUILT}\panda3d\egg${EXT_SUFFIX}"
+        File /nonfatal /r "${BUILT}\panda3d\fx${EXT_SUFFIX}"
+        File /nonfatal /r "${BUILT}\panda3d\interrogatedb${EXT_SUFFIX}"
+        File /nonfatal /r "${BUILT}\panda3d\physics${EXT_SUFFIX}"
+        File /nonfatal /r "${BUILT}\panda3d\_rplight${EXT_SUFFIX}"
+        File /nonfatal /r "${BUILT}\panda3d\skel${EXT_SUFFIX}"
+        File /nonfatal /r "${BUILT}\panda3d\vision${EXT_SUFFIX}"
+        File /nonfatal /r "${BUILT}\panda3d\vrpn${EXT_SUFFIX}"
+
+        !ifdef HAVE_BULLET
+            SectionGetFlags ${SecBullet} $R0
+            IntOp $R0 $R0 & ${SF_SELECTED}
+            StrCmp $R0 ${SF_SELECTED} 0 SkipBulletPyd
+            File /nonfatal /r "${BUILT}\panda3d\bullet${EXT_SUFFIX}"
+            SkipBulletPyd:
+        !endif
+
+        !ifdef HAVE_ODE
+            SectionGetFlags ${SecODE} $R0
+            IntOp $R0 $R0 & ${SF_SELECTED}
+            StrCmp $R0 ${SF_SELECTED} 0 SkipODEPyd
+            File /nonfatal /r "${BUILT}\panda3d\ode${EXT_SUFFIX}"
+            SkipODEPyd:
+        !endif
+
+        !ifdef HAVE_PHYSX
+            SectionGetFlags ${SecPhysX} $R0
+            IntOp $R0 $R0 & ${SF_SELECTED}
+            StrCmp $R0 ${SF_SELECTED} 0 SkipPhysXPyd
+            File /nonfatal /r "${BUILT}\panda3d\physx${EXT_SUFFIX}"
+            SkipPhysXPyd:
+        !endif
+
+        !ifdef HAVE_ROCKET
+            SectionGetFlags ${SecRocket} $R0
+            IntOp $R0 $R0 & ${SF_SELECTED}
+            StrCmp $R0 ${SF_SELECTED} 0 SkipRocketPyd
+            File /nonfatal /r "${BUILT}\panda3d\rocket${EXT_SUFFIX}"
+            SkipRocketPyd:
+        !endif
+
+        SetOutPath $INSTDIR\pandac\input
+        File /r "${BUILT}\pandac\input\*"
+        SetOutPath $INSTDIR\Pmw
+        File /nonfatal /r /x CVS "${BUILT}\Pmw\*"
+        SetOutPath $INSTDIR\panda3d.dist-info
+        File /nonfatal /r "${BUILT}\panda3d.dist-info\*"
+
+        !ifdef REGVIEW
+        SetRegView ${REGVIEW}
+        !endif
+
+        ; Install a Panda3D path into the global PythonPath for this version
+        ; of Python.
+        WriteRegStr HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D" "" "$INSTDIR"
+    SectionEnd
+    !undef _present
+    !endif
+!macroend
+
 Function runFunction
 Function runFunction
     ExecShell "open" "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk"
     ExecShell "open" "$SMPROGRAMS\${TITLE}\Panda3D Manual.lnk"
 FunctionEnd
 FunctionEnd
 
 
-Function .onInit
-    ${If} ${REGVIEW} = 64
-    ${AndIfNot} ${RunningX64}
-        MessageBox MB_OK|MB_ICONEXCLAMATION "You are attempting to install the 64-bit version of Panda3D on a 32-bit version of Windows.  Please download and install the 32-bit version of Panda3D instead."
-        Abort
-    ${EndIf}
-FunctionEnd
-
 SectionGroup "Panda3D Libraries"
 SectionGroup "Panda3D Libraries"
     Section "Core Libraries" SecCore
     Section "Core Libraries" SecCore
-        SectionIn 1 2 RO
+        SectionIn 1 2 3 RO
 
 
         SetShellVarContext current
         SetShellVarContext current
         SetOverwrite try
         SetOverwrite try
@@ -177,7 +264,7 @@ SectionGroup "Panda3D Libraries"
 
 
     !ifdef HAVE_GL
     !ifdef HAVE_GL
     Section "OpenGL" SecOpenGL
     Section "OpenGL" SecOpenGL
-        SectionIn 1 2 RO
+        SectionIn 1 2 3 RO
 
 
         SetOutPath "$INSTDIR\bin"
         SetOutPath "$INSTDIR\bin"
         File "${BUILT}\bin\libpandagl.dll"
         File "${BUILT}\bin\libpandagl.dll"
@@ -186,7 +273,7 @@ SectionGroup "Panda3D Libraries"
 
 
     !ifdef HAVE_DX9
     !ifdef HAVE_DX9
     Section "Direct3D 9" SecDirect3D9
     Section "Direct3D 9" SecDirect3D9
-        SectionIn 1
+        SectionIn 1 2
 
 
         SetOutPath "$INSTDIR\bin"
         SetOutPath "$INSTDIR\bin"
         File "${BUILT}\bin\libpandadx9.dll"
         File "${BUILT}\bin\libpandadx9.dll"
@@ -196,7 +283,7 @@ SectionGroup "Panda3D Libraries"
 
 
     !ifdef HAVE_OPENAL
     !ifdef HAVE_OPENAL
     Section "OpenAL Audio" SecOpenAL
     Section "OpenAL Audio" SecOpenAL
-        SectionIn 1 2
+        SectionIn 1 2 3
 
 
         SetOutPath "$INSTDIR\bin"
         SetOutPath "$INSTDIR\bin"
         File "${BUILT}\bin\libp3openal_audio.dll"
         File "${BUILT}\bin\libp3openal_audio.dll"
@@ -207,7 +294,7 @@ SectionGroup "Panda3D Libraries"
 
 
     !ifdef HAVE_FMOD
     !ifdef HAVE_FMOD
     Section "FMOD Audio" SecFMOD
     Section "FMOD Audio" SecFMOD
-        SectionIn 1
+        SectionIn 1 2
 
 
         SetOutPath "$INSTDIR\bin"
         SetOutPath "$INSTDIR\bin"
         File "${BUILT}\bin\libp3fmod_audio.dll"
         File "${BUILT}\bin\libp3fmod_audio.dll"
@@ -217,7 +304,7 @@ SectionGroup "Panda3D Libraries"
 
 
     !ifdef HAVE_FFMPEG
     !ifdef HAVE_FFMPEG
     Section "FFMpeg" SecFFMpeg
     Section "FFMpeg" SecFFMpeg
-        SectionIn 1
+        SectionIn 1 2
 
 
         SetOutPath "$INSTDIR\bin"
         SetOutPath "$INSTDIR\bin"
         File "${BUILT}\bin\libp3ffmpeg.dll"
         File "${BUILT}\bin\libp3ffmpeg.dll"
@@ -230,7 +317,7 @@ SectionGroup "Panda3D Libraries"
 
 
     !ifdef HAVE_BULLET
     !ifdef HAVE_BULLET
     Section "Bullet Physics" SecBullet
     Section "Bullet Physics" SecBullet
-        SectionIn 1
+        SectionIn 1 2
 
 
         SetOutPath "$INSTDIR\bin"
         SetOutPath "$INSTDIR\bin"
         File "${BUILT}\bin\libpandabullet.dll"
         File "${BUILT}\bin\libpandabullet.dll"
@@ -239,7 +326,7 @@ SectionGroup "Panda3D Libraries"
 
 
     !ifdef HAVE_ODE
     !ifdef HAVE_ODE
     Section "ODE Physics" SecODE
     Section "ODE Physics" SecODE
-        SectionIn 1
+        SectionIn 1 2
 
 
         SetOutPath "$INSTDIR\bin"
         SetOutPath "$INSTDIR\bin"
         File "${BUILT}\bin\libpandaode.dll"
         File "${BUILT}\bin\libpandaode.dll"
@@ -248,7 +335,8 @@ SectionGroup "Panda3D Libraries"
 
 
     !ifdef HAVE_PHYSX
     !ifdef HAVE_PHYSX
     Section "NVIDIA PhysX" SecPhysX
     Section "NVIDIA PhysX" SecPhysX
-        SectionIn 1
+        ; Only enable in "Full"
+        SectionIn 2
 
 
         SetOutPath "$INSTDIR\bin"
         SetOutPath "$INSTDIR\bin"
         File "${BUILT}\bin\libpandaphysx.dll"
         File "${BUILT}\bin\libpandaphysx.dll"
@@ -260,7 +348,7 @@ SectionGroup "Panda3D Libraries"
 
 
     !ifdef HAVE_ROCKET
     !ifdef HAVE_ROCKET
     Section "libRocket GUI" SecRocket
     Section "libRocket GUI" SecRocket
-        SectionIn 1
+        SectionIn 1 2
 
 
         SetOutPath "$INSTDIR\bin"
         SetOutPath "$INSTDIR\bin"
         File "${BUILT}\bin\libp3rocket.dll"
         File "${BUILT}\bin\libp3rocket.dll"
@@ -272,7 +360,7 @@ SectionGroup "Panda3D Libraries"
 SectionGroupEnd
 SectionGroupEnd
 
 
 Section "Tools and utilities" SecTools
 Section "Tools and utilities" SecTools
-    SectionIn 1 2
+    SectionIn 1 2 3
 
 
     SetDetailsPrint both
     SetDetailsPrint both
     DetailPrint "Installing utilities..."
     DetailPrint "Installing utilities..."
@@ -305,17 +393,14 @@ Section "Tools and utilities" SecTools
     WriteRegStr HKCU "Software\Classes\Panda3D.Multifile\shell\extract\command" "" '"$INSTDIR\bin\multify.exe" -xf "%1"'
     WriteRegStr HKCU "Software\Classes\Panda3D.Multifile\shell\extract\command" "" '"$INSTDIR\bin\multify.exe" -xf "%1"'
 SectionEnd
 SectionEnd
 
 
-SectionGroup "Python support"
-    Section "Python bindings" SecPyBindings
-        SectionIn 1 2
+SectionGroup "Python modules" SecGroupPython
+    Section "Shared code" SecPyShared
+        SectionIn 1 2 3
 
 
         SetDetailsPrint both
         SetDetailsPrint both
-        DetailPrint "Installing Panda3D Python modules..."
+        DetailPrint "Installing Panda3D shared Python modules..."
         SetDetailsPrint listonly
         SetDetailsPrint listonly
 
 
-        SetOutPath "$INSTDIR\bin"
-        File /nonfatal /r "${BUILT}\bin\*.pyd"
-
         SetOutPath $INSTDIR\direct\directscripts
         SetOutPath $INSTDIR\direct\directscripts
         File /r /x CVS /x Opt?-Win32 "${BUILT}\direct\directscripts\*"
         File /r /x CVS /x Opt?-Win32 "${BUILT}\direct\directscripts\*"
         SetOutPath $INSTDIR\direct
         SetOutPath $INSTDIR\direct
@@ -328,143 +413,133 @@ SectionGroup "Python support"
         File /r "${BUILT}\pandac\*.py"
         File /r "${BUILT}\pandac\*.py"
         SetOutPath $INSTDIR\panda3d
         SetOutPath $INSTDIR\panda3d
         File /r "${BUILT}\panda3d\*.py"
         File /r "${BUILT}\panda3d\*.py"
+    SectionEnd
 
 
-        File /nonfatal /r "${BUILT}\panda3d\core${EXT_SUFFIX}"
-        File /nonfatal /r "${BUILT}\panda3d\ai${EXT_SUFFIX}"
-        File /nonfatal /r "${BUILT}\panda3d\direct${EXT_SUFFIX}"
-        File /nonfatal /r "${BUILT}\panda3d\egg${EXT_SUFFIX}"
-        File /nonfatal /r "${BUILT}\panda3d\fx${EXT_SUFFIX}"
-        File /nonfatal /r "${BUILT}\panda3d\interrogatedb${EXT_SUFFIX}"
-        File /nonfatal /r "${BUILT}\panda3d\physics${EXT_SUFFIX}"
-        File /nonfatal /r "${BUILT}\panda3d\_rplight${EXT_SUFFIX}"
-        File /nonfatal /r "${BUILT}\panda3d\skel${EXT_SUFFIX}"
-        File /nonfatal /r "${BUILT}\panda3d\vision${EXT_SUFFIX}"
-        File /nonfatal /r "${BUILT}\panda3d\vrpn${EXT_SUFFIX}"
-
-        !ifdef HAVE_BULLET
-            SectionGetFlags ${SecBullet} $R0
-            IntOp $R0 $R0 & ${SF_SELECTED}
-            StrCmp $R0 ${SF_SELECTED} 0 SkipBulletPyd
-            File /nonfatal /r "${BUILT}\panda3d\bullet${EXT_SUFFIX}"
-            SkipBulletPyd:
-        !endif
-
-        !ifdef HAVE_ODE
-            SectionGetFlags ${SecODE} $R0
-            IntOp $R0 $R0 & ${SF_SELECTED}
-            StrCmp $R0 ${SF_SELECTED} 0 SkipODEPyd
-            File /nonfatal /r "${BUILT}\panda3d\ode${EXT_SUFFIX}"
-            SkipODEPyd:
-        !endif
-
-        !ifdef HAVE_PHYSX
-            SectionGetFlags ${SecPhysX} $R0
-            IntOp $R0 $R0 & ${SF_SELECTED}
-            StrCmp $R0 ${SF_SELECTED} 0 SkipPhysXPyd
-            File /nonfatal /r "${BUILT}\panda3d\physx${EXT_SUFFIX}"
-            SkipPhysXPyd:
-        !endif
-
-        !ifdef HAVE_ROCKET
-            SectionGetFlags ${SecRocket} $R0
-            IntOp $R0 $R0 & ${SF_SELECTED}
-            StrCmp $R0 ${SF_SELECTED} 0 SkipRocketPyd
-            File /nonfatal /r "${BUILT}\panda3d\rocket${EXT_SUFFIX}"
-            SkipRocketPyd:
-        !endif
+    !insertmacro PyBindingSection 2.7 .pyd
+    !if "${REGVIEW}" == "32"
+        !insertmacro PyBindingSection 3.5-32 .cp35-win32.pyd
+        !insertmacro PyBindingSection 3.6-32 .cp36-win32.pyd
+        !insertmacro PyBindingSection 3.7-32 .cp37-win32.pyd
+        !insertmacro PyBindingSection 3.8-32 .cp38-win32.pyd
+        !insertmacro PyBindingSection 3.9-32 .cp39-win32.pyd
+    !else
+        !insertmacro PyBindingSection 3.5 .cp35-win_amd64.pyd
+        !insertmacro PyBindingSection 3.6 .cp36-win_amd64.pyd
+        !insertmacro PyBindingSection 3.7 .cp37-win_amd64.pyd
+        !insertmacro PyBindingSection 3.8 .cp38-win_amd64.pyd
+        !insertmacro PyBindingSection 3.9 .cp39-win_amd64.pyd
+    !endif
+SectionGroupEnd
 
 
-        SetOutPath $INSTDIR\pandac\input
-        File /r "${BUILT}\pandac\input\*"
-        SetOutPath $INSTDIR\Pmw
-        File /nonfatal /r /x CVS "${BUILT}\Pmw\*"
-        SetOutPath $INSTDIR\panda3d.dist-info
-        File /nonfatal /r "${BUILT}\panda3d.dist-info\*"
+!ifdef INCLUDE_PYVER
+Section "Python ${INCLUDE_PYVER}" SecPython
+    SectionIn 1 2 3
 
 
-        !ifdef REGVIEW
-        SetRegView ${REGVIEW}
-        !endif
+    !ifdef REGVIEW
+    SetRegView ${REGVIEW}
+    !endif
 
 
-        ; Check for a non-Panda3D system-wide Python installation.
-        ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" ""
-        StrCmp $0 "$INSTDIR\python" UserExternalPthCheck 0
-        StrCmp $0 "" UserExternalPthCheck 0
-        IfFileExists "$0\ppython.exe" UserExternalPthCheck 0
-        IfFileExists "$0\python.exe" AskExternalPth UserExternalPthCheck
-
-        ; Check for a non-Panda3D user installation of Python.
-        UserExternalPthCheck:
-        ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" ""
-        StrCmp $0 "$INSTDIR\python" SkipExternalPth 0
-        StrCmp $0 "" SkipExternalPth 0
-        IfFileExists "$0\ppython.exe" SkipExternalPth 0
-        IfFileExists "$0\python.exe" AskExternalPth SkipExternalPth
-
-        ; We're pretty sure this Python build is of the right architecture.
-        AskExternalPth:
-        MessageBox MB_YESNO|MB_ICONQUESTION \
-            "Your system already has a copy of Python ${PYVER} installed in:$\r$\n$0$\r$\nWould you like to configure it to be able to use the Panda3D libraries?$\r$\nIf you choose no, you will only be able to use Panda3D's own copy of Python." \
-            IDYES WriteExternalPth IDNO SkipExternalPth
-
-        WriteExternalPth:
-        ;FileOpen $1 "$0\Lib\site-packages\panda.pth" w
-        ;FileWrite $1 "$INSTDIR$\r$\n"
-        ;FileWrite $1 "$INSTDIR\bin$\r$\n"
-        ;FileClose $1
-
-        ; Actually, it looks like we can just do this instead:
-        WriteRegStr HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D" "" "$INSTDIR"
+    SetDetailsPrint both
+    DetailPrint "Installing Python ${INCLUDE_PYVER} interpreter (${REGVIEW}-bit)..."
+    SetDetailsPrint listonly
 
 
-        SkipExternalPth:
-    SectionEnd
+    SetOutPath "$INSTDIR\bin"
+    File /nonfatal "${BUILT}\bin\python*.dll"
 
 
-    !ifdef HAVE_PYTHON
-    Section "Python ${PYVER}" SecPython
-        SectionIn 1 2
+    SetOutPath "$INSTDIR\python"
+    File /r /x *.pdb "${BUILT}\python\*"
 
 
-        !ifdef REGVIEW
-        SetRegView ${REGVIEW}
-        !endif
+    SetDetailsPrint both
+    DetailPrint "Adding registry keys for Python..."
+    SetDetailsPrint listonly
 
 
-        SetDetailsPrint both
-        DetailPrint "Installing Python ${PYVER} (${REGVIEW}-bit)..."
-        SetDetailsPrint listonly
+    ; Check if a copy of Python is installed for this user.
+    ReadRegStr $0 HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" ""
+    StrCmp "$0" "$INSTDIR\python" RegPath 0
+    StrCmp "$0" "" SkipFileCheck 0
+    IfFileExists "$0\python.exe" AskRegPath 0
+    SkipFileCheck:
 
 
-        SetOutPath "$INSTDIR\bin"
-        File /nonfatal "${BUILT}\bin\python*.dll"
+    ; Check if a system-wide copy of Python is installed.
+    ReadRegStr $0 HKLM "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" ""
+    StrCmp "$0" "$INSTDIR\python" RegPath 0
+    StrCmp "$0" "" RegPath 0
+    IfFileExists "$0\python.exe" AskRegPath RegPath
 
 
-        SetOutPath "$INSTDIR\python"
-        File /r /x *.pdb "${BUILT}\python\*"
+    AskRegPath:
+    MessageBox MB_YESNO|MB_ICONQUESTION \
+        "You already have a copy of Python ${INCLUDE_PYVER} installed in:$\r$\n$0$\r$\n$\r$\nPanda3D installs its own copy of Python ${INCLUDE_PYVER}, which will install alongside your existing copy.  Would you like to make Panda's copy the default Python for your user account?" \
+        IDNO SkipRegPath
 
 
-        SetDetailsPrint both
-        DetailPrint "Adding registry keys for Python..."
-        SetDetailsPrint listonly
+    RegPath:
+    WriteRegStr HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" "" "$INSTDIR\python"
+    WriteRegStr HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" "ExecutablePath" "$INSTDIR\python\python.exe"
+    SkipRegPath:
 
 
-        ; Check if a copy of Python is installed for this user.
-        ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" ""
-        StrCmp "$0" "$INSTDIR\python" RegPath 0
-        StrCmp "$0" "" SkipFileCheck 0
-        IfFileExists "$0\python.exe" AskRegPath 0
-        SkipFileCheck:
+SectionEnd
+!endif
 
 
-        ; Check if a system-wide copy of Python is installed.
-        ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" ""
-        StrCmp "$0" "$INSTDIR\python" RegPath 0
-        StrCmp "$0" "" RegPath 0
-        IfFileExists "$0\python.exe" AskRegPath RegPath
+!macro MaybeEnablePyBindingSection PYVER
+    !if "${INCLUDE_PYVER}" != "${PYVER}"
+        !ifdef SecPyBindings${PYVER}
+            ; Check if a copy of Python is installed for this user.
+            Push $0
+            ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+            StrCmp "$0" "" +2 0
+            IfFileExists "$0\python.exe" Py${PYVER}Exists 0
+
+            ; Check if a system-wide copy of Python is installed.
+            ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+            StrCmp "$0" "" Py${PYVER}ExistsNot 0
+            IfFileExists "$0\python.exe" Py${PYVER}Exists Py${PYVER}ExistsNot
+
+            Py${PYVER}Exists:
+            SectionSetFlags ${SecPyBindings${PYVER}} ${SF_SELECTED}
+            SectionSetInstTypes ${SecPyBindings${PYVER}} 3
+
+            Py${PYVER}ExistsNot:
+            Pop $0
+        !endif
+    !endif
+!macroend
 
 
-        AskRegPath:
-        MessageBox MB_YESNO|MB_ICONQUESTION \
-            "You already have a copy of Python ${PYVER} installed in:$\r$\n$0$\r$\n$\r$\nPanda3D installs its own copy of Python ${PYVER}, which will install alongside your existing copy.  Would you like to make Panda's copy the default Python for your user account?" \
-            IDNO SkipRegPath
+Function .onInit
+    ${If} ${REGVIEW} = 64
+    ${AndIfNot} ${RunningX64}
+        MessageBox MB_OK|MB_ICONEXCLAMATION "You are attempting to install the 64-bit version of Panda3D on a 32-bit version of Windows.  Please download and install the 32-bit version of Panda3D instead."
+        Abort
+    ${EndIf}
 
 
-        RegPath:
-        WriteRegStr HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" "" "$INSTDIR\python"
-        SkipRegPath:
+    !ifdef REGVIEW
+    SetRegView ${REGVIEW}
+    !endif
 
 
-    SectionEnd
+    ; We never check for 2.7; it is always enabled in Auto mode
+    !if "${REGVIEW}" == "32"
+        !insertmacro MaybeEnablePyBindingSection 3.5-32
+        !insertmacro MaybeEnablePyBindingSection 3.6-32
+        !insertmacro MaybeEnablePyBindingSection 3.7-32
+        !insertmacro MaybeEnablePyBindingSection 3.8-32
+        !insertmacro MaybeEnablePyBindingSection 3.9-32
+    !else
+        !insertmacro MaybeEnablePyBindingSection 3.5
+        !insertmacro MaybeEnablePyBindingSection 3.6
+        !insertmacro MaybeEnablePyBindingSection 3.7
+        !insertmacro MaybeEnablePyBindingSection 3.8
+        !insertmacro MaybeEnablePyBindingSection 3.9
     !endif
     !endif
-SectionGroupEnd
+FunctionEnd
+
+Function .onSelChange
+    ; If someone selects any Python version, the "shared modules" must be on.
+    ${If} ${SectionIsPartiallySelected} ${SecGroupPython}
+        SectionGetFlags ${SecPyShared} $R0
+        IntOp $R0 $R0 | ${SF_SELECTED}
+        SectionSetFlags ${SecPyShared} $R0
+    ${EndIf}
+FunctionEnd
 
 
+!ifdef INCLUDE_PYVER
 Function ConfirmPythonSelection
 Function ConfirmPythonSelection
     ; Check the current state of the "Python" section selection.
     ; Check the current state of the "Python" section selection.
     SectionGetFlags ${SecPython} $R0
     SectionGetFlags ${SecPython} $R0
@@ -474,16 +549,14 @@ Function ConfirmPythonSelection
     StrCmp $R1 ${SF_SELECTED} SkipCheck 0
     StrCmp $R1 ${SF_SELECTED} SkipCheck 0
 
 
     ; Maybe the user just doesn't want Python support at all?
     ; Maybe the user just doesn't want Python support at all?
-    SectionGetFlags ${SecPyBindings} $R1
-    IntOp $R1 $R1 & ${SF_SELECTED}
-    StrCmp $R1 ${SF_SELECTED} 0 SkipCheck
+    !insertmacro SectionFlagIsSet ${SecGroupPython} ${SF_PSELECTED} 0 SkipCheck
 
 
     !ifdef REGVIEW
     !ifdef REGVIEW
     SetRegView ${REGVIEW}
     SetRegView ${REGVIEW}
     !endif
     !endif
 
 
     ; Check for a user installation of Python.
     ; Check for a user installation of Python.
-    ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+    ReadRegStr $0 HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" ""
     StrCmp $0 "$INSTDIR\python" CheckSystemWidePython 0
     StrCmp $0 "$INSTDIR\python" CheckSystemWidePython 0
     StrCmp $0 "" CheckSystemWidePython 0
     StrCmp $0 "" CheckSystemWidePython 0
     IfFileExists "$0\ppython.exe" CheckSystemWidePython 0
     IfFileExists "$0\ppython.exe" CheckSystemWidePython 0
@@ -491,7 +564,7 @@ Function ConfirmPythonSelection
 
 
     ; Check for a system-wide Python installation.
     ; Check for a system-wide Python installation.
     CheckSystemWidePython:
     CheckSystemWidePython:
-    ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" ""
+    ReadRegStr $0 HKLM "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" ""
     StrCmp $0 "$INSTDIR\python" AskConfirmation 0
     StrCmp $0 "$INSTDIR\python" AskConfirmation 0
     StrCmp $0 "" AskConfirmation 0
     StrCmp $0 "" AskConfirmation 0
     IfFileExists "$0\ppython.exe" AskConfirmation 0
     IfFileExists "$0\ppython.exe" AskConfirmation 0
@@ -501,7 +574,7 @@ Function ConfirmPythonSelection
     ; of a different Panda3D build.)  Ask the user if he's sure about this.
     ; of a different Panda3D build.)  Ask the user if he's sure about this.
     AskConfirmation:
     AskConfirmation:
     MessageBox MB_YESNO|MB_ICONQUESTION \
     MessageBox MB_YESNO|MB_ICONQUESTION \
-        "You do not appear to have a ${REGVIEW}-bit version of Python ${PYVER} installed.  Are you sure you don't want Panda to install a compatible copy of Python?$\r$\n$\r$\nIf you choose Yes, you will not be able to do Python development with Panda3D until you install a ${REGVIEW}-bit version of Python ${PYVER} and manually configure it to be able to use Panda3D." \
+        "You do not appear to have a ${REGVIEW}-bit version of Python ${INCLUDE_PYVER} installed.  Are you sure you don't want Panda to install a compatible copy of Python?$\r$\n$\r$\nIf you choose Yes, you will not be able to do Python development with Panda3D until you install a ${REGVIEW}-bit version of Python and install the bindings for this version." \
         IDYES SkipCheck
         IDYES SkipCheck
 
 
     ; User clicked no, so re-enable the select box and abort.
     ; User clicked no, so re-enable the select box and abort.
@@ -511,9 +584,10 @@ Function ConfirmPythonSelection
 
 
     SkipCheck:
     SkipCheck:
 FunctionEnd
 FunctionEnd
+!endif
 
 
 Section "C++ support" SecHeadersLibs
 Section "C++ support" SecHeadersLibs
-    SectionIn 1
+    SectionIn 1 2
 
 
     SetDetailsPrint both
     SetDetailsPrint both
     DetailPrint "Installing header files..."
     DetailPrint "Installing header files..."
@@ -532,7 +606,7 @@ SectionEnd
 
 
 !ifdef HAVE_SAMPLES
 !ifdef HAVE_SAMPLES
 Section "Sample programs" SecSamples
 Section "Sample programs" SecSamples
-    SectionIn 1
+    SectionIn 1 2
 
 
     ; Necessary for proper start menu shortcut installation
     ; Necessary for proper start menu shortcut installation
     SetShellVarContext current
     SetShellVarContext current
@@ -599,7 +673,7 @@ SectionEnd
 
 
 !ifdef HAVE_MAX_PLUGINS
 !ifdef HAVE_MAX_PLUGINS
 Section "3ds Max plug-ins" SecMaxPlugins
 Section "3ds Max plug-ins" SecMaxPlugins
-    SectionIn 1 3
+    SectionIn 1 2
 
 
     SetDetailsPrint both
     SetDetailsPrint both
     DetailPrint "Installing Autodesk 3ds Max plug-ins..."
     DetailPrint "Installing Autodesk 3ds Max plug-ins..."
@@ -614,7 +688,7 @@ SectionEnd
 
 
 !ifdef HAVE_MAYA_PLUGINS
 !ifdef HAVE_MAYA_PLUGINS
 Section "Maya plug-ins" SecMayaPlugins
 Section "Maya plug-ins" SecMayaPlugins
-    SectionIn 1 3
+    SectionIn 1 2
 
 
     SetDetailsPrint both
     SetDetailsPrint both
     DetailPrint "Installing Autodesk Maya plug-ins..."
     DetailPrint "Installing Autodesk Maya plug-ins..."
@@ -737,17 +811,30 @@ Section Uninstall
     DeleteRegKey HKCU "Software\Classes\Panda3D.Multifile\DefaultIcon"
     DeleteRegKey HKCU "Software\Classes\Panda3D.Multifile\DefaultIcon"
     DeleteRegKey HKCU "Software\Classes\Panda3D.Multifile\shell"
     DeleteRegKey HKCU "Software\Classes\Panda3D.Multifile\shell"
 
 
-    ReadRegStr $0 HKLM "Software\Python\PythonCore\${PYVER}\InstallPath" ""
-    StrCmp $0 "$INSTDIR\python" 0 +2
-    DeleteRegKey HKLM "Software\Python\PythonCore\${PYVER}"
+    !ifdef INCLUDE_PYVER
+        ReadRegStr $0 HKLM "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" ""
+        StrCmp $0 "$INSTDIR\python" 0 +2
+        DeleteRegKey HKLM "Software\Python\PythonCore\${INCLUDE_PYVER}"
 
 
-    ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\InstallPath" ""
-    StrCmp $0 "$INSTDIR\python" 0 +2
-    DeleteRegKey HKCU "Software\Python\PythonCore\${PYVER}"
+        ReadRegStr $0 HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}\InstallPath" ""
+        StrCmp $0 "$INSTDIR\python" 0 +2
+        DeleteRegKey HKCU "Software\Python\PythonCore\${INCLUDE_PYVER}"
+    !endif
 
 
-    ReadRegStr $0 HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D" ""
-    StrCmp $0 "$INSTDIR" 0 +2
-    DeleteRegKey HKCU "Software\Python\PythonCore\${PYVER}\PythonPath\Panda3D"
+    !insertmacro RemovePythonPath 2.7
+    !if "${REGVIEW}" == "32"
+        !insertmacro RemovePythonPath 3.5-32
+        !insertmacro RemovePythonPath 3.6-32
+        !insertmacro RemovePythonPath 3.7-32
+        !insertmacro RemovePythonPath 3.8-32
+        !insertmacro RemovePythonPath 3.9-32
+    !else
+        !insertmacro RemovePythonPath 3.5
+        !insertmacro RemovePythonPath 3.6
+        !insertmacro RemovePythonPath 3.7
+        !insertmacro RemovePythonPath 3.8
+        !insertmacro RemovePythonPath 3.9
+    !endif
 
 
     SetDetailsPrint both
     SetDetailsPrint both
     DetailPrint "Deleting files..."
     DetailPrint "Deleting files..."
@@ -812,8 +899,23 @@ SectionEnd
     !insertmacro MUI_DESCRIPTION_TEXT ${SecRocket} $(DESC_SecRocket)
     !insertmacro MUI_DESCRIPTION_TEXT ${SecRocket} $(DESC_SecRocket)
   !endif
   !endif
   !insertmacro MUI_DESCRIPTION_TEXT ${SecTools} $(DESC_SecTools)
   !insertmacro MUI_DESCRIPTION_TEXT ${SecTools} $(DESC_SecTools)
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings} $(DESC_SecPyBindings)
-  !ifdef HAVE_PYTHON
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecGroupPython} $(DESC_SecGroupPython)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecPyShared} $(DESC_SecPyShared)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings2.7} $(DESC_SecPyBindings2.7)
+  !if "${REGVIEW}" == "32"
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.5-32} $(DESC_SecPyBindings3.5-32)
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.6-32} $(DESC_SecPyBindings3.6-32)
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.7-32} $(DESC_SecPyBindings3.7-32)
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.8-32} $(DESC_SecPyBindings3.8-32)
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.9-32} $(DESC_SecPyBindings3.9-32)
+  !else
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.5} $(DESC_SecPyBindings3.5)
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.6} $(DESC_SecPyBindings3.6)
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.7} $(DESC_SecPyBindings3.7)
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.8} $(DESC_SecPyBindings3.8)
+    !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.9} $(DESC_SecPyBindings3.9)
+  !endif
+  !ifdef INCLUDE_PYVER
     !insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython)
     !insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython)
   !endif
   !endif
   !insertmacro MUI_DESCRIPTION_TEXT ${SecHeadersLibs} $(DESC_SecHeadersLibs)
   !insertmacro MUI_DESCRIPTION_TEXT ${SecHeadersLibs} $(DESC_SecHeadersLibs)

+ 25 - 24
makepanda/installpanda.py

@@ -13,10 +13,6 @@ from distutils.sysconfig import get_python_lib
 from optparse import OptionParser
 from optparse import OptionParser
 from makepandacore import *
 from makepandacore import *
 
 
-def python_sitepackages_path():
-    from distutils.sysconfig import get_python_lib
-    return get_python_lib(1)
-PYTHON_SITEPACKAGES=python_sitepackages_path()
 
 
 MIME_INFO = (
 MIME_INFO = (
   ("egg", "model/x-egg", "EGG model file", "pview"),
   ("egg", "model/x-egg", "EGG model file", "pview"),
@@ -153,18 +149,11 @@ def GetLibDir():
 
 
     return "lib"
     return "lib"
 
 
-def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir()):
+def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir(), python_versions=[]):
     if (not prefix.startswith("/")):
     if (not prefix.startswith("/")):
         prefix = "/" + prefix
         prefix = "/" + prefix
     libdir = prefix + "/" + libdir
     libdir = prefix + "/" + libdir
 
 
-    # Determine the location of the Python executable and site-packages dir.
-    PPATH = get_python_lib(1)
-    if os.path.islink(sys.executable):
-        PEXEC = os.path.join(os.path.dirname(sys.executable), os.readlink(sys.executable))
-    else:
-        PEXEC = sys.executable
-
     # Create the directory structure that we will be putting our files in.
     # Create the directory structure that we will be putting our files in.
     oscmd("mkdir -m 0755 -p "+destdir+prefix+"/bin")
     oscmd("mkdir -m 0755 -p "+destdir+prefix+"/bin")
     oscmd("mkdir -m 0755 -p "+destdir+prefix+"/include")
     oscmd("mkdir -m 0755 -p "+destdir+prefix+"/include")
@@ -174,8 +163,10 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir(
     oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/application-registry")
     oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/application-registry")
     oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/applications")
     oscmd("mkdir -m 0755 -p "+destdir+prefix+"/share/applications")
     oscmd("mkdir -m 0755 -p "+destdir+libdir+"/panda3d")
     oscmd("mkdir -m 0755 -p "+destdir+libdir+"/panda3d")
-    oscmd("mkdir -m 0755 -p "+destdir+PPATH)
-    oscmd("mkdir -m 0755 -p "+destdir+PPATH+"/panda3d")
+
+    for python_version in python_versions:
+        oscmd("mkdir -m 0755 -p "+destdir+python_version["purelib"])
+        oscmd("mkdir -m 0755 -p "+destdir+python_version["platlib"]+"/panda3d")
 
 
     if (sys.platform.startswith("freebsd")):
     if (sys.platform.startswith("freebsd")):
         oscmd("mkdir -m 0755 -p "+destdir+prefix+"/etc")
         oscmd("mkdir -m 0755 -p "+destdir+prefix+"/etc")
@@ -201,10 +192,12 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir(
     if os.path.isdir(outputdir+"/Pmw"):      oscmd("cp -R "+outputdir+"/Pmw     "+destdir+prefix+"/share/panda3d/")
     if os.path.isdir(outputdir+"/Pmw"):      oscmd("cp -R "+outputdir+"/Pmw     "+destdir+prefix+"/share/panda3d/")
     if os.path.isdir(outputdir+"/plugins"):  oscmd("cp -R "+outputdir+"/plugins "+destdir+prefix+"/share/panda3d/")
     if os.path.isdir(outputdir+"/plugins"):  oscmd("cp -R "+outputdir+"/plugins "+destdir+prefix+"/share/panda3d/")
 
 
-    suffix = GetExtensionSuffix()
-    for base in os.listdir(outputdir + "/panda3d"):
-        if base.endswith(".py") or (base.endswith(suffix) and '.' not in base[:-len(suffix)]):
-            oscmd("cp "+outputdir+"/panda3d/"+base+" "+destdir+PPATH+"/panda3d/"+base)
+    for python_version in python_versions:
+        for base in os.listdir(outputdir + "/panda3d"):
+            suffix = python_version["ext_suffix"]
+            platlib = python_version["platlib"]
+            if base.endswith(".py") or (base.endswith(suffix) and '.' not in base[:-len(suffix)]):
+                oscmd("cp "+outputdir+"/panda3d/"+base+" "+destdir+platlib+"/panda3d/"+base)
 
 
     WriteMimeFile(destdir+prefix+"/share/mime-info/panda3d.mime", MIME_INFO)
     WriteMimeFile(destdir+prefix+"/share/mime-info/panda3d.mime", MIME_INFO)
     WriteKeysFile(destdir+prefix+"/share/mime-info/panda3d.keys", MIME_INFO)
     WriteKeysFile(destdir+prefix+"/share/mime-info/panda3d.keys", MIME_INFO)
@@ -214,10 +207,14 @@ def InstallPanda(destdir="", prefix="/usr", outputdir="built", libdir=GetLibDir(
         oscmd("cp makepanda/pview.desktop "+destdir+prefix+"/share/applications/pview.desktop")
         oscmd("cp makepanda/pview.desktop "+destdir+prefix+"/share/applications/pview.desktop")
 
 
     oscmd("cp doc/ReleaseNotes                  "+destdir+prefix+"/share/panda3d/ReleaseNotes")
     oscmd("cp doc/ReleaseNotes                  "+destdir+prefix+"/share/panda3d/ReleaseNotes")
-    oscmd("echo '"+prefix+"/share/panda3d' >    "+destdir+PPATH+"/panda3d.pth")
-    oscmd("echo '"+libdir+"/panda3d'>>   "+destdir+PPATH+"/panda3d.pth")
-    if os.path.isdir(outputdir+"/panda3d.dist-info"):
-        oscmd("cp -R "+outputdir+"/panda3d.dist-info "+destdir+PPATH)
+
+    for python_version in python_versions:
+        pth_file = python_version["purelib"] + "/panda3d.pth"
+        oscmd("echo '"+prefix+"/share/panda3d' > "+destdir+pth_file)
+
+        if os.path.isdir(outputdir+"/panda3d.dist-info"):
+            oscmd("cp -R "+outputdir+"/panda3d.dist-info "+destdir+python_version["platlib"])
+
     if (sys.platform.startswith("freebsd")):
     if (sys.platform.startswith("freebsd")):
         oscmd("echo '"+libdir+"/panda3d'>    "+destdir+"/usr/local/libdata/ldconfig/panda3d")
         oscmd("echo '"+libdir+"/panda3d'>    "+destdir+"/usr/local/libdata/ldconfig/panda3d")
     else:
     else:
@@ -302,6 +299,8 @@ if (__name__ == "__main__"):
     if (destdir != "" and not os.path.isdir(destdir)):
     if (destdir != "" and not os.path.isdir(destdir)):
         exit("Directory '%s' does not exist!" % destdir)
         exit("Directory '%s' does not exist!" % destdir)
 
 
+    SetOutputDir(options.outputdir)
+
     if options.verbose:
     if options.verbose:
         SetVerbose(True)
         SetVerbose(True)
 
 
@@ -310,6 +309,8 @@ if (__name__ == "__main__"):
         InstallRuntime(destdir = destdir, prefix = options.prefix, outputdir = options.outputdir)
         InstallRuntime(destdir = destdir, prefix = options.prefix, outputdir = options.outputdir)
     else:
     else:
         print("Installing Panda3D SDK into " + destdir + options.prefix)
         print("Installing Panda3D SDK into " + destdir + options.prefix)
-        InstallPanda(destdir = destdir, prefix = options.prefix, outputdir = options.outputdir)
+        InstallPanda(destdir=destdir,
+                     prefix=options.prefix,
+                     outputdir=options.outputdir,
+                     python_versions=ReadPythonVersionInfoFile())
     print("Installation finished!")
     print("Installation finished!")
-

+ 165 - 75
makepanda/makepackage.py

@@ -16,9 +16,9 @@ Architecture: ARCH
 Essential: no
 Essential: no
 Depends: DEPENDS
 Depends: DEPENDS
 Recommends: RECOMMENDS
 Recommends: RECOMMENDS
-Provides: panda3d, pythonPV-panda3d
-Conflicts: panda3d, pythonPV-panda3d
-Replaces: panda3d, pythonPV-panda3d
+Provides: PROVIDES
+Conflicts: PROVIDES
+Replaces: PROVIDES
 Maintainer: rdb <[email protected]>
 Maintainer: rdb <[email protected]>
 Installed-Size: INSTSIZE
 Installed-Size: INSTSIZE
 Description: Panda3D free 3D engine SDK
 Description: Panda3D free 3D engine SDK
@@ -73,7 +73,6 @@ This package contains the SDK for development with Panda3D, install panda3d-runt
 /usr/share/panda3d
 /usr/share/panda3d
 /etc/ld.so.conf.d/panda3d.conf
 /etc/ld.so.conf.d/panda3d.conf
 /usr/%_lib/panda3d
 /usr/%_lib/panda3d
-""" + PYTHON_SITEPACKAGES + """
 /usr/include/panda3d
 /usr/include/panda3d
 """
 """
 INSTALLER_SPEC_FILE_PVIEW = \
 INSTALLER_SPEC_FILE_PVIEW = \
@@ -169,17 +168,10 @@ def MakeInstallerNSIS(version, file, title, installdir, runtime=False, compresso
     elif os.path.isdir(file):
     elif os.path.isdir(file):
         shutil.rmtree(file)
         shutil.rmtree(file)
 
 
-    if "PYTHONVERSION" in SDK:
-        pyver = SDK["PYTHONVERSION"][6:9]
-    else:
-        pyver = "%d.%d" % (sys.version_info[:2])
-
     if GetTargetArch() == 'x64':
     if GetTargetArch() == 'x64':
         regview = '64'
         regview = '64'
     else:
     else:
         regview = '32'
         regview = '32'
-        if int(pyver[0]) == 3 and int(pyver[2]) >= 5:
-            pyver += '-32'
 
 
     if runtime:
     if runtime:
         # Invoke the make_installer script.
         # Invoke the make_installer script.
@@ -212,11 +204,22 @@ def MakeInstallerNSIS(version, file, title, installdir, runtime=False, compresso
         'OUTFILE'   : '..\\' + file,
         'OUTFILE'   : '..\\' + file,
         'BUILT'     : '..\\' + outputdir,
         'BUILT'     : '..\\' + outputdir,
         'SOURCE'    : '..',
         'SOURCE'    : '..',
-        'PYVER'     : pyver,
         'REGVIEW'   : regview,
         'REGVIEW'   : regview,
-        'EXT_SUFFIX': GetExtensionSuffix(),
     }
     }
 
 
+    # Are we shipping a version of Python?
+    if os.path.isfile(os.path.join(outputdir, "python", "python.exe")):
+        py_dlls = glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9].dll")) \
+                + glob.glob(os.path.join(outputdir, "python", "python[0-9][0-9]_d.dll"))
+        assert py_dlls
+        py_dll = os.path.basename(py_dlls[0])
+        pyver = py_dll[6] + "." + py_dll[7]
+
+        if GetTargetArch() != 'x64':
+            pyver += '-32'
+
+        nsis_defs['INCLUDE_PYVER'] = pyver
+
     if GetHost() == 'windows':
     if GetHost() == 'windows':
         cmd = os.path.join(GetThirdpartyBase(), 'win-nsis', 'makensis') + ' /V2'
         cmd = os.path.join(GetThirdpartyBase(), 'win-nsis', 'makensis') + ' /V2'
         for item in nsis_defs.items():
         for item in nsis_defs.items():
@@ -254,17 +257,28 @@ def MakeDebugSymbolArchive(zipname, dirname):
     zip.close()
     zip.close()
 
 
 
 
-def MakeInstallerLinux(version, debversion=None, runtime=False, **kwargs):
+def MakeInstallerLinux(version, debversion=None, rpmrelease=1, runtime=False,
+                       python_versions=[], **kwargs):
     outputdir = GetOutputDir()
     outputdir = GetOutputDir()
 
 
-    if not runtime and not PkgSkip("PYTHON"):
-        if "PYTHONVERSION" in SDK:
-            PYTHONV = SDK["PYTHONVERSION"].rstrip('dmu')
-        else:
-            PYTHONV = "python%d.%d" % (sys.version_info[:2])
-    else:
-        PYTHONV = "python"
-    PV = PYTHONV.replace("python", "")
+    # We pack Python 2 and Python 3, if we built with support for it.
+    python2_ver = None
+    python3_ver = None
+    install_python_versions = []
+
+    if not runtime:
+        # What's the system version of Python 3?
+        oscmd('python3 -V > "%s/tmp/python3_version.txt"' % (outputdir))
+        sys_python3_ver = '.'.join(ReadFile(outputdir + "/tmp/python3_version.txt").strip().split(' ')[1].split('.')[:2])
+
+        # Check that we built with support for these.
+        for version_info in python_versions:
+            if version_info["version"] == "2.7":
+                python2_ver = "2.7"
+                install_python_versions.append(version_info)
+            elif version_info["version"] == sys_python3_ver:
+                python3_ver = sys_python3_ver
+                install_python_versions.append(version_info)
 
 
     major_version = '.'.join(version.split('.')[:2])
     major_version = '.'.join(version.split('.')[:2])
     if not debversion:
     if not debversion:
@@ -272,7 +286,7 @@ def MakeInstallerLinux(version, debversion=None, runtime=False, **kwargs):
 
 
     # Clean and set up a directory to install Panda3D into
     # Clean and set up a directory to install Panda3D into
     oscmd("rm -rf targetroot data.tar.gz control.tar.gz panda3d.spec")
     oscmd("rm -rf targetroot data.tar.gz control.tar.gz panda3d.spec")
-    oscmd("mkdir --mode=0755 targetroot")
+    oscmd("mkdir -m 0755 targetroot")
 
 
     dpkg_present = False
     dpkg_present = False
     if os.path.exists("/usr/bin/dpkg-architecture") and os.path.exists("/usr/bin/dpkg-deb"):
     if os.path.exists("/usr/bin/dpkg-architecture") and os.path.exists("/usr/bin/dpkg-deb"):
@@ -288,9 +302,12 @@ def MakeInstallerLinux(version, debversion=None, runtime=False, **kwargs):
         # Invoke installpanda.py to install it into a temporary dir
         # Invoke installpanda.py to install it into a temporary dir
         lib_dir = GetDebLibDir()
         lib_dir = GetDebLibDir()
         if runtime:
         if runtime:
-            InstallRuntime(destdir="targetroot", prefix="/usr", outputdir=outputdir, libdir=lib_dir)
+            InstallRuntime(destdir="targetroot", prefix="/usr",
+                           outputdir=outputdir, libdir=lib_dir)
         else:
         else:
-            InstallPanda(destdir="targetroot", prefix="/usr", outputdir=outputdir, libdir=lib_dir)
+            InstallPanda(destdir="targetroot", prefix="/usr",
+                         outputdir=outputdir, libdir=lib_dir,
+                         python_versions=install_python_versions)
             oscmd("chmod -R 755 targetroot/usr/share/panda3d")
             oscmd("chmod -R 755 targetroot/usr/share/panda3d")
             oscmd("mkdir -m 0755 -p targetroot/usr/share/man/man1")
             oscmd("mkdir -m 0755 -p targetroot/usr/share/man/man1")
             oscmd("install -m 0644 doc/man/*.1 targetroot/usr/share/man/man1/")
             oscmd("install -m 0644 doc/man/*.1 targetroot/usr/share/man/man1/")
@@ -301,9 +318,10 @@ def MakeInstallerLinux(version, debversion=None, runtime=False, **kwargs):
             txt = RUNTIME_INSTALLER_DEB_FILE[1:]
             txt = RUNTIME_INSTALLER_DEB_FILE[1:]
         else:
         else:
             txt = INSTALLER_DEB_FILE[1:]
             txt = INSTALLER_DEB_FILE[1:]
-        txt = txt.replace("VERSION", debversion).replace("ARCH", pkg_arch).replace("PV", PV).replace("MAJOR", major_version)
-        txt = txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") / 1024))
-        oscmd("mkdir --mode=0755 -p targetroot/DEBIAN")
+        txt = txt.replace("VERSION", debversion).replace("ARCH", pkg_arch).replace("MAJOR", major_version)
+        txt = txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") // 1024))
+
+        oscmd("mkdir -m 0755 -p targetroot/DEBIAN")
         oscmd("cd targetroot && (find usr -type f -exec md5sum {} ;) > DEBIAN/md5sums")
         oscmd("cd targetroot && (find usr -type f -exec md5sum {} ;) > DEBIAN/md5sums")
         if not runtime:
         if not runtime:
             oscmd("cd targetroot && (find etc -type f -exec md5sum {} ;) >> DEBIAN/md5sums")
             oscmd("cd targetroot && (find etc -type f -exec md5sum {} ;) >> DEBIAN/md5sums")
@@ -337,6 +355,7 @@ def MakeInstallerLinux(version, debversion=None, runtime=False, **kwargs):
             oscmd("cd targetroot && %(dpkg_shlibdeps)s -x%(pkg_name)s %(lib_pattern)s %(bin_pattern)s*" % locals())
             oscmd("cd targetroot && %(dpkg_shlibdeps)s -x%(pkg_name)s %(lib_pattern)s %(bin_pattern)s*" % locals())
             depends = ReadFile("targetroot/debian/substvars").replace("shlibs:Depends=", "").strip()
             depends = ReadFile("targetroot/debian/substvars").replace("shlibs:Depends=", "").strip()
             recommends = ""
             recommends = ""
+            provides = "panda3d-runtime"
         else:
         else:
             pkg_name = "panda3d" + major_version
             pkg_name = "panda3d" + major_version
             pkg_dir = "debian/panda3d" + major_version
             pkg_dir = "debian/panda3d" + major_version
@@ -352,15 +371,29 @@ def MakeInstallerLinux(version, debversion=None, runtime=False, **kwargs):
             # Parse the substvars files generated by dpkg-shlibdeps.
             # Parse the substvars files generated by dpkg-shlibdeps.
             depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip()
             depends = ReadFile("targetroot/debian/substvars_dep").replace("shlibs:Depends=", "").strip()
             recommends = ReadFile("targetroot/debian/substvars_rec").replace("shlibs:Depends=", "").strip()
             recommends = ReadFile("targetroot/debian/substvars_rec").replace("shlibs:Depends=", "").strip()
-            if PkgSkip("PYTHON")==0:
-                depends += ", " + PYTHONV
-                recommends += ", python-wxversion, python-profiler (>= " + PV + "), python-pmw, python-tk (>= " + PV + ")"
-            if PkgSkip("NVIDIACG")==0:
+            provides = "panda3d"
+
+            if python2_ver or python3_ver:
+                recommends += ", python-pmw"
+
+            if python2_ver:
+                depends += ", python%s" % (python2_ver)
+                recommends += ", python-wxversion"
+                recommends += ", python-tk (>= %s)" % (python2_ver)
+                provides += ", python2-panda3d"
+
+            if python3_ver:
+                depends += ", python%s" % (python3_ver)
+                recommends += ", python3-tk (>= %s)" % (python3_ver)
+                provides += ", python3-panda3d"
+
+            if not PkgSkip("NVIDIACG"):
                 depends += ", nvidia-cg-toolkit"
                 depends += ", nvidia-cg-toolkit"
 
 
         # Write back the dependencies, and delete the dummy set-up.
         # Write back the dependencies, and delete the dummy set-up.
         txt = txt.replace("DEPENDS", depends.strip(', '))
         txt = txt.replace("DEPENDS", depends.strip(', '))
         txt = txt.replace("RECOMMENDS", recommends.strip(', '))
         txt = txt.replace("RECOMMENDS", recommends.strip(', '))
+        txt = txt.replace("PROVIDES", provides.strip(', '))
         WriteFile("targetroot/DEBIAN/control", txt)
         WriteFile("targetroot/DEBIAN/control", txt)
         oscmd("rm -rf targetroot/debian")
         oscmd("rm -rf targetroot/debian")
 
 
@@ -376,7 +409,9 @@ def MakeInstallerLinux(version, debversion=None, runtime=False, **kwargs):
         if runtime:
         if runtime:
             InstallRuntime(destdir="targetroot", prefix="/usr", outputdir=outputdir, libdir=GetRPMLibDir())
             InstallRuntime(destdir="targetroot", prefix="/usr", outputdir=outputdir, libdir=GetRPMLibDir())
         else:
         else:
-            InstallPanda(destdir="targetroot", prefix="/usr", outputdir=outputdir, libdir=GetRPMLibDir())
+            InstallPanda(destdir="targetroot", prefix="/usr",
+                         outputdir=outputdir, libdir=GetRPMLibDir(),
+                         python_versions=install_python_versions)
             oscmd("chmod -R 755 targetroot/usr/share/panda3d")
             oscmd("chmod -R 755 targetroot/usr/share/panda3d")
 
 
         oscmd("rpm -E '%_target_cpu' > "+outputdir+"/tmp/architecture.txt")
         oscmd("rpm -E '%_target_cpu' > "+outputdir+"/tmp/architecture.txt")
@@ -392,15 +427,23 @@ def MakeInstallerLinux(version, debversion=None, runtime=False, **kwargs):
             if not PkgSkip("PVIEW"):
             if not PkgSkip("PVIEW"):
                 txt += INSTALLER_SPEC_FILE_PVIEW
                 txt += INSTALLER_SPEC_FILE_PVIEW
 
 
+            # Add the platform-specific Python directories.
+            dirs = set()
+            for version_info in install_python_versions:
+                dirs.add(version_info["platlib"])
+                dirs.add(version_info["purelib"])
+
+            for dir in dirs:
+                txt += dir + "\n"
+
             # Add the binaries in /usr/bin explicitly to the spec file
             # Add the binaries in /usr/bin explicitly to the spec file
             for base in os.listdir(outputdir + "/bin"):
             for base in os.listdir(outputdir + "/bin"):
                 txt += "/usr/bin/%s\n" % (base)
                 txt += "/usr/bin/%s\n" % (base)
 
 
         # Write out the spec file.
         # Write out the spec file.
         txt = txt.replace("VERSION", version)
         txt = txt.replace("VERSION", version)
-        txt = txt.replace("RPMRELEASE", rpmrelease)
+        txt = txt.replace("RPMRELEASE", str(rpmrelease))
         txt = txt.replace("PANDASOURCE", pandasource)
         txt = txt.replace("PANDASOURCE", pandasource)
-        txt = txt.replace("PV", PV)
         WriteFile("panda3d.spec", txt)
         WriteFile("panda3d.spec", txt)
 
 
         oscmd("fakeroot rpmbuild --define '_rpmdir "+pandasource+"' --buildroot '"+os.path.abspath("targetroot")+"' -bb panda3d.spec")
         oscmd("fakeroot rpmbuild --define '_rpmdir "+pandasource+"' --buildroot '"+os.path.abspath("targetroot")+"' -bb panda3d.spec")
@@ -414,7 +457,7 @@ def MakeInstallerLinux(version, debversion=None, runtime=False, **kwargs):
         exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!")
         exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!")
 
 
 
 
-def MakeInstallerOSX(version, runtime=False, **kwargs):
+def MakeInstallerOSX(version, runtime=False, python_versions=[], **kwargs):
     outputdir = GetOutputDir()
     outputdir = GetOutputDir()
 
 
     if runtime:
     if runtime:
@@ -428,17 +471,11 @@ def MakeInstallerOSX(version, runtime=False, **kwargs):
         oscmd(cmdstr)
         oscmd(cmdstr)
         return
         return
 
 
-    if "PYTHONVERSION" in SDK:
-        pyver = SDK["PYTHONVERSION"][6:9]
-    else:
-        pyver = "%d.%d" % (sys.version_info[:2])
-
     dmg_name = "Panda3D-" + version
     dmg_name = "Panda3D-" + version
-    if not pyver.startswith("2."):
+    if len(python_versions) == 1 and not python_versions[0]["version"].startswith("2."):
         dmg_name += "-py" + pyver
         dmg_name += "-py" + pyver
     dmg_name += ".dmg"
     dmg_name += ".dmg"
 
 
-    import compileall
     if (os.path.isfile(dmg_name)): oscmd("rm -f %s" % dmg_name)
     if (os.path.isfile(dmg_name)): oscmd("rm -f %s" % dmg_name)
     if (os.path.exists("dstroot")): oscmd("rm -rf dstroot")
     if (os.path.exists("dstroot")): oscmd("rm -rf dstroot")
     if (os.path.exists("Panda3D-rw.dmg")): oscmd('rm -f Panda3D-rw.dmg')
     if (os.path.exists("Panda3D-rw.dmg")): oscmd('rm -f Panda3D-rw.dmg')
@@ -479,33 +516,43 @@ def MakeInstallerOSX(version, runtime=False, **kwargs):
         # OSX needs the -R argument to copy symbolic links correctly, it doesn't have -d. How weird.
         # OSX needs the -R argument to copy symbolic links correctly, it doesn't have -d. How weird.
         oscmd("cp -R " + outputdir + "/bin/" + base + " " + binname)
         oscmd("cp -R " + outputdir + "/bin/" + base + " " + binname)
 
 
-    if not PkgSkip("PYTHON"):
-        pyexec = SDK.get("PYTHONEXEC", sys.executable)
-        oscmd("mkdir -p dstroot/pythoncode/usr/local/bin")
+    if python_versions:
+        # Let's only write a ppython link if there is only one Python version.
+        if len(python_versions) == 1:
+            oscmd("mkdir -p dstroot/pythoncode/usr/local/bin")
+            oscmd("ln -s %s dstroot/pythoncode/usr/local/bin/ppython" % (python_versions[0]["executable"]))
+
         oscmd("mkdir -p dstroot/pythoncode/Developer/Panda3D/panda3d")
         oscmd("mkdir -p dstroot/pythoncode/Developer/Panda3D/panda3d")
-        oscmd("mkdir -p dstroot/pythoncode/Library/Python/%s/site-packages" % pyver)
-        WriteFile("dstroot/pythoncode/Library/Python/%s/site-packages/Panda3D.pth" % pyver, "/Developer/Panda3D")
         oscmd("cp -R %s/pandac                dstroot/pythoncode/Developer/Panda3D/pandac" % outputdir)
         oscmd("cp -R %s/pandac                dstroot/pythoncode/Developer/Panda3D/pandac" % outputdir)
         oscmd("cp -R %s/direct                dstroot/pythoncode/Developer/Panda3D/direct" % outputdir)
         oscmd("cp -R %s/direct                dstroot/pythoncode/Developer/Panda3D/direct" % outputdir)
-        oscmd("ln -s %s                       dstroot/pythoncode/usr/local/bin/ppython" % pyexec)
         oscmd("cp -R %s/*.so                  dstroot/pythoncode/Developer/Panda3D/" % outputdir, True)
         oscmd("cp -R %s/*.so                  dstroot/pythoncode/Developer/Panda3D/" % outputdir, True)
         oscmd("cp -R %s/*.py                  dstroot/pythoncode/Developer/Panda3D/" % outputdir, True)
         oscmd("cp -R %s/*.py                  dstroot/pythoncode/Developer/Panda3D/" % outputdir, True)
         if os.path.isdir(outputdir+"/Pmw"):
         if os.path.isdir(outputdir+"/Pmw"):
             oscmd("cp -R %s/Pmw               dstroot/pythoncode/Developer/Panda3D/Pmw" % outputdir)
             oscmd("cp -R %s/Pmw               dstroot/pythoncode/Developer/Panda3D/Pmw" % outputdir)
-            compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/Pmw")
-        WriteFile("dstroot/pythoncode/Developer/Panda3D/direct/__init__.py", "")
-        for base in os.listdir("dstroot/pythoncode/Developer/Panda3D/direct"):
-            if ((base != "extensions") and (base != "extensions_native")):
-                compileall.compile_dir("dstroot/pythoncode/Developer/Panda3D/direct/"+base)
 
 
-        suffix = GetExtensionSuffix()
         for base in os.listdir(outputdir+"/panda3d"):
         for base in os.listdir(outputdir+"/panda3d"):
-            if base.endswith('.py') or (base.endswith(suffix) and '.' not in base[:-len(suffix)]):
+            if base.endswith('.py'):
                 libname = "dstroot/pythoncode/Developer/Panda3D/panda3d/" + base
                 libname = "dstroot/pythoncode/Developer/Panda3D/panda3d/" + base
+                oscmd("cp -R " + outputdir + "/panda3d/" + base + " " + libname)
+
+    for version_info in python_versions:
+        pyver = version_info["version"]
+        oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver))
+        oscmd("mkdir -p dstroot/pybindings%s/Developer/Panda3D/panda3d" % (pyver))
+
+        # Copy over extension modules.
+        suffix = version_info["ext_suffix"]
+        for base in os.listdir(outputdir+"/panda3d"):
+            if base.endswith(suffix) and '.' not in base[:-len(suffix)]:
+                libname = "dstroot/pybindings%s/Developer/Panda3D/panda3d/%s" % (pyver, base)
                 # We really need to specify -R in order not to follow symlinks
                 # We really need to specify -R in order not to follow symlinks
                 # On OSX, just specifying -P is not enough to do that.
                 # On OSX, just specifying -P is not enough to do that.
                 oscmd("cp -R -P " + outputdir + "/panda3d/" + base + " " + libname)
                 oscmd("cp -R -P " + outputdir + "/panda3d/" + base + " " + libname)
 
 
+        # Write a .pth file.
+        oscmd("mkdir -p dstroot/pybindings%s/Library/Python/%s/site-packages" % (pyver, pyver))
+        WriteFile("dstroot/pybindings%s/Library/Python/%s/site-packages/Panda3D.pth" % (pyver, pyver), "/Developer/Panda3D")
+
     if not PkgSkip("FFMPEG"):
     if not PkgSkip("FFMPEG"):
         oscmd("mkdir -p dstroot/ffmpeg/Developer/Panda3D/lib")
         oscmd("mkdir -p dstroot/ffmpeg/Developer/Panda3D/lib")
         oscmd("cp -R %s/lib/libp3ffmpeg.* dstroot/ffmpeg/Developer/Panda3D/lib/" % outputdir)
         oscmd("cp -R %s/lib/libp3ffmpeg.* dstroot/ffmpeg/Developer/Panda3D/lib/" % outputdir)
@@ -528,9 +575,19 @@ def MakeInstallerOSX(version, runtime=False, **kwargs):
         oscmd("mkdir -p dstroot/samples/Developer/Examples/Panda3D")
         oscmd("mkdir -p dstroot/samples/Developer/Examples/Panda3D")
         oscmd("cp -R samples/* dstroot/samples/Developer/Examples/Panda3D/")
         oscmd("cp -R samples/* dstroot/samples/Developer/Examples/Panda3D/")
 
 
-    oscmd("chmod -R 0775 dstroot/*")
     DeleteVCS("dstroot")
     DeleteVCS("dstroot")
     DeleteBuildFiles("dstroot")
     DeleteBuildFiles("dstroot")
+
+    # Compile Python files.  Do this *after* the DeleteVCS step, above, which
+    # deletes __pycache__ directories.
+    for version_info in python_versions:
+        if os.path.isdir("dstroot/pythoncode/Developer/Panda3D/Pmw"):
+            oscmd("%s -m compileall -q -f -d /Developer/Panda3D/Pmw dstroot/pythoncode/Developer/Panda3D/Pmw" % (version_info["executable"]), True)
+        oscmd("%s -m compileall -q -f -d /Developer/Panda3D/direct dstroot/pythoncode/Developer/Panda3D/direct" % (version_info["executable"]))
+        oscmd("%s -m compileall -q -f -d /Developer/Panda3D/pandac dstroot/pythoncode/Developer/Panda3D/pandac" % (version_info["executable"]))
+        oscmd("%s -m compileall -q -f -d /Developer/Panda3D/panda3d dstroot/pythoncode/Developer/Panda3D/panda3d" % (version_info["executable"]))
+
+    oscmd("chmod -R 0775 dstroot/*")
     # We need to be root to perform a chown. Bleh.
     # We need to be root to perform a chown. Bleh.
     # Fortunately PackageMaker does it for us, on 10.5 and above.
     # Fortunately PackageMaker does it for us, on 10.5 and above.
     #oscmd("chown -R root:admin dstroot/*", True)
     #oscmd("chown -R root:admin dstroot/*", True)
@@ -539,7 +596,10 @@ def MakeInstallerOSX(version, runtime=False, **kwargs):
     oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Resources/en.lproj/")
     oscmd("mkdir -p dstroot/Panda3D/Panda3D.mpkg/Contents/Resources/en.lproj/")
 
 
     pkgs = ["base", "tools", "headers"]
     pkgs = ["base", "tools", "headers"]
-    if not PkgSkip("PYTHON"):    pkgs.append("pythoncode")
+    if python_versions:
+        pkgs.append("pythoncode")
+    for version_info in python_versions:
+        pkgs.append("pybindings" + version_info["version"])
     if not PkgSkip("FFMPEG"):    pkgs.append("ffmpeg")
     if not PkgSkip("FFMPEG"):    pkgs.append("ffmpeg")
     #if not PkgSkip("OPENAL"):    pkgs.append("openal")
     #if not PkgSkip("OPENAL"):    pkgs.append("openal")
     if not PkgSkip("FMODEX"):    pkgs.append("fmodex")
     if not PkgSkip("FMODEX"):    pkgs.append("fmodex")
@@ -581,22 +641,55 @@ def MakeInstallerOSX(version, runtime=False, **kwargs):
     dist.write('    <title>Panda3D SDK %s</title>\n' % (version))
     dist.write('    <title>Panda3D SDK %s</title>\n' % (version))
     dist.write('    <options customize="always" allow-external-scripts="no" rootVolumeOnly="false"/>\n')
     dist.write('    <options customize="always" allow-external-scripts="no" rootVolumeOnly="false"/>\n')
     dist.write('    <license language="en" mime-type="text/plain">%s</license>\n' % ReadFile("doc/LICENSE"))
     dist.write('    <license language="en" mime-type="text/plain">%s</license>\n' % ReadFile("doc/LICENSE"))
+    dist.write('    <script>\n')
+    dist.write('    function isPythonVersionInstalled(version) {\n')
+    dist.write('        return system.files.fileExistsAtPath("/usr/bin/python" + version)\n')
+    dist.write('            || system.files.fileExistsAtPath("/usr/local/bin/python" + version)\n')
+    dist.write('            || system.files.fileExistsAtPath("/opt/local/bin/python" + version)\n')
+    dist.write('            || system.files.fileExistsAtPath("/sw/bin/python" + version)\n')
+    dist.write('            || system.files.fileExistsAtPath("/System/Library/Frameworks/Python.framework/Versions/" + version + "/bin/python")\n')
+    dist.write('            || system.files.fileExistsAtPath("/Library/Frameworks/Python.framework/Versions/" + version + "/bin/python");\n')
+    dist.write('    }\n')
+    dist.write('    </script>\n')
     dist.write('    <choices-outline>\n')
     dist.write('    <choices-outline>\n')
-    for pkg in pkgs:
-        dist.write('        <line choice="%s"/>\n' % (pkg))
+    dist.write('        <line choice="base"/>\n')
+    if python_versions:
+        dist.write('        <line choice="pythoncode">\n')
+        for version_info in sorted(python_versions, key=lambda info:info["version"], reverse=True):
+            dist.write('            <line choice="pybindings%s"/>\n' % (version_info["version"]))
+        dist.write('        </line>\n')
+    dist.write('        <line choice="tools"/>\n')
+    if os.path.isdir("samples"):
+        dist.write('        <line choice="samples"/>\n')
+    if not PkgSkip("FFMPEG"):
+        dist.write('        <line choice="ffmpeg"/>\n')
+    if not PkgSkip("FMODEX"):
+        dist.write('        <line choice="fmodex"/>\n')
+    dist.write('        <line choice="headers"/>\n')
     dist.write('    </choices-outline>\n')
     dist.write('    </choices-outline>\n')
-    dist.write('    <choice id="base" title="Panda3D Base Installation" description="This package contains the Panda3D libraries, configuration files and models/textures that are needed to use Panda3D. Location: /Developer/Panda3D/" start_enabled="false">\n')
+    dist.write('    <choice id="base" title="Panda3D Base Installation" description="This package contains the Panda3D libraries, configuration files and models/textures that are needed to use Panda3D.&#10;&#10;Location: /Developer/Panda3D/" start_enabled="false">\n')
     dist.write('        <pkg-ref id="org.panda3d.panda3d.base.pkg"/>\n')
     dist.write('        <pkg-ref id="org.panda3d.panda3d.base.pkg"/>\n')
     dist.write('    </choice>\n')
     dist.write('    </choice>\n')
-    dist.write('    <choice id="tools" title="Tools" tooltip="Useful tools and model converters to help with Panda3D development" description="This package contains the various utilities that ship with Panda3D, including packaging tools, model converters, and many more. Location: /Developer/Panda3D/bin/">\n')
+    dist.write('    <choice id="tools" title="Tools" tooltip="Useful tools and model converters to help with Panda3D development" description="This package contains the various utilities that ship with Panda3D, including packaging tools, model converters, and many more.&#10;&#10;Location: /Developer/Panda3D/bin/">\n')
     dist.write('        <pkg-ref id="org.panda3d.panda3d.tools.pkg"/>\n')
     dist.write('        <pkg-ref id="org.panda3d.panda3d.tools.pkg"/>\n')
     dist.write('    </choice>\n')
     dist.write('    </choice>\n')
 
 
-    if not PkgSkip("PYTHON"):
-        dist.write('    <choice id="pythoncode" title="Python Support" tooltip="Python bindings for the Panda3D libraries" description="This package contains the \'direct\', \'pandac\' and \'panda3d\' python packages that are needed to do Python development with Panda3D. Location: /Developer/Panda3D/">\n')
+    if python_versions:
+        dist.write('    <choice id="pythoncode" title="Python Support" tooltip="Python bindings for the Panda3D libraries" description="This package contains the \'direct\', \'pandac\' and \'panda3d\' python packages that are needed to do Python development with Panda3D.&#10;&#10;Location: /Developer/Panda3D/">\n')
         dist.write('        <pkg-ref id="org.panda3d.panda3d.pythoncode.pkg"/>\n')
         dist.write('        <pkg-ref id="org.panda3d.panda3d.pythoncode.pkg"/>\n')
         dist.write('    </choice>\n')
         dist.write('    </choice>\n')
 
 
+    for version_info in python_versions:
+        pyver = version_info["version"]
+        if pyver == "2.7":
+            # Always install Python 2.7 by default; it's included on macOS.
+            cond = "true"
+        else:
+            cond = "isPythonVersionInstalled('%s')" % (pyver)
+        dist.write('    <choice id="pybindings%s" start_selected="%s" title="Python %s Bindings" tooltip="Python bindings for the Panda3D libraries" description="Support for Python %s.">\n' % (pyver, cond, pyver, pyver))
+        dist.write('        <pkg-ref id="org.panda3d.panda3d.pybindings%s.pkg"/>\n' % (pyver))
+        dist.write('    </choice>\n')
+
     if not PkgSkip("FFMPEG"):
     if not PkgSkip("FFMPEG"):
         dist.write('    <choice id="ffmpeg" title="FFMpeg Plug-In" tooltip="FFMpeg video and audio decoding plug-in" description="This package contains the FFMpeg plug-in, which is used for decoding video and audio files with OpenAL.')
         dist.write('    <choice id="ffmpeg" title="FFMpeg Plug-In" tooltip="FFMpeg video and audio decoding plug-in" description="This package contains the FFMpeg plug-in, which is used for decoding video and audio files with OpenAL.')
         if PkgSkip("VORBIS") and PkgSkip("OPUS"):
         if PkgSkip("VORBIS") and PkgSkip("OPUS"):
@@ -621,11 +714,11 @@ def MakeInstallerOSX(version, runtime=False, **kwargs):
         dist.write('    </choice>\n')
         dist.write('    </choice>\n')
 
 
     if os.path.isdir("samples"):
     if os.path.isdir("samples"):
-        dist.write('    <choice id="samples" title="Sample Programs" tooltip="Python sample programs that use Panda3D" description="This package contains the Python sample programs that can help you with learning how to use Panda3D. Location: /Developer/Examples/Panda3D/">\n')
+        dist.write('    <choice id="samples" title="Sample Programs" tooltip="Python sample programs that use Panda3D" description="This package contains the Python sample programs that can help you with learning how to use Panda3D.&#10;&#10;Location: /Developer/Examples/Panda3D/">\n')
         dist.write('        <pkg-ref id="org.panda3d.panda3d.samples.pkg"/>\n')
         dist.write('        <pkg-ref id="org.panda3d.panda3d.samples.pkg"/>\n')
         dist.write('    </choice>\n')
         dist.write('    </choice>\n')
 
 
-    dist.write('    <choice id="headers" title="C++ Header Files" tooltip="Header files for C++ development with Panda3D" description="This package contains the C++ header files that are needed in order to do C++ development with Panda3D. You don\'t need this if you want to develop in Python. Location: /Developer/Panda3D/include/" start_selected="false">\n')
+    dist.write('    <choice id="headers" title="C++ Header Files" tooltip="Header files for C++ development with Panda3D" description="This package contains the C++ header files that are needed in order to do C++ development with Panda3D. You don\'t need this if you want to develop in Python.&#10;&#10;Location: /Developer/Panda3D/include/" start_selected="false">\n')
     dist.write('        <pkg-ref id="org.panda3d.panda3d.headers.pkg"/>\n')
     dist.write('        <pkg-ref id="org.panda3d.panda3d.headers.pkg"/>\n')
     dist.write('    </choice>\n')
     dist.write('    </choice>\n')
     for pkg in pkgs:
     for pkg in pkgs:
@@ -693,7 +786,7 @@ def MakeInstallerFreeBSD(version, runtime=False, **kwargs):
     manifest_txt = manifest_txt.replace("ARCH", pkg_arch)
     manifest_txt = manifest_txt.replace("ARCH", pkg_arch)
     manifest_txt = manifest_txt.replace("ORIGIN", 'devel/panda3d' if not runtime else 'graphics/panda3d-runtime')
     manifest_txt = manifest_txt.replace("ORIGIN", 'devel/panda3d' if not runtime else 'graphics/panda3d-runtime')
     manifest_txt = manifest_txt.replace("DEPENDS", dependencies)
     manifest_txt = manifest_txt.replace("DEPENDS", dependencies)
-    manifest_txt = manifest_txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") / 1024 / 1024))
+    manifest_txt = manifest_txt.replace("INSTSIZE", str(GetDirectorySize("targetroot") // 1024 // 1024))
 
 
     WriteFile("pkg-plist", plist_txt)
     WriteFile("pkg-plist", plist_txt)
     WriteFile("+DESC", INSTALLER_PKG_DESCR_FILE[1:] if not runtime else RUNTIME_INSTALLER_PKG_DESCR_FILE[1:])
     WriteFile("+DESC", INSTALLER_PKG_DESCR_FILE[1:] if not runtime else RUNTIME_INSTALLER_PKG_DESCR_FILE[1:])
@@ -889,13 +982,9 @@ def MakeInstaller(version, **kwargs):
 
 
         fn += version
         fn += version
 
 
-        if "PYTHONVERSION" in SDK:
-            pyver = SDK["PYTHONVERSION"][6:9]
-        else:
-            pyver = "%d.%d" % (sys.version_info[:2])
-
-        if not runtime and pyver != "2.7":
-            fn += '-py' + pyver
+        python_versions = kwargs.get('python_versions', [])
+        if not runtime and len(python_versions) == 1 and python_versions[0]["version"] != "2.7":
+            fn += '-py' + python_versions[0]["version"]
 
 
         if GetOptimize() <= 2:
         if GetOptimize() <= 2:
             fn += "-dbg"
             fn += "-dbg"
@@ -961,4 +1050,5 @@ if __name__ == "__main__":
                   compressor=options.compressor,
                   compressor=options.compressor,
                   debversion=options.debversion,
                   debversion=options.debversion,
                   rpmrelease=options.rpmrelease,
                   rpmrelease=options.rpmrelease,
-                  runtime=options.runtime)
+                  runtime=options.runtime,
+                  python_versions=ReadPythonVersionInfoFile())

+ 25 - 10
makepanda/makepanda.py

@@ -72,6 +72,7 @@ MSVC_VERSION = None
 BOOUSEINTELCOMPILER = False
 BOOUSEINTELCOMPILER = False
 OPENCV_VER_23 = False
 OPENCV_VER_23 = False
 PLATFORM = None
 PLATFORM = None
+COPY_PYTHON = True
 
 
 if "MACOSX_DEPLOYMENT_TARGET" in os.environ:
 if "MACOSX_DEPLOYMENT_TARGET" in os.environ:
     OSXTARGET=os.environ["MACOSX_DEPLOYMENT_TARGET"]
     OSXTARGET=os.environ["MACOSX_DEPLOYMENT_TARGET"]
@@ -172,6 +173,7 @@ def parseopts(args):
     global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL
     global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL
     global DEBVERSION,WHLVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION
     global DEBVERSION,WHLVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION
     global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER
     global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER
+    global COPY_PYTHON
 
 
     # Options for which to display a deprecation warning.
     # Options for which to display a deprecation warning.
     removedopts = [
     removedopts = [
@@ -185,7 +187,7 @@ def parseopts(args):
         "version=","lzma","no-python","threads=","outputdir=","override=",
         "version=","lzma","no-python","threads=","outputdir=","override=",
         "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=",
         "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=",
         "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl",
         "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl",
-        "universal", "target=", "arch=", "git-commit=",
+        "universal", "target=", "arch=", "git-commit=", "no-copy-python",
         ] + removedopts
         ] + removedopts
 
 
     anything = 0
     anything = 0
@@ -251,6 +253,7 @@ def parseopts(args):
                 MSVC_VERSION = value.strip().lower()
                 MSVC_VERSION = value.strip().lower()
             elif (option=="--use-icl"): BOOUSEINTELCOMPILER = True
             elif (option=="--use-icl"): BOOUSEINTELCOMPILER = True
             elif (option=="--clean"): clean_build = True
             elif (option=="--clean"): clean_build = True
+            elif (option=="--no-copy-python"): COPY_PYTHON = False
             elif (option[2:] in removedopts):
             elif (option[2:] in removedopts):
                 Warn("Ignoring removed option %s" % (option))
                 Warn("Ignoring removed option %s" % (option))
             else:
             else:
@@ -3031,7 +3034,7 @@ else:
     configprc = configprc.replace("aux-display pandadx9", "")
     configprc = configprc.replace("aux-display pandadx9", "")
 
 
 if (GetTarget() == 'darwin'):
 if (GetTarget() == 'darwin'):
-    configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "Library/Caches/Panda3D-%s" % MAJOR_VERSION)
+    configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "$HOME/Library/Caches/Panda3D-%s" % MAJOR_VERSION)
 
 
     # OpenAL is not yet working well on OSX for us, so let's do this for now.
     # OpenAL is not yet working well on OSX for us, so let's do this for now.
     configprc = configprc.replace("p3openal_audio", "p3fmod_audio")
     configprc = configprc.replace("p3openal_audio", "p3fmod_audio")
@@ -3155,7 +3158,8 @@ if tp_dir is not None:
                 CopyFile(GetOutputDir() + "/bin/", fn)
                 CopyFile(GetOutputDir() + "/bin/", fn)
 
 
             # Copy the whole Python directory.
             # Copy the whole Python directory.
-            CopyTree(GetOutputDir() + "/python", SDK["PYTHON"])
+            if COPY_PYTHON:
+                CopyTree(GetOutputDir() + "/python", SDK["PYTHON"])
 
 
             # NB: Python does not always ship with the correct manifest/dll.
             # NB: Python does not always ship with the correct manifest/dll.
             # Figure out the correct one to ship, and grab it from WinSxS dir.
             # Figure out the correct one to ship, and grab it from WinSxS dir.
@@ -3164,7 +3168,7 @@ if tp_dir is not None:
                 os.unlink(manifest)
                 os.unlink(manifest)
             oscmd('mt -inputresource:"%s\\python.exe";#1 -out:"%s" -nologo' % (SDK["PYTHON"], manifest), True)
             oscmd('mt -inputresource:"%s\\python.exe";#1 -out:"%s" -nologo' % (SDK["PYTHON"], manifest), True)
 
 
-            if os.path.isfile(manifest):
+            if COPY_PYTHON and os.path.isfile(manifest):
                 import xml.etree.ElementTree as ET
                 import xml.etree.ElementTree as ET
                 tree = ET.parse(manifest)
                 tree = ET.parse(manifest)
                 idents = tree.findall('./{urn:schemas-microsoft-com:asm.v1}dependency/{urn:schemas-microsoft-com:asm.v1}dependentAssembly/{urn:schemas-microsoft-com:asm.v1}assemblyIdentity')
                 idents = tree.findall('./{urn:schemas-microsoft-com:asm.v1}dependency/{urn:schemas-microsoft-com:asm.v1}dependentAssembly/{urn:schemas-microsoft-com:asm.v1}assemblyIdentity')
@@ -3194,11 +3198,12 @@ if tp_dir is not None:
                     CopyFile(GetOutputDir() + "/python/", file)
                     CopyFile(GetOutputDir() + "/python/", file)
 
 
             # Copy python.exe to ppython.exe.
             # Copy python.exe to ppython.exe.
-            if not os.path.isfile(SDK["PYTHON"] + "/ppython.exe") and os.path.isfile(SDK["PYTHON"] + "/python.exe"):
-                CopyFile(GetOutputDir() + "/python/ppython.exe", SDK["PYTHON"] + "/python.exe")
-            if not os.path.isfile(SDK["PYTHON"] + "/ppythonw.exe") and os.path.isfile(SDK["PYTHON"] + "/pythonw.exe"):
-                CopyFile(GetOutputDir() + "/python/ppythonw.exe", SDK["PYTHON"] + "/pythonw.exe")
-            ConditionalWriteFile(GetOutputDir() + "/python/panda.pth", "..\n../bin\n")
+            if COPY_PYTHON:
+                if not os.path.isfile(SDK["PYTHON"] + "/ppython.exe") and os.path.isfile(SDK["PYTHON"] + "/python.exe"):
+                    CopyFile(GetOutputDir() + "/python/ppython.exe", SDK["PYTHON"] + "/python.exe")
+                if not os.path.isfile(SDK["PYTHON"] + "/ppythonw.exe") and os.path.isfile(SDK["PYTHON"] + "/pythonw.exe"):
+                    CopyFile(GetOutputDir() + "/python/ppythonw.exe", SDK["PYTHON"] + "/pythonw.exe")
+                ConditionalWriteFile(GetOutputDir() + "/python/panda.pth", "..\n../bin\n")
 
 
 # Copy over the MSVC runtime.
 # Copy over the MSVC runtime.
 if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK:
 if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK:
@@ -6869,6 +6874,10 @@ if RUNTESTS:
         cmdstr += " --verbose"
         cmdstr += " --verbose"
     oscmd(cmdstr)
     oscmd(cmdstr)
 
 
+# Write out information about the Python versions in the built dir.
+python_version_info = GetCurrentPythonVersionInfo()
+UpdatePythonVersionInfoFile(python_version_info)
+
 ##########################################################################################
 ##########################################################################################
 #
 #
 # The Installers
 # The Installers
@@ -6882,10 +6891,16 @@ if RUNTESTS:
 if INSTALLER:
 if INSTALLER:
     ProgressOutput(100.0, "Building installer")
     ProgressOutput(100.0, "Building installer")
     from makepackage import MakeInstaller
     from makepackage import MakeInstaller
+
+    # When using the --installer flag, only install for the current version.
+    python_versions = []
+    if python_version_info:
+        python_versions.append(python_version_info)
+
     MakeInstaller(version=VERSION, outputdir=GetOutputDir(),
     MakeInstaller(version=VERSION, outputdir=GetOutputDir(),
                   optimize=GetOptimize(), compressor=COMPRESSOR,
                   optimize=GetOptimize(), compressor=COMPRESSOR,
                   debversion=DEBVERSION, rpmrelease=RPMRELEASE,
                   debversion=DEBVERSION, rpmrelease=RPMRELEASE,
-                  runtime=RUNTIME)
+                  runtime=RUNTIME, python_versions=python_versions)
 
 
 if WHEEL:
 if WHEEL:
     ProgressOutput(100.0, "Building wheel")
     ProgressOutput(100.0, "Building wheel")

+ 3 - 3
makepanda/makepanda.vcproj

@@ -1043,7 +1043,7 @@
 				<File RelativePath="..\panda\src\collide\collisionLine.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionLine.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionHandlerFluidPusher.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionHandlerFluidPusher.h"></File>
 				<File RelativePath="..\panda\src\collide\config_collide.h"></File>
 				<File RelativePath="..\panda\src\collide\config_collide.h"></File>
-				<File RelativePath="..\panda\src\collide\collisionTube.h"></File>
+				<File RelativePath="..\panda\src\collide\collisionCapsule.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionSegment.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionSegment.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionBox.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionBox.h"></File>
 				<File RelativePath="..\panda\src\collide\collide_composite1.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collide_composite1.cxx"></File>
@@ -1054,7 +1054,7 @@
 				<File RelativePath="..\panda\src\collide\collisionNode.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionNode.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionGeom.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionGeom.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionSegment.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionSegment.I"></File>
-				<File RelativePath="..\panda\src\collide\collisionTube.cxx"></File>
+				<File RelativePath="..\panda\src\collide\collisionCapsule.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionHandlerQueue.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionHandlerQueue.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionParabola.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionParabola.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionPlane.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionPlane.cxx"></File>
@@ -1089,7 +1089,7 @@
 				<File RelativePath="..\panda\src\collide\collisionInvSphere.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionInvSphere.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionLine.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionLine.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionEntry.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionEntry.I"></File>
-				<File RelativePath="..\panda\src\collide\collisionTube.I"></File>
+				<File RelativePath="..\panda\src\collide\collisionCapsule.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionSolid.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionSolid.h"></File>
 				<File RelativePath="..\panda\src\collide\collisionPlane.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionPlane.I"></File>
 				<File RelativePath="..\panda\src\collide\collisionHandlerGravity.cxx"></File>
 				<File RelativePath="..\panda\src\collide\collisionHandlerGravity.cxx"></File>

+ 65 - 0
makepanda/makepandacore.py

@@ -3383,6 +3383,71 @@ def FindLocation(fn, ipath, pyabi=None):
     ORIG_EXT[loc] = ext
     ORIG_EXT[loc] = ext
     return loc
     return loc
 
 
+
+########################################################################
+##
+## These files maintain a python_versions.json file in the built/tmp
+## directory that can be used by the other scripts in this directory.
+##
+########################################################################
+
+
+def GetCurrentPythonVersionInfo():
+    if PkgSkip("PYTHON"):
+        return
+
+    from distutils.sysconfig import get_python_lib
+    return {
+        "version": SDK["PYTHONVERSION"][6:9],
+        "soabi": GetPythonABI(),
+        "ext_suffix": GetExtensionSuffix(),
+        "executable": sys.executable,
+        "purelib": get_python_lib(False),
+        "platlib": get_python_lib(True),
+    }
+
+
+def UpdatePythonVersionInfoFile(new_info):
+    import json
+
+    json_file = os.path.join(GetOutputDir(), "tmp", "python_versions.json")
+    json_data = []
+    if os.path.isfile(json_file) and not PkgSkip("PYTHON"):
+        try:
+            json_data = json.load(open(json_file, 'r'))
+        except:
+            json_data = []
+
+        # Prune the list by removing the entries that conflict with our build,
+        # plus the entries that no longer exist
+        for version_info in json_data[:]:
+            core_pyd = os.path.join(GetOutputDir(), "panda3d", "core" + version_info["ext_suffix"])
+            if version_info["ext_suffix"] == new_info["ext_suffix"] or \
+               version_info["soabi"] == new_info["soabi"] or \
+               not os.path.isfile(core_pyd):
+                json_data.remove(version_info)
+
+    if not PkgSkip("PYTHON"):
+        json_data.append(new_info)
+
+    if VERBOSE:
+        print("Writing %s" % (json_file))
+    json.dump(json_data, open(json_file, 'w'), indent=4)
+
+
+def ReadPythonVersionInfoFile():
+    import json
+
+    json_file = os.path.join(GetOutputDir(), "tmp", "python_versions.json")
+    if os.path.isfile(json_file):
+        try:
+            return json.load(open(json_file, 'r'))
+        except:
+            pass
+
+    return []
+
+
 ########################################################################
 ########################################################################
 ##
 ##
 ## TargetAdd
 ## TargetAdd

+ 6 - 6
panda/src/bullet/bulletBodyNode.cxx

@@ -28,7 +28,7 @@
 #include "collisionPlane.h"
 #include "collisionPlane.h"
 #include "collisionSphere.h"
 #include "collisionSphere.h"
 #include "collisionPolygon.h"
 #include "collisionPolygon.h"
-#include "collisionTube.h"
+#include "collisionCapsule.h"
 
 
 TypeHandle BulletBodyNode::_type_handle;
 TypeHandle BulletBodyNode::_type_handle;
 
 
@@ -813,12 +813,12 @@ add_shapes_from_collision_solids(CollisionNode *cnode) {
       do_add_shape(BulletBoxShape::make_from_solid(box), ts);
       do_add_shape(BulletBoxShape::make_from_solid(box), ts);
     }
     }
 
 
-    // CollisionTube
-    else if (CollisionTube::get_class_type() == type) {
-      CPT(CollisionTube) tube = DCAST(CollisionTube, solid);
-      CPT(TransformState) ts = TransformState::make_pos((tube->get_point_b() + tube->get_point_a()) / 2.0);
+    // CollisionCapsule
+    else if (CollisionCapsule::get_class_type() == type) {
+      CPT(CollisionCapsule) capsule = DCAST(CollisionCapsule, solid);
+      CPT(TransformState) ts = TransformState::make_pos((capsule->get_point_b() + capsule->get_point_a()) / 2.0);
 
 
-      do_add_shape(BulletCapsuleShape::make_from_solid(tube), ts);
+      do_add_shape(BulletCapsuleShape::make_from_solid(capsule), ts);
     }
     }
 
 
     // CollisionPlane
     // CollisionPlane

+ 5 - 5
panda/src/bullet/bulletCapsuleShape.cxx

@@ -87,16 +87,16 @@ ptr() const {
 
 
 /**
 /**
  * Constructs a new BulletCapsuleShape using the information from a
  * Constructs a new BulletCapsuleShape using the information from a
- * CollisionTube from the builtin collision system.
+ * CollisionCapsule from the builtin collision system.
  */
  */
 BulletCapsuleShape *BulletCapsuleShape::
 BulletCapsuleShape *BulletCapsuleShape::
-make_from_solid(const CollisionTube *solid) {
-  
+make_from_solid(const CollisionCapsule *solid) {
+
   PN_stdfloat radius = solid->get_radius();
   PN_stdfloat radius = solid->get_radius();
-  // Get tube's cylinder height: length from point A to point B
+  // Get capsule's cylinder height: length from point A to point B
   PN_stdfloat height = (solid->get_point_b() - solid->get_point_a()).length();
   PN_stdfloat height = (solid->get_point_b() - solid->get_point_a()).length();
 
 
-  // CollisionTubes are always Z-Up.
+  // CollisionCapsules are always Z-Up.
   return new BulletCapsuleShape(radius, height, Z_up);
   return new BulletCapsuleShape(radius, height, Z_up);
 }
 }
 
 

+ 2 - 2
panda/src/bullet/bulletCapsuleShape.h

@@ -20,7 +20,7 @@
 #include "bullet_utils.h"
 #include "bullet_utils.h"
 #include "bulletShape.h"
 #include "bulletShape.h"
 
 
-#include "collisionTube.h"
+#include "collisionCapsule.h"
 
 
 /**
 /**
  *
  *
@@ -35,7 +35,7 @@ PUBLISHED:
   BulletCapsuleShape(const BulletCapsuleShape &copy);
   BulletCapsuleShape(const BulletCapsuleShape &copy);
   INLINE ~BulletCapsuleShape();
   INLINE ~BulletCapsuleShape();
 
 
-  static BulletCapsuleShape *make_from_solid(const CollisionTube *solid);
+  static BulletCapsuleShape *make_from_solid(const CollisionCapsule *solid);
 
 
   INLINE PN_stdfloat get_radius() const;
   INLINE PN_stdfloat get_radius() const;
   INLINE PN_stdfloat get_half_height() const;
   INLINE PN_stdfloat get_half_height() const;

+ 8 - 0
panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h

@@ -20,6 +20,7 @@
 
 
 #import <AppKit/NSOpenGL.h>
 #import <AppKit/NSOpenGL.h>
 #import <OpenGL/OpenGL.h>
 #import <OpenGL/OpenGL.h>
+#import <CoreVideo/CoreVideo.h>
 
 
 /**
 /**
  * A tiny specialization on GLGraphicsStateGuardian to add some Cocoa-specific
  * A tiny specialization on GLGraphicsStateGuardian to add some Cocoa-specific
@@ -38,14 +39,21 @@ public:
                              CocoaGraphicsStateGuardian *share_with);
                              CocoaGraphicsStateGuardian *share_with);
 
 
   virtual ~CocoaGraphicsStateGuardian();
   virtual ~CocoaGraphicsStateGuardian();
+  bool setup_vsync();
 
 
   INLINE void lock_context();
   INLINE void lock_context();
   INLINE void unlock_context();
   INLINE void unlock_context();
 
 
   NSOpenGLContext *_share_context;
   NSOpenGLContext *_share_context;
   NSOpenGLContext *_context;
   NSOpenGLContext *_context;
+  NSOpenGLPixelFormat *_format = nullptr;
   FrameBufferProperties _fbprops;
   FrameBufferProperties _fbprops;
 
 
+  CVDisplayLinkRef _display_link = nullptr;
+  TrueMutexImpl _swap_lock;
+  TrueConditionVarImpl _swap_condition;
+  AtomicAdjust::Integer _last_wait_frame = 0;
+
 protected:
 protected:
   virtual void query_gl_version();
   virtual void query_gl_version();
   virtual void *do_get_extension_func(const char *name);
   virtual void *do_get_extension_func(const char *name);

+ 68 - 4
panda/src/cocoadisplay/cocoaGraphicsStateGuardian.mm

@@ -28,6 +28,21 @@
 #define NSAppKitVersionNumber10_7 1138
 #define NSAppKitVersionNumber10_7 1138
 #endif
 #endif
 
 
+/**
+ * Called whenever a display wants a frame.  The context argument contains the
+ * applicable CocoaGraphicsStateGuardian.
+ */
+static CVReturn
+display_link_cb(CVDisplayLinkRef link, const CVTimeStamp *now,
+                const CVTimeStamp* output_time, CVOptionFlags flags_in,
+                CVOptionFlags *flags_out, void *context) {
+  CocoaGraphicsStateGuardian *gsg = (CocoaGraphicsStateGuardian *)context;
+  gsg->_swap_lock.lock();
+  gsg->_swap_condition.notify();
+  gsg->_swap_lock.unlock();
+  return kCVReturnSuccess;
+}
+
 TypeHandle CocoaGraphicsStateGuardian::_type_handle;
 TypeHandle CocoaGraphicsStateGuardian::_type_handle;
 
 
 /**
 /**
@@ -36,7 +51,8 @@ TypeHandle CocoaGraphicsStateGuardian::_type_handle;
 CocoaGraphicsStateGuardian::
 CocoaGraphicsStateGuardian::
 CocoaGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
 CocoaGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
                            CocoaGraphicsStateGuardian *share_with) :
                            CocoaGraphicsStateGuardian *share_with) :
-  GLGraphicsStateGuardian(engine, pipe)
+  GLGraphicsStateGuardian(engine, pipe),
+  _swap_condition(_swap_lock)
 {
 {
   _share_context = nil;
   _share_context = nil;
   _context = nil;
   _context = nil;
@@ -52,12 +68,60 @@ CocoaGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
  */
  */
 CocoaGraphicsStateGuardian::
 CocoaGraphicsStateGuardian::
 ~CocoaGraphicsStateGuardian() {
 ~CocoaGraphicsStateGuardian() {
+  if (_format != nil) {
+    [_format release];
+  }
+  if (_display_link != nil) {
+    CVDisplayLinkRelease(_display_link);
+    _display_link = nil;
+    _swap_lock.lock();
+    _swap_condition.notify();
+    _swap_lock.unlock();
+  }
   if (_context != nil) {
   if (_context != nil) {
     [_context clearDrawable];
     [_context clearDrawable];
     [_context release];
     [_context release];
   }
   }
 }
 }
 
 
+/**
+ * Creates a CVDisplayLink, which tells us when the display the window is on
+ * will want a frame.
+ */
+bool CocoaGraphicsStateGuardian::
+setup_vsync() {
+  if (_display_link != nil) {
+    // Already set up.
+    return true;
+  }
+
+  CVReturn result = CVDisplayLinkCreateWithActiveCGDisplays(&_display_link);
+  if (result != kCVReturnSuccess) {
+    cocoadisplay_cat.error() << "Failed to create CVDisplayLink.\n";
+    return false;
+  }
+
+  result = CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_display_link, (CGLContextObj)[_context CGLContextObj], (CGLPixelFormatObj)[_format CGLPixelFormatObj]);
+  if (result != kCVReturnSuccess) {
+    cocoadisplay_cat.error() << "Failed to set CVDisplayLink's current display.\n";
+    return false;
+  }
+
+  result = CVDisplayLinkSetOutputCallback(_display_link, &display_link_cb, this);
+  if (result != kCVReturnSuccess) {
+    cocoadisplay_cat.error() << "Failed to set CVDisplayLink output callback.\n";
+    return false;
+  }
+
+  result = CVDisplayLinkStart(_display_link);
+  if (result != kCVReturnSuccess) {
+    cocoadisplay_cat.error() << "Failed to start the CVDisplayLink.\n";
+    return false;
+  }
+
+  return true;
+}
+
 /**
 /**
  * Gets the FrameBufferProperties to match the indicated config.
  * Gets the FrameBufferProperties to match the indicated config.
  */
  */
@@ -262,15 +326,15 @@ choose_pixel_format(const FrameBufferProperties &properties,
   // TODO: print out renderer
   // TODO: print out renderer
 
 
   _context = [[NSOpenGLContext alloc] initWithFormat:format shareContext:_share_context];
   _context = [[NSOpenGLContext alloc] initWithFormat:format shareContext:_share_context];
-  [format release];
+  _format = format;
   if (_context == nil) {
   if (_context == nil) {
     cocoadisplay_cat.error() <<
     cocoadisplay_cat.error() <<
       "Failed to create OpenGL context!\n";
       "Failed to create OpenGL context!\n";
     return;
     return;
   }
   }
 
 
-  // Set vsync setting on the context
-  GLint swap = sync_video ? 1 : 0;
+  // Disable vsync via the built-in mechanism, which doesn't work on Mojave
+  GLint swap = 0;
   [_context setValues:&swap forParameter:NSOpenGLCPSwapInterval];
   [_context setValues:&swap forParameter:NSOpenGLCPSwapInterval];
 
 
   cocoadisplay_cat.debug()
   cocoadisplay_cat.debug()

+ 3 - 0
panda/src/cocoadisplay/cocoaGraphicsWindow.h

@@ -23,6 +23,8 @@
 #import <AppKit/NSView.h>
 #import <AppKit/NSView.h>
 #import <AppKit/NSWindow.h>
 #import <AppKit/NSWindow.h>
 
 
+#import <CoreVideo/CoreVideo.h>
+
 /**
 /**
  * An interface to the Cocoa system for managing OpenGL windows under Mac OS
  * An interface to the Cocoa system for managing OpenGL windows under Mac OS
  * X.
  * X.
@@ -92,6 +94,7 @@ private:
   PT(GraphicsWindowInputDevice) _input;
   PT(GraphicsWindowInputDevice) _input;
   bool _mouse_hidden;
   bool _mouse_hidden;
   bool _context_needs_update;
   bool _context_needs_update;
+  bool _vsync_enabled = false;
 
 
 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
 #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   CGDisplayModeRef _fullscreen_mode;
   CGDisplayModeRef _fullscreen_mode;

+ 14 - 0
panda/src/cocoadisplay/cocoaGraphicsWindow.mm

@@ -274,6 +274,15 @@ end_flip() {
     CocoaGraphicsStateGuardian *cocoagsg;
     CocoaGraphicsStateGuardian *cocoagsg;
     DCAST_INTO_V(cocoagsg, _gsg);
     DCAST_INTO_V(cocoagsg, _gsg);
 
 
+    if (_vsync_enabled) {
+      AtomicAdjust::Integer cur_frame = ClockObject::get_global_clock()->get_frame_count();
+      if (AtomicAdjust::set(cocoagsg->_last_wait_frame, cur_frame) != cur_frame) {
+        cocoagsg->_swap_lock.lock();
+        cocoagsg->_swap_condition.wait();
+        cocoagsg->_swap_lock.unlock();
+      }
+    }
+
     cocoagsg->lock_context();
     cocoagsg->lock_context();
 
 
     // Swap the front and back buffer.
     // Swap the front and back buffer.
@@ -669,6 +678,8 @@ open_window() {
     mouse_mode_relative();
     mouse_mode_relative();
   }
   }
 
 
+  _vsync_enabled = sync_video && cocoagsg->setup_vsync();
+
   return true;
   return true;
 }
 }
 
 
@@ -710,6 +721,8 @@ close_window() {
     _view = nil;
     _view = nil;
   }
   }
 
 
+  _vsync_enabled = false;
+
   GraphicsWindow::close_window();
   GraphicsWindow::close_window();
 }
 }
 
 
@@ -1491,6 +1504,7 @@ handle_close_event() {
       cocoagsg->unlock_context();
       cocoagsg->unlock_context();
     }
     }
     _gsg.clear();
     _gsg.clear();
+    _vsync_enabled = false;
   }
   }
 
 
   // Dump the view, too
   // Dump the view, too

+ 10 - 10
panda/src/collide/collisionBox.cxx

@@ -16,7 +16,7 @@
 #include "collisionRay.h"
 #include "collisionRay.h"
 #include "collisionSphere.h"
 #include "collisionSphere.h"
 #include "collisionSegment.h"
 #include "collisionSegment.h"
-#include "collisionTube.h"
+#include "collisionCapsule.h"
 #include "collisionHandler.h"
 #include "collisionHandler.h"
 #include "collisionEntry.h"
 #include "collisionEntry.h"
 #include "config_collide.h"
 #include "config_collide.h"
@@ -547,19 +547,19 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
 }
 }
 
 
 /**
 /**
- * Double dispatch point for tube as a FROM object
+ * Double dispatch point for capsule as a FROM object
  */
  */
 PT(CollisionEntry) CollisionBox::
 PT(CollisionEntry) CollisionBox::
-test_intersection_from_tube(const CollisionEntry &entry) const {
-  const CollisionTube *tube;
-  DCAST_INTO_R(tube, entry.get_from(), nullptr);
+test_intersection_from_capsule(const CollisionEntry &entry) const {
+  const CollisionCapsule *capsule;
+  DCAST_INTO_R(capsule, entry.get_from(), nullptr);
 
 
   const LMatrix4 &wrt_mat = entry.get_wrt_mat();
   const LMatrix4 &wrt_mat = entry.get_wrt_mat();
 
 
-  LPoint3 from_a = tube->get_point_a() * wrt_mat;
-  LPoint3 from_b = tube->get_point_b() * wrt_mat;
+  LPoint3 from_a = capsule->get_point_a() * wrt_mat;
+  LPoint3 from_b = capsule->get_point_b() * wrt_mat;
   LVector3 from_direction = from_b - from_a;
   LVector3 from_direction = from_b - from_a;
-  PN_stdfloat radius_sq = wrt_mat.xform_vec(LVector3(0, 0, tube->get_radius())).length_squared();
+  PN_stdfloat radius_sq = wrt_mat.xform_vec(LVector3(0, 0, capsule->get_radius())).length_squared();
   PN_stdfloat radius = csqrt(radius_sq);
   PN_stdfloat radius = csqrt(radius_sq);
 
 
   LPoint3 box_min = get_min();
   LPoint3 box_min = get_min();
@@ -620,7 +620,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const {
       LVector3 delta(0);
       LVector3 delta(0);
       delta[edges[i].axis] = dimensions[edges[i].axis];
       delta[edges[i].axis] = dimensions[edges[i].axis];
       double u1, u2;
       double u1, u2;
-      CollisionTube::calc_closest_segment_points(u1, u2, from_a, from_direction, vertex, delta);
+      CollisionCapsule::calc_closest_segment_points(u1, u2, from_a, from_direction, vertex, delta);
       PN_stdfloat dist_sq = ((from_a + from_direction * u1) - (vertex + delta * u2)).length_squared();
       PN_stdfloat dist_sq = ((from_a + from_direction * u1) - (vertex + delta * u2)).length_squared();
       if (dist_sq < best_dist_sq) {
       if (dist_sq < best_dist_sq) {
         best_dist_sq = dist_sq;
         best_dist_sq = dist_sq;
@@ -682,7 +682,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const {
   new_entry->set_interior_point(point - interior_vec * radius);
   new_entry->set_interior_point(point - interior_vec * radius);
   new_entry->set_surface_point(surface_point);
   new_entry->set_surface_point(surface_point);
 
 
-  if (has_effective_normal() && tube->get_respect_effective_normal()) {
+  if (has_effective_normal() && capsule->get_respect_effective_normal()) {
     new_entry->set_surface_normal(get_effective_normal());
     new_entry->set_surface_normal(get_effective_normal());
   } else {
   } else {
     new_entry->set_surface_normal(normal);
     new_entry->set_surface_normal(normal);

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

@@ -82,7 +82,7 @@ protected:
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
     test_intersection_from_segment(const CollisionEntry &entry) const;
     test_intersection_from_segment(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
-    test_intersection_from_tube(const CollisionEntry &entry) const;
+    test_intersection_from_capsule(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
     test_intersection_from_box(const CollisionEntry &entry) const;
     test_intersection_from_box(const CollisionEntry &entry) const;
 
 

+ 19 - 19
panda/src/collide/collisionTube.I → panda/src/collide/collisionCapsule.I

@@ -6,7 +6,7 @@
  * license.  You should have received a copy of this license along
  * license.  You should have received a copy of this license along
  * with this source code in a file named "LICENSE."
  * with this source code in a file named "LICENSE."
  *
  *
- * @file collisionTube.I
+ * @file collisionCapsule.I
  * @author drose
  * @author drose
  * @date 2003-09-25
  * @date 2003-09-25
  */
  */
@@ -14,8 +14,8 @@
 /**
 /**
  *
  *
  */
  */
-INLINE CollisionTube::
-CollisionTube(const LPoint3 &a, const LPoint3 &b, PN_stdfloat radius) :
+INLINE CollisionCapsule::
+CollisionCapsule(const LPoint3 &a, const LPoint3 &b, PN_stdfloat radius) :
   _a(a), _b(b), _radius(radius)
   _a(a), _b(b), _radius(radius)
 {
 {
   recalc_internals();
   recalc_internals();
@@ -25,8 +25,8 @@ CollisionTube(const LPoint3 &a, const LPoint3 &b, PN_stdfloat radius) :
 /**
 /**
  *
  *
  */
  */
-INLINE CollisionTube::
-CollisionTube(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az,
+INLINE CollisionCapsule::
+CollisionCapsule(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az,
               PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz,
               PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz,
               PN_stdfloat radius) :
               PN_stdfloat radius) :
   _a(ax, ay, az), _b(bx, by, bz), _radius(radius)
   _a(ax, ay, az), _b(bx, by, bz), _radius(radius)
@@ -36,17 +36,17 @@ CollisionTube(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az,
 }
 }
 
 
 /**
 /**
- * Creates an invalid tube.  Only used when reading from a bam file.
+ * Creates an invalid capsule.  Only used when reading from a bam file.
  */
  */
-INLINE CollisionTube::
-CollisionTube() {
+INLINE CollisionCapsule::
+CollisionCapsule() {
 }
 }
 
 
 /**
 /**
  *
  *
  */
  */
-INLINE CollisionTube::
-CollisionTube(const CollisionTube &copy) :
+INLINE CollisionCapsule::
+CollisionCapsule(const CollisionCapsule &copy) :
   CollisionSolid(copy),
   CollisionSolid(copy),
   _a(copy._a),
   _a(copy._a),
   _b(copy._b),
   _b(copy._b),
@@ -58,7 +58,7 @@ CollisionTube(const CollisionTube &copy) :
 /**
 /**
  * Flushes the PStatCollectors used during traversal.
  * Flushes the PStatCollectors used during traversal.
  */
  */
-INLINE void CollisionTube::
+INLINE void CollisionCapsule::
 flush_level() {
 flush_level() {
   _volume_pcollector.flush_level();
   _volume_pcollector.flush_level();
   _test_pcollector.flush_level();
   _test_pcollector.flush_level();
@@ -67,7 +67,7 @@ flush_level() {
 /**
 /**
  *
  *
  */
  */
-INLINE void CollisionTube::
+INLINE void CollisionCapsule::
 set_point_a(const LPoint3 &a) {
 set_point_a(const LPoint3 &a) {
   _a = a;
   _a = a;
   recalc_internals();
   recalc_internals();
@@ -76,7 +76,7 @@ set_point_a(const LPoint3 &a) {
 /**
 /**
  *
  *
  */
  */
-INLINE void CollisionTube::
+INLINE void CollisionCapsule::
 set_point_a(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) {
 set_point_a(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) {
   set_point_a(LPoint3(x, y, z));
   set_point_a(LPoint3(x, y, z));
 }
 }
@@ -84,7 +84,7 @@ set_point_a(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) {
 /**
 /**
  *
  *
  */
  */
-INLINE const LPoint3 &CollisionTube::
+INLINE const LPoint3 &CollisionCapsule::
 get_point_a() const {
 get_point_a() const {
   return _a;
   return _a;
 }
 }
@@ -92,7 +92,7 @@ get_point_a() const {
 /**
 /**
  *
  *
  */
  */
-INLINE void CollisionTube::
+INLINE void CollisionCapsule::
 set_point_b(const LPoint3 &b) {
 set_point_b(const LPoint3 &b) {
   _b = b;
   _b = b;
   recalc_internals();
   recalc_internals();
@@ -101,7 +101,7 @@ set_point_b(const LPoint3 &b) {
 /**
 /**
  *
  *
  */
  */
-INLINE void CollisionTube::
+INLINE void CollisionCapsule::
 set_point_b(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) {
 set_point_b(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) {
   set_point_b(LPoint3(x, y, z));
   set_point_b(LPoint3(x, y, z));
 }
 }
@@ -109,7 +109,7 @@ set_point_b(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) {
 /**
 /**
  *
  *
  */
  */
-INLINE const LPoint3 &CollisionTube::
+INLINE const LPoint3 &CollisionCapsule::
 get_point_b() const {
 get_point_b() const {
   return _b;
   return _b;
 }
 }
@@ -117,7 +117,7 @@ get_point_b() const {
 /**
 /**
  *
  *
  */
  */
-INLINE void CollisionTube::
+INLINE void CollisionCapsule::
 set_radius(PN_stdfloat radius) {
 set_radius(PN_stdfloat radius) {
   nassertv(radius >= 0.0f);
   nassertv(radius >= 0.0f);
   _radius = radius;
   _radius = radius;
@@ -131,7 +131,7 @@ set_radius(PN_stdfloat radius) {
 /**
 /**
  *
  *
  */
  */
-INLINE PN_stdfloat CollisionTube::
+INLINE PN_stdfloat CollisionCapsule::
 get_radius() const {
 get_radius() const {
   return _radius;
   return _radius;
 }
 }

+ 74 - 74
panda/src/collide/collisionTube.cxx → panda/src/collide/collisionCapsule.cxx

@@ -6,12 +6,12 @@
  * license.  You should have received a copy of this license along
  * license.  You should have received a copy of this license along
  * with this source code in a file named "LICENSE."
  * with this source code in a file named "LICENSE."
  *
  *
- * @file collisionTube.cxx
+ * @file collisionCapsule.cxx
  * @author drose
  * @author drose
  * @date 2003-09-25
  * @date 2003-09-25
  */
  */
 
 
-#include "collisionTube.h"
+#include "collisionCapsule.h"
 #include "collisionSphere.h"
 #include "collisionSphere.h"
 #include "collisionLine.h"
 #include "collisionLine.h"
 #include "collisionRay.h"
 #include "collisionRay.h"
@@ -35,30 +35,30 @@
 #include "geomVertexWriter.h"
 #include "geomVertexWriter.h"
 #include "boundingSphere.h"
 #include "boundingSphere.h"
 
 
-PStatCollector CollisionTube::_volume_pcollector("Collision Volumes:CollisionTube");
-PStatCollector CollisionTube::_test_pcollector("Collision Tests:CollisionTube");
-TypeHandle CollisionTube::_type_handle;
+PStatCollector CollisionCapsule::_volume_pcollector("Collision Volumes:CollisionCapsule");
+PStatCollector CollisionCapsule::_test_pcollector("Collision Tests:CollisionCapsule");
+TypeHandle CollisionCapsule::_type_handle;
 
 
 /**
 /**
  *
  *
  */
  */
-CollisionSolid *CollisionTube::
+CollisionSolid *CollisionCapsule::
 make_copy() {
 make_copy() {
-  return new CollisionTube(*this);
+  return new CollisionCapsule(*this);
 }
 }
 
 
 /**
 /**
  *
  *
  */
  */
-PT(CollisionEntry) CollisionTube::
+PT(CollisionEntry) CollisionCapsule::
 test_intersection(const CollisionEntry &entry) const {
 test_intersection(const CollisionEntry &entry) const {
-  return entry.get_into()->test_intersection_from_tube(entry);
+  return entry.get_into()->test_intersection_from_capsule(entry);
 }
 }
 
 
 /**
 /**
  * Transforms the solid by the indicated matrix.
  * Transforms the solid by the indicated matrix.
  */
  */
-void CollisionTube::
+void CollisionCapsule::
 xform(const LMatrix4 &mat) {
 xform(const LMatrix4 &mat) {
   _a = _a * mat;
   _a = _a * mat;
   _b = _b * mat;
   _b = _b * mat;
@@ -77,7 +77,7 @@ xform(const LMatrix4 &mat) {
  * collision purposes.  The closest intersection point to this origin point is
  * collision purposes.  The closest intersection point to this origin point is
  * considered to be the most significant.
  * considered to be the most significant.
  */
  */
-LPoint3 CollisionTube::
+LPoint3 CollisionCapsule::
 get_collision_origin() const {
 get_collision_origin() const {
   return get_point_a();
   return get_point_a();
 }
 }
@@ -86,7 +86,7 @@ get_collision_origin() const {
  * Returns a PStatCollector that is used to count the number of bounding
  * Returns a PStatCollector that is used to count the number of bounding
  * volume tests made against a solid of this type in a given frame.
  * volume tests made against a solid of this type in a given frame.
  */
  */
-PStatCollector &CollisionTube::
+PStatCollector &CollisionCapsule::
 get_volume_pcollector() {
 get_volume_pcollector() {
   return _volume_pcollector;
   return _volume_pcollector;
 }
 }
@@ -95,7 +95,7 @@ get_volume_pcollector() {
  * Returns a PStatCollector that is used to count the number of intersection
  * Returns a PStatCollector that is used to count the number of intersection
  * tests made against a solid of this type in a given frame.
  * tests made against a solid of this type in a given frame.
  */
  */
-PStatCollector &CollisionTube::
+PStatCollector &CollisionCapsule::
 get_test_pcollector() {
 get_test_pcollector() {
   return _test_pcollector;
   return _test_pcollector;
 }
 }
@@ -103,15 +103,15 @@ get_test_pcollector() {
 /**
 /**
  *
  *
  */
  */
-void CollisionTube::
+void CollisionCapsule::
 output(std::ostream &out) const {
 output(std::ostream &out) const {
-  out << "tube, a (" << _a << "), b (" << _b << "), r " << _radius;
+  out << "capsule, a (" << _a << "), b (" << _b << "), r " << _radius;
 }
 }
 
 
 /**
 /**
  *
  *
  */
  */
-PT(BoundingVolume) CollisionTube::
+PT(BoundingVolume) CollisionCapsule::
 compute_internal_bounds() const {
 compute_internal_bounds() const {
   PT(BoundingVolume) bound = CollisionSolid::compute_internal_bounds();
   PT(BoundingVolume) bound = CollisionSolid::compute_internal_bounds();
 
 
@@ -143,7 +143,7 @@ compute_internal_bounds() const {
 /**
 /**
  *
  *
  */
  */
-PT(CollisionEntry) CollisionTube::
+PT(CollisionEntry) CollisionCapsule::
 test_intersection_from_sphere(const CollisionEntry &entry) const {
 test_intersection_from_sphere(const CollisionEntry &entry) const {
   const CollisionSphere *sphere;
   const CollisionSphere *sphere;
   DCAST_INTO_R(sphere, entry.get_from(), nullptr);
   DCAST_INTO_R(sphere, entry.get_from(), nullptr);
@@ -160,7 +160,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   PN_stdfloat actual_t = 0.0f;
   PN_stdfloat actual_t = 0.0f;
 
 
   if (wrt_prev_space != wrt_space) {
   if (wrt_prev_space != wrt_space) {
-    // If the sphere is moving relative to the tube, it becomes a tube itself.
+    // If the sphere is moving relative to the capsule, it becomes a capsule itself.
     from_a = sphere->get_center() * wrt_prev_space->get_mat();
     from_a = sphere->get_center() * wrt_prev_space->get_mat();
   }
   }
 
 
@@ -195,11 +195,11 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
 
 
   LPoint3 into_intersection_point;
   LPoint3 into_intersection_point;
   if (t2 > 1.0) {
   if (t2 > 1.0) {
-    // Point b is within the tube.  The first intersection point is point b
+    // Point b is within the capsule.  The first intersection point is point b
     // itself.
     // itself.
     into_intersection_point = from_b;
     into_intersection_point = from_b;
   } else {
   } else {
-    // Point b is outside the tube, and point a is either inside the tube or
+    // Point b is outside the capsule, and point a is either inside the capsule or
     // beyond it.  The first intersection point is at t2.
     // beyond it.  The first intersection point is at t2.
     into_intersection_point = from_a + t2 * from_direction;
     into_intersection_point = from_a + t2 * from_direction;
   }
   }
@@ -221,7 +221,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
 /**
 /**
  *
  *
  */
  */
-PT(CollisionEntry) CollisionTube::
+PT(CollisionEntry) CollisionCapsule::
 test_intersection_from_line(const CollisionEntry &entry) const {
 test_intersection_from_line(const CollisionEntry &entry) const {
   const CollisionLine *line;
   const CollisionLine *line;
   DCAST_INTO_R(line, entry.get_from(), nullptr);
   DCAST_INTO_R(line, entry.get_from(), nullptr);
@@ -269,7 +269,7 @@ test_intersection_from_line(const CollisionEntry &entry) const {
 /**
 /**
  *
  *
  */
  */
-PT(CollisionEntry) CollisionTube::
+PT(CollisionEntry) CollisionCapsule::
 test_intersection_from_ray(const CollisionEntry &entry) const {
 test_intersection_from_ray(const CollisionEntry &entry) const {
   const CollisionRay *ray;
   const CollisionRay *ray;
   DCAST_INTO_R(ray, entry.get_from(), nullptr);
   DCAST_INTO_R(ray, entry.get_from(), nullptr);
@@ -299,11 +299,11 @@ test_intersection_from_ray(const CollisionEntry &entry) const {
 
 
   LPoint3 into_intersection_point;
   LPoint3 into_intersection_point;
   if (t1 < 0.0) {
   if (t1 < 0.0) {
-    // Point a is within the tube.  The first intersection point is point a
+    // Point a is within the capsule.  The first intersection point is point a
     // itself.
     // itself.
     into_intersection_point = from_origin;
     into_intersection_point = from_origin;
   } else {
   } else {
-    // Point a is outside the tube.  The first intersection point is at t1.
+    // Point a is outside the capsule.  The first intersection point is at t1.
     into_intersection_point = from_origin + t1 * from_direction;
     into_intersection_point = from_origin + t1 * from_direction;
   }
   }
   set_intersection_point(new_entry, into_intersection_point, 0.0);
   set_intersection_point(new_entry, into_intersection_point, 0.0);
@@ -330,7 +330,7 @@ test_intersection_from_ray(const CollisionEntry &entry) const {
 /**
 /**
  *
  *
  */
  */
-PT(CollisionEntry) CollisionTube::
+PT(CollisionEntry) CollisionCapsule::
 test_intersection_from_segment(const CollisionEntry &entry) const {
 test_intersection_from_segment(const CollisionEntry &entry) const {
   const CollisionSegment *segment;
   const CollisionSegment *segment;
   DCAST_INTO_R(segment, entry.get_from(), nullptr);
   DCAST_INTO_R(segment, entry.get_from(), nullptr);
@@ -362,11 +362,11 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
 
 
   LPoint3 into_intersection_point;
   LPoint3 into_intersection_point;
   if (t1 < 0.0) {
   if (t1 < 0.0) {
-    // Point a is within the tube.  The first intersection point is point a
+    // Point a is within the capsule.  The first intersection point is point a
     // itself.
     // itself.
     into_intersection_point = from_a;
     into_intersection_point = from_a;
   } else {
   } else {
-    // Point a is outside the tube, and point b is either inside the tube or
+    // Point a is outside the capsule, and point b is either inside the capsule or
     // beyond it.  The first intersection point is at t1.
     // beyond it.  The first intersection point is at t1.
     into_intersection_point = from_a + t1 * from_direction;
     into_intersection_point = from_a + t1 * from_direction;
   }
   }
@@ -394,22 +394,22 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
 /**
 /**
  *
  *
  */
  */
-PT(CollisionEntry) CollisionTube::
-test_intersection_from_tube(const CollisionEntry &entry) const {
-  const CollisionTube *tube;
-  DCAST_INTO_R(tube, entry.get_from(), nullptr);
+PT(CollisionEntry) CollisionCapsule::
+test_intersection_from_capsule(const CollisionEntry &entry) const {
+  const CollisionCapsule *capsule;
+  DCAST_INTO_R(capsule, entry.get_from(), nullptr);
 
 
   LPoint3 into_a = _a;
   LPoint3 into_a = _a;
   LVector3 into_direction = _b - into_a;
   LVector3 into_direction = _b - into_a;
 
 
   const LMatrix4 &wrt_mat = entry.get_wrt_mat();
   const LMatrix4 &wrt_mat = entry.get_wrt_mat();
 
 
-  LPoint3 from_a = tube->get_point_a() * wrt_mat;
-  LPoint3 from_b = tube->get_point_b() * wrt_mat;
+  LPoint3 from_a = capsule->get_point_a() * wrt_mat;
+  LPoint3 from_b = capsule->get_point_b() * wrt_mat;
   LVector3 from_direction = from_b - from_a;
   LVector3 from_direction = from_b - from_a;
 
 
   LVector3 from_radius_v =
   LVector3 from_radius_v =
-    LVector3(tube->get_radius(), 0.0f, 0.0f) * wrt_mat;
+    LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat;
   PN_stdfloat from_radius = length(from_radius_v);
   PN_stdfloat from_radius = length(from_radius_v);
 
 
   // Determine the points on each segment with the smallest distance between.
   // Determine the points on each segment with the smallest distance between.
@@ -420,7 +420,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const {
   LPoint3 into_closest = into_a + into_direction * into_t;
   LPoint3 into_closest = into_a + into_direction * into_t;
   LPoint3 from_closest = from_a + from_direction * from_t;
   LPoint3 from_closest = from_a + from_direction * from_t;
 
 
-  // If the distance is greater than the sum of tube radii, the test fails.
+  // If the distance is greater than the sum of capsule radii, the test fails.
   LVector3 closest_vec = from_closest - into_closest;
   LVector3 closest_vec = from_closest - into_closest;
   PN_stdfloat distance = closest_vec.length();
   PN_stdfloat distance = closest_vec.length();
   if (distance > _radius + from_radius) {
   if (distance > _radius + from_radius) {
@@ -442,7 +442,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const {
     new_entry->set_surface_point(into_closest + surface_normal * _radius);
     new_entry->set_surface_point(into_closest + surface_normal * _radius);
     new_entry->set_interior_point(from_closest - surface_normal * from_radius);
     new_entry->set_interior_point(from_closest - surface_normal * from_radius);
 
 
-    if (has_effective_normal() && tube->get_respect_effective_normal()) {
+    if (has_effective_normal() && capsule->get_respect_effective_normal()) {
       new_entry->set_surface_normal(get_effective_normal());
       new_entry->set_surface_normal(get_effective_normal());
     } else if (distance != 0) {
     } else if (distance != 0) {
       new_entry->set_surface_normal(surface_normal);
       new_entry->set_surface_normal(surface_normal);
@@ -458,7 +458,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const {
 /**
 /**
  *
  *
  */
  */
-PT(CollisionEntry) CollisionTube::
+PT(CollisionEntry) CollisionCapsule::
 test_intersection_from_parabola(const CollisionEntry &entry) const {
 test_intersection_from_parabola(const CollisionEntry &entry) const {
   const CollisionParabola *parabola;
   const CollisionParabola *parabola;
   DCAST_INTO_R(parabola, entry.get_from(), nullptr);
   DCAST_INTO_R(parabola, entry.get_from(), nullptr);
@@ -510,14 +510,14 @@ test_intersection_from_parabola(const CollisionEntry &entry) const {
  * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
  * Fills the _viz_geom GeomNode up with Geoms suitable for rendering this
  * solid.
  * solid.
  */
  */
-void CollisionTube::
+void CollisionCapsule::
 fill_viz_geom() {
 fill_viz_geom() {
   if (collide_cat.is_debug()) {
   if (collide_cat.is_debug()) {
     collide_cat.debug()
     collide_cat.debug()
       << "Recomputing viz for " << *this << "\n";
       << "Recomputing viz for " << *this << "\n";
   }
   }
 
 
-  // Generate the vertices such that we draw a tube with one endpoint at (0,
+  // Generate the vertices such that we draw a capsule with one endpoint at (0,
   // 0, 0), and another at (0, length, 0).  Then we'll rotate and translate it
   // 0, 0), and another at (0, length, 0).  Then we'll rotate and translate it
   // into place with the appropriate look_at matrix.
   // into place with the appropriate look_at matrix.
   LVector3 direction = (_b - _a);
   LVector3 direction = (_b - _a);
@@ -576,9 +576,9 @@ fill_viz_geom() {
 
 
 /**
 /**
  * Should be called internally to recompute the matrix and length when the
  * Should be called internally to recompute the matrix and length when the
- * properties of the tube have changed.
+ * properties of the capsule have changed.
  */
  */
-void CollisionTube::
+void CollisionCapsule::
 recalc_internals() {
 recalc_internals() {
   LVector3 direction = (_b - _a);
   LVector3 direction = (_b - _a);
   _length = direction.length();
   _length = direction.length();
@@ -595,7 +595,7 @@ recalc_internals() {
  * Calculates a particular vertex on the surface of the first endcap
  * Calculates a particular vertex on the surface of the first endcap
  * hemisphere, for use in generating the viz geometry.
  * hemisphere, for use in generating the viz geometry.
  */
  */
-LVertex CollisionTube::
+LVertex CollisionCapsule::
 calc_sphere1_vertex(int ri, int si, int num_rings, int num_slices) {
 calc_sphere1_vertex(int ri, int si, int num_rings, int num_slices) {
   PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings;
   PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings;
   PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
   PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
@@ -620,7 +620,7 @@ calc_sphere1_vertex(int ri, int si, int num_rings, int num_slices) {
  * Calculates a particular vertex on the surface of the second endcap
  * Calculates a particular vertex on the surface of the second endcap
  * hemisphere, for use in generating the viz geometry.
  * hemisphere, for use in generating the viz geometry.
  */
  */
-LVertex CollisionTube::
+LVertex CollisionCapsule::
 calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices,
 calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices,
                     PN_stdfloat length) {
                     PN_stdfloat length) {
   PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings;
   PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings;
@@ -646,7 +646,7 @@ calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices,
  * Given line segments s1 and s2 defined by two points each, computes the
  * Given line segments s1 and s2 defined by two points each, computes the
  * point on each segment with the closest distance between them.
  * point on each segment with the closest distance between them.
  */
  */
-void CollisionTube::
+void CollisionCapsule::
 calc_closest_segment_points(double &t1, double &t2,
 calc_closest_segment_points(double &t1, double &t2,
                             const LPoint3 &from1, const LVector3 &delta1,
                             const LPoint3 &from1, const LVector3 &delta1,
                             const LPoint3 &from2, const LVector3 &delta2) {
                             const LPoint3 &from2, const LVector3 &delta2) {
@@ -717,18 +717,18 @@ calc_closest_segment_points(double &t1, double &t2,
 }
 }
 
 
 /**
 /**
- * Determine the point(s) of intersection of a parametric line with the tube.
+ * Determine the point(s) of intersection of a parametric line with the capsule.
  * The line is infinite in both directions, and passes through "from" and
  * The line is infinite in both directions, and passes through "from" and
- * from+delta.  If the line does not intersect the tube, the function returns
- * false, and t1 and t2 are undefined.  If it does intersect the tube, it
+ * from+delta.  If the line does not intersect the capsule, the function returns
+ * false, and t1 and t2 are undefined.  If it does intersect the capsule, it
  * returns true, and t1 and t2 are set to the points along the equation
  * returns true, and t1 and t2 are set to the points along the equation
  * from+t*delta that correspond to the two points of intersection.
  * from+t*delta that correspond to the two points of intersection.
  */
  */
-bool CollisionTube::
+bool CollisionCapsule::
 intersects_line(double &t1, double &t2,
 intersects_line(double &t1, double &t2,
                 const LPoint3 &from0, const LVector3 &delta0,
                 const LPoint3 &from0, const LVector3 &delta0,
                 PN_stdfloat inflate_radius) const {
                 PN_stdfloat inflate_radius) const {
-  // Convert the line into our canonical coordinate space: the tube is aligned
+  // Convert the line into our canonical coordinate space: the capsule is aligned
   // with the y axis.
   // with the y axis.
   LPoint3 from = from0 * _inv_mat;
   LPoint3 from = from0 * _inv_mat;
   LVector3 delta = delta0 * _inv_mat;
   LVector3 delta = delta0 * _inv_mat;
@@ -776,7 +776,7 @@ intersects_line(double &t1, double &t2,
         }
         }
       }
       }
 
 
-      // The point is within the tube!
+      // The point is within the capsule!
       t1 = t2 = 0.0;
       t1 = t2 = 0.0;
       return true;
       return true;
     }
     }
@@ -819,15 +819,15 @@ intersects_line(double &t1, double &t2,
   PN_stdfloat t2_y = from[1] + t2 * delta[1];
   PN_stdfloat t2_y = from[1] + t2 * delta[1];
 
 
   if (t1_y < -radius && t2_y < -radius) {
   if (t1_y < -radius && t2_y < -radius) {
-    // Both points are way off the bottom of the tube; no intersection.
+    // Both points are way off the bottom of the capsule; no intersection.
     return false;
     return false;
   } else if (t1_y > _length + radius && t2_y > _length + radius) {
   } else if (t1_y > _length + radius && t2_y > _length + radius) {
-    // Both points are way off the top of the tube; no intersection.
+    // Both points are way off the top of the capsule; no intersection.
     return false;
     return false;
   }
   }
 
 
   if (t1_y < 0.0f) {
   if (t1_y < 0.0f) {
-    // The starting point is off the bottom of the tube.  Test the line
+    // The starting point is off the bottom of the capsule.  Test the line
     // against the first endcap.
     // against the first endcap.
     double t1a, t2a;
     double t1a, t2a;
     if (!sphere_intersects_line(t1a, t2a, 0.0f, from, delta, radius)) {
     if (!sphere_intersects_line(t1a, t2a, 0.0f, from, delta, radius)) {
@@ -838,7 +838,7 @@ intersects_line(double &t1, double &t2,
     t1 = t1a;
     t1 = t1a;
 
 
   } else if (t1_y > _length) {
   } else if (t1_y > _length) {
-    // The starting point is off the top of the tube.  Test the line against
+    // The starting point is off the top of the capsule.  Test the line against
     // the second endcap.
     // the second endcap.
     double t1b, t2b;
     double t1b, t2b;
     if (!sphere_intersects_line(t1b, t2b, _length, from, delta, radius)) {
     if (!sphere_intersects_line(t1b, t2b, _length, from, delta, radius)) {
@@ -850,7 +850,7 @@ intersects_line(double &t1, double &t2,
   }
   }
 
 
   if (t2_y < 0.0f) {
   if (t2_y < 0.0f) {
-    // The ending point is off the bottom of the tube.  Test the line against
+    // The ending point is off the bottom of the capsule.  Test the line against
     // the first endcap.
     // the first endcap.
     double t1a, t2a;
     double t1a, t2a;
     if (!sphere_intersects_line(t1a, t2a, 0.0f, from, delta, radius)) {
     if (!sphere_intersects_line(t1a, t2a, 0.0f, from, delta, radius)) {
@@ -861,7 +861,7 @@ intersects_line(double &t1, double &t2,
     t2 = t2a;
     t2 = t2a;
 
 
   } else if (t2_y > _length) {
   } else if (t2_y > _length) {
-    // The ending point is off the top of the tube.  Test the line against the
+    // The ending point is off the top of the capsule.  Test the line against the
     // second endcap.
     // second endcap.
     double t1b, t2b;
     double t1b, t2b;
     if (!sphere_intersects_line(t1b, t2b, _length, from, delta, radius)) {
     if (!sphere_intersects_line(t1b, t2b, _length, from, delta, radius)) {
@@ -880,7 +880,7 @@ intersects_line(double &t1, double &t2,
  * whether it intersects one or the other endcaps.  The y parameter specifies
  * whether it intersects one or the other endcaps.  The y parameter specifies
  * the center of the sphere (and hence the particular endcap).
  * the center of the sphere (and hence the particular endcap).
  */
  */
-bool CollisionTube::
+bool CollisionCapsule::
 sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y,
 sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y,
                        const LPoint3 &from, const LVector3 &delta,
                        const LPoint3 &from, const LVector3 &delta,
                        PN_stdfloat radius) {
                        PN_stdfloat radius) {
@@ -917,14 +917,14 @@ sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y,
 }
 }
 
 
 /**
 /**
- * Determine a point of intersection of a parametric parabola with the tube.
+ * Determine a point of intersection of a parametric parabola with the capsule.
  *
  *
  * We only consider the segment of the parabola between t1 and t2, which has
  * We only consider the segment of the parabola between t1 and t2, which has
  * already been computed as corresponding to points p1 and p2.  If there is an
  * already been computed as corresponding to points p1 and p2.  If there is an
  * intersection, t is set to the parametric point of intersection, and true is
  * intersection, t is set to the parametric point of intersection, and true is
  * returned; otherwise, false is returned.
  * returned; otherwise, false is returned.
  */
  */
-bool CollisionTube::
+bool CollisionCapsule::
 intersects_parabola(double &t, const LParabola &parabola,
 intersects_parabola(double &t, const LParabola &parabola,
                     double t1, double t2,
                     double t1, double t2,
                     const LPoint3 &p1, const LPoint3 &p2) const {
                     const LPoint3 &p1, const LPoint3 &p2) const {
@@ -965,11 +965,11 @@ intersects_parabola(double &t, const LParabola &parabola,
 }
 }
 
 
 /**
 /**
- * Calculates a point that is exactly on the surface of the tube and its
+ * Calculates a point that is exactly on the surface of the capsule and its
  * corresponding normal, given a point that is supposedly on the surface of
  * corresponding normal, given a point that is supposedly on the surface of
- * the tube.
+ * the capsule.
  */
  */
-void CollisionTube::
+void CollisionCapsule::
 calculate_surface_point_and_normal(const LPoint3 &surface_point,
 calculate_surface_point_and_normal(const LPoint3 &surface_point,
                                    double extra_radius,
                                    double extra_radius,
                                    LPoint3 &result_point,
                                    LPoint3 &result_point,
@@ -1015,7 +1015,7 @@ calculate_surface_point_and_normal(const LPoint3 &surface_point,
  * point in the CollisionEntry, and also compute the relevant normal based on
  * point in the CollisionEntry, and also compute the relevant normal based on
  * that point.
  * that point.
  */
  */
-void CollisionTube::
+void CollisionCapsule::
 set_intersection_point(CollisionEntry *new_entry,
 set_intersection_point(CollisionEntry *new_entry,
                        const LPoint3 &into_intersection_point,
                        const LPoint3 &into_intersection_point,
                        double extra_radius) const {
                        double extra_radius) const {
@@ -1033,16 +1033,16 @@ set_intersection_point(CollisionEntry *new_entry,
 
 
   new_entry->set_surface_normal(normal);
   new_entry->set_surface_normal(normal);
   new_entry->set_surface_point(point);
   new_entry->set_surface_point(point);
-  // Also adjust the original point into the tube by the amount of
-  // extra_radius, which should put it on the surface of the tube if our
+  // Also adjust the original point into the capsule by the amount of
+  // extra_radius, which should put it on the surface of the capsule if our
   // collision was tangential.
   // collision was tangential.
   new_entry->set_interior_point(into_intersection_point - normal * extra_radius);
   new_entry->set_interior_point(into_intersection_point - normal * extra_radius);
 }
 }
 
 
 /**
 /**
- * Tells the BamReader how to create objects of type CollisionTube.
+ * Tells the BamReader how to create objects of type CollisionCapsule.
  */
  */
-void CollisionTube::
+void CollisionCapsule::
 register_with_read_factory() {
 register_with_read_factory() {
   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
 }
 }
@@ -1051,7 +1051,7 @@ register_with_read_factory() {
  * Writes the contents of this object to the datagram for shipping out to a
  * Writes the contents of this object to the datagram for shipping out to a
  * Bam file.
  * Bam file.
  */
  */
-void CollisionTube::
+void CollisionCapsule::
 write_datagram(BamWriter *manager, Datagram &dg) {
 write_datagram(BamWriter *manager, Datagram &dg) {
   CollisionSolid::write_datagram(manager, dg);
   CollisionSolid::write_datagram(manager, dg);
   _a.write_datagram(dg);
   _a.write_datagram(dg);
@@ -1061,12 +1061,12 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 
 
 /**
 /**
  * This function is called by the BamReader's factory when a new object of
  * This function is called by the BamReader's factory when a new object of
- * type CollisionTube is encountered in the Bam file.  It should create the
- * CollisionTube and extract its information from the file.
+ * type CollisionCapsule is encountered in the Bam file.  It should create the
+ * CollisionCapsule and extract its information from the file.
  */
  */
-TypedWritable *CollisionTube::
+TypedWritable *CollisionCapsule::
 make_from_bam(const FactoryParams &params) {
 make_from_bam(const FactoryParams &params) {
-  CollisionTube *node = new CollisionTube();
+  CollisionCapsule *node = new CollisionCapsule();
   DatagramIterator scan;
   DatagramIterator scan;
   BamReader *manager;
   BamReader *manager;
 
 
@@ -1078,9 +1078,9 @@ make_from_bam(const FactoryParams &params) {
 
 
 /**
 /**
  * This internal function is called by make_from_bam to read in all of the
  * This internal function is called by make_from_bam to read in all of the
- * relevant data from the BamFile for the new CollisionTube.
+ * relevant data from the BamFile for the new CollisionCapsule.
  */
  */
-void CollisionTube::
+void CollisionCapsule::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   CollisionSolid::fillin(scan, manager);
   CollisionSolid::fillin(scan, manager);
   _a.read_datagram(scan);
   _a.read_datagram(scan);

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

@@ -0,0 +1,161 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file collisionCapsule.h
+ * @author drose
+ * @date 2003-09-25
+ */
+
+#ifndef COLLISIONCAPSULE_H
+#define COLLISIONCAPSULE_H
+
+#include "pandabase.h"
+#include "collisionSolid.h"
+#include "parabola.h"
+
+/**
+ * This implements a solid consisting of a cylinder with hemispherical endcaps,
+ * also known as a capsule or a spherocylinder.
+ *
+ * This shape was previously erroneously called CollisionTube.
+ */
+class EXPCL_PANDA_COLLIDE CollisionCapsule : public CollisionSolid {
+PUBLISHED:
+  INLINE explicit CollisionCapsule(const LPoint3 &a, const LPoint3 &db,
+                                   PN_stdfloat radius);
+  INLINE explicit CollisionCapsule(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az,
+                                   PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz,
+                                   PN_stdfloat radius);
+
+  virtual LPoint3 get_collision_origin() const;
+
+private:
+  INLINE CollisionCapsule();
+
+public:
+  INLINE CollisionCapsule(const CollisionCapsule &copy);
+  virtual CollisionSolid *make_copy();
+
+  virtual PT(CollisionEntry)
+  test_intersection(const CollisionEntry &entry) const;
+
+  virtual void xform(const LMatrix4 &mat);
+
+  virtual PStatCollector &get_volume_pcollector();
+  virtual PStatCollector &get_test_pcollector();
+
+  virtual void output(std::ostream &out) const;
+
+  INLINE static void flush_level();
+
+PUBLISHED:
+  INLINE void set_point_a(const LPoint3 &a);
+  INLINE void set_point_a(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z);
+  INLINE const LPoint3 &get_point_a() const;
+
+  INLINE void set_point_b(const LPoint3 &b);
+  INLINE void set_point_b(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z);
+  INLINE const LPoint3 &get_point_b() const;
+
+  INLINE void set_radius(PN_stdfloat radius);
+  INLINE PN_stdfloat get_radius() const;
+
+PUBLISHED:
+  MAKE_PROPERTY(point_a, get_point_a, set_point_a);
+  MAKE_PROPERTY(point_b, get_point_b, set_point_b);
+  MAKE_PROPERTY(radius, get_radius, set_radius);
+
+protected:
+  virtual PT(BoundingVolume) compute_internal_bounds() const;
+
+protected:
+  virtual PT(CollisionEntry)
+  test_intersection_from_sphere(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_line(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_ray(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_segment(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_capsule(const CollisionEntry &entry) const;
+  virtual PT(CollisionEntry)
+  test_intersection_from_parabola(const CollisionEntry &entry) const;
+
+  virtual void fill_viz_geom();
+
+private:
+  void recalc_internals();
+
+  LVertex calc_sphere1_vertex(int ri, int si, int num_rings, int num_slices);
+  LVertex calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices,
+                              PN_stdfloat length);
+
+  static void calc_closest_segment_points(double &t1, double &t2,
+                                          const LPoint3 &from1, const LVector3 &delta1,
+                                          const LPoint3 &from2, const LVector3 &delta2);
+  bool intersects_line(double &t1, double &t2,
+                       const LPoint3 &from, const LVector3 &delta,
+                       PN_stdfloat inflate_radius) const;
+  static bool sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y,
+                                     const LPoint3 &from, const LVector3 &delta,
+                                     PN_stdfloat radius);
+  bool intersects_parabola(double &t, const LParabola &parabola,
+                           double t1, double t2,
+                           const LPoint3 &p1, const LPoint3 &p2) const;
+  void calculate_surface_point_and_normal(const LPoint3 &surface_point,
+                                          double extra_radius,
+                                          LPoint3 &result_point,
+                                          LVector3 &result_normal) const;
+  void set_intersection_point(CollisionEntry *new_entry,
+                              const LPoint3 &into_intersection_point,
+                              double extra_radius) const;
+
+private:
+  LPoint3 _a, _b;
+  PN_stdfloat _radius;
+
+  // These are derived from the above.
+  LMatrix4 _mat;
+  LMatrix4 _inv_mat;
+  PN_stdfloat _length;
+
+  static PStatCollector _volume_pcollector;
+  static PStatCollector _test_pcollector;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CollisionSolid::init_type();
+    register_type(_type_handle, "CollisionCapsule",
+                  CollisionSolid::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class CollisionBox;
+};
+
+#include "collisionCapsule.I"
+
+#endif

+ 1 - 1
panda/src/collide/collisionHandlerFluidPusher.cxx

@@ -72,7 +72,7 @@ handle_entries() {
     PosB = collider's current position
     PosB = collider's current position
     M = movement vector (PosB - PosA)
     M = movement vector (PosB - PosA)
     BV = bounding sphere that includes collider at PosA and PosB
     BV = bounding sphere that includes collider at PosA and PosB
-    CS = 'collision set', all 'collidables' within BV (collision polys, tubes, etc)
+    CS = 'collision set', all 'collidables' within BV (collision polys, capsules, etc)
 
 
     VARIABLES
     VARIABLES
     N = movement vector since most recent collision (or start of frame)
     N = movement vector since most recent collision (or start of frame)

+ 11 - 11
panda/src/collide/collisionPlane.cxx

@@ -18,7 +18,7 @@
 #include "collisionLine.h"
 #include "collisionLine.h"
 #include "collisionRay.h"
 #include "collisionRay.h"
 #include "collisionSegment.h"
 #include "collisionSegment.h"
-#include "collisionTube.h"
+#include "collisionCapsule.h"
 #include "collisionParabola.h"
 #include "collisionParabola.h"
 #include "config_collide.h"
 #include "config_collide.h"
 #include "pointerToArray.h"
 #include "pointerToArray.h"
@@ -298,16 +298,16 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
  *
  *
  */
  */
 PT(CollisionEntry) CollisionPlane::
 PT(CollisionEntry) CollisionPlane::
-test_intersection_from_tube(const CollisionEntry &entry) const {
-  const CollisionTube *tube;
-  DCAST_INTO_R(tube, entry.get_from(), nullptr);
+test_intersection_from_capsule(const CollisionEntry &entry) const {
+  const CollisionCapsule *capsule;
+  DCAST_INTO_R(capsule, entry.get_from(), nullptr);
 
 
   const LMatrix4 &wrt_mat = entry.get_wrt_mat();
   const LMatrix4 &wrt_mat = entry.get_wrt_mat();
 
 
-  LPoint3 from_a = tube->get_point_a() * wrt_mat;
-  LPoint3 from_b = tube->get_point_b() * wrt_mat;
+  LPoint3 from_a = capsule->get_point_a() * wrt_mat;
+  LPoint3 from_b = capsule->get_point_b() * wrt_mat;
   LVector3 from_radius_v =
   LVector3 from_radius_v =
-    LVector3(tube->get_radius(), 0.0f, 0.0f) * wrt_mat;
+    LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat;
   PN_stdfloat from_radius = length(from_radius_v);
   PN_stdfloat from_radius = length(from_radius_v);
 
 
   PN_stdfloat dist_a = _plane.dist_to_plane(from_a);
   PN_stdfloat dist_a = _plane.dist_to_plane(from_a);
@@ -325,7 +325,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const {
   }
   }
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
 
 
-  LVector3 normal = (has_effective_normal() && tube->get_respect_effective_normal()) ? get_effective_normal() : get_normal();
+  LVector3 normal = (has_effective_normal() && capsule->get_respect_effective_normal()) ? get_effective_normal() : get_normal();
   new_entry->set_surface_normal(normal);
   new_entry->set_surface_normal(normal);
 
 
   PN_stdfloat t;
   PN_stdfloat t;
@@ -339,17 +339,17 @@ test_intersection_from_tube(const CollisionEntry &entry) const {
       new_entry->set_surface_point(from_a - get_normal() * dist_a);
       new_entry->set_surface_point(from_a - get_normal() * dist_a);
 
 
     } else {
     } else {
-      // Within the tube!  Yay, that means we have a surface point.
+      // Within the capsule!  Yay, that means we have a surface point.
       new_entry->set_surface_point(from_a + t * from_direction);
       new_entry->set_surface_point(from_a + t * from_direction);
     }
     }
   } else {
   } else {
     // If it's completely parallel, pretend it's colliding in the center of
     // If it's completely parallel, pretend it's colliding in the center of
-    // the tube.
+    // the capsule.
     new_entry->set_surface_point(from_a + 0.5f * from_direction - get_normal() * dist_a);
     new_entry->set_surface_point(from_a + 0.5f * from_direction - get_normal() * dist_a);
   }
   }
 
 
   if (IS_NEARLY_EQUAL(dist_a, dist_b)) {
   if (IS_NEARLY_EQUAL(dist_a, dist_b)) {
-    // Let's be fair and choose the center of the tube.
+    // Let's be fair and choose the center of the capsule.
     new_entry->set_interior_point(from_a + 0.5f * from_direction - get_normal() * from_radius);
     new_entry->set_interior_point(from_a + 0.5f * from_direction - get_normal() * from_radius);
 
 
   } else if (dist_a < dist_b) {
   } else if (dist_a < dist_b) {

+ 1 - 1
panda/src/collide/collisionPlane.h

@@ -72,7 +72,7 @@ protected:
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_segment(const CollisionEntry &entry) const;
   test_intersection_from_segment(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
-  test_intersection_from_tube(const CollisionEntry &entry) const;
+  test_intersection_from_capsule(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_parabola(const CollisionEntry &entry) const;
   test_intersection_from_parabola(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)

+ 4 - 4
panda/src/collide/collisionSolid.cxx

@@ -17,7 +17,7 @@
 #include "collisionLine.h"
 #include "collisionLine.h"
 #include "collisionRay.h"
 #include "collisionRay.h"
 #include "collisionSegment.h"
 #include "collisionSegment.h"
-#include "collisionTube.h"
+#include "collisionCapsule.h"
 #include "collisionParabola.h"
 #include "collisionParabola.h"
 #include "collisionBox.h"
 #include "collisionBox.h"
 #include "collisionEntry.h"
 #include "collisionEntry.h"
@@ -240,11 +240,11 @@ test_intersection_from_segment(const CollisionEntry &) const {
 
 
 /**
 /**
  * This is part of the double-dispatch implementation of test_intersection().
  * This is part of the double-dispatch implementation of test_intersection().
- * It is called when the "from" object is a tube.
+ * It is called when the "from" object is a capsule.
  */
  */
 PT(CollisionEntry) CollisionSolid::
 PT(CollisionEntry) CollisionSolid::
-test_intersection_from_tube(const CollisionEntry &) const {
-  report_undefined_intersection_test(CollisionTube::get_class_type(),
+test_intersection_from_capsule(const CollisionEntry &) const {
+  report_undefined_intersection_test(CollisionCapsule::get_class_type(),
                                      get_type());
                                      get_type());
   return nullptr;
   return nullptr;
 }
 }

+ 2 - 2
panda/src/collide/collisionSolid.h

@@ -108,7 +108,7 @@ protected:
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_segment(const CollisionEntry &entry) const;
   test_intersection_from_segment(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
-  test_intersection_from_tube(const CollisionEntry &entry) const;
+  test_intersection_from_capsule(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_parabola(const CollisionEntry &entry) const;
   test_intersection_from_parabola(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
@@ -177,7 +177,7 @@ private:
   friend class CollisionLine;
   friend class CollisionLine;
   friend class CollisionRay;
   friend class CollisionRay;
   friend class CollisionSegment;
   friend class CollisionSegment;
-  friend class CollisionTube;
+  friend class CollisionCapsule;
   friend class CollisionParabola;
   friend class CollisionParabola;
   friend class CollisionHandlerFluidPusher;
   friend class CollisionHandlerFluidPusher;
   friend class CollisionBox;
   friend class CollisionBox;

+ 10 - 10
panda/src/collide/collisionSphere.cxx

@@ -17,7 +17,7 @@
 #include "collisionHandler.h"
 #include "collisionHandler.h"
 #include "collisionEntry.h"
 #include "collisionEntry.h"
 #include "collisionSegment.h"
 #include "collisionSegment.h"
-#include "collisionTube.h"
+#include "collisionCapsule.h"
 #include "collisionParabola.h"
 #include "collisionParabola.h"
 #include "collisionBox.h"
 #include "collisionBox.h"
 #include "config_collide.h"
 #include "config_collide.h"
@@ -444,18 +444,18 @@ test_intersection_from_segment(const CollisionEntry &entry) const {
  *
  *
  */
  */
 PT(CollisionEntry) CollisionSphere::
 PT(CollisionEntry) CollisionSphere::
-test_intersection_from_tube(const CollisionEntry &entry) const {
-  const CollisionTube *tube;
-  DCAST_INTO_R(tube, entry.get_from(), nullptr);
+test_intersection_from_capsule(const CollisionEntry &entry) const {
+  const CollisionCapsule *capsule;
+  DCAST_INTO_R(capsule, entry.get_from(), nullptr);
 
 
   const LMatrix4 &wrt_mat = entry.get_wrt_mat();
   const LMatrix4 &wrt_mat = entry.get_wrt_mat();
 
 
-  LPoint3 from_a = tube->get_point_a() * wrt_mat;
-  LPoint3 from_b = tube->get_point_b() * wrt_mat;
+  LPoint3 from_a = capsule->get_point_a() * wrt_mat;
+  LPoint3 from_b = capsule->get_point_b() * wrt_mat;
   LVector3 from_direction = from_b - from_a;
   LVector3 from_direction = from_b - from_a;
 
 
   LVector3 from_radius_v =
   LVector3 from_radius_v =
-    LVector3(tube->get_radius(), 0.0f, 0.0f) * wrt_mat;
+    LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat;
   PN_stdfloat from_radius = length(from_radius_v);
   PN_stdfloat from_radius = length(from_radius_v);
 
 
   double t1, t2;
   double t1, t2;
@@ -465,8 +465,8 @@ test_intersection_from_tube(const CollisionEntry &entry) const {
   }
   }
 
 
   if (t2 < 0.0 || t1 > 1.0) {
   if (t2 < 0.0 || t1 > 1.0) {
-    // Both intersection points are before the start of the tube or after
-    // the end of the tube.
+    // Both intersection points are before the start of the capsule or after
+    // the end of the capsule.
     return nullptr;
     return nullptr;
   }
   }
 
 
@@ -487,7 +487,7 @@ test_intersection_from_tube(const CollisionEntry &entry) const {
   new_entry->set_surface_point(get_center() + normal * get_radius());
   new_entry->set_surface_point(get_center() + normal * get_radius());
   new_entry->set_interior_point(inner_point - normal * from_radius);
   new_entry->set_interior_point(inner_point - normal * from_radius);
 
 
-  if (has_effective_normal() && tube->get_respect_effective_normal()) {
+  if (has_effective_normal() && capsule->get_respect_effective_normal()) {
     new_entry->set_surface_normal(get_effective_normal());
     new_entry->set_surface_normal(get_effective_normal());
   } else {
   } else {
     new_entry->set_surface_normal(normal);
     new_entry->set_surface_normal(normal);

+ 1 - 1
panda/src/collide/collisionSphere.h

@@ -72,7 +72,7 @@ protected:
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_segment(const CollisionEntry &entry) const;
   test_intersection_from_segment(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
-  test_intersection_from_tube(const CollisionEntry &entry) const;
+  test_intersection_from_capsule(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_parabola(const CollisionEntry &entry) const;
   test_intersection_from_parabola(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)

+ 2 - 2
panda/src/collide/collisionTraverser.cxx

@@ -20,7 +20,7 @@
 #include "collisionVisualizer.h"
 #include "collisionVisualizer.h"
 #include "collisionSphere.h"
 #include "collisionSphere.h"
 #include "collisionBox.h"
 #include "collisionBox.h"
-#include "collisionTube.h"
+#include "collisionCapsule.h"
 #include "collisionPolygon.h"
 #include "collisionPolygon.h"
 #include "collisionPlane.h"
 #include "collisionPlane.h"
 #include "config_collide.h"
 #include "config_collide.h"
@@ -345,7 +345,7 @@ traverse(const NodePath &root) {
   _geom_volume_pcollector.flush_level();
   _geom_volume_pcollector.flush_level();
 
 
   CollisionSphere::flush_level();
   CollisionSphere::flush_level();
-  CollisionTube::flush_level();
+  CollisionCapsule::flush_level();
   CollisionPolygon::flush_level();
   CollisionPolygon::flush_level();
   CollisionPlane::flush_level();
   CollisionPlane::flush_level();
   CollisionBox::flush_level();
   CollisionBox::flush_level();

+ 8 - 141
panda/src/collide/collisionTube.h

@@ -7,154 +7,21 @@
  * with this source code in a file named "LICENSE."
  * with this source code in a file named "LICENSE."
  *
  *
  * @file collisionTube.h
  * @file collisionTube.h
- * @author drose
- * @date 2003-09-25
+ * @author rdb
+ * @date 2018-12-23
  */
  */
 
 
 #ifndef COLLISIONTUBE_H
 #ifndef COLLISIONTUBE_H
 #define COLLISIONTUBE_H
 #define COLLISIONTUBE_H
 
 
-#include "pandabase.h"
-#include "collisionSolid.h"
-#include "parabola.h"
+#include "collisionCapsule.h"
 
 
+BEGIN_PUBLISH
 /**
 /**
- * This implements a solid roughly in cylindrical shape.  It's not called a
- * CollisionCylinder because it's not a true cylinder; specifically, it has
- * rounded ends instead of flat ends.  It looks more like a Contac pill.
+ * Alias for backward compatibility.
+ * @deprecated use CollisionCapsule instead.
  */
  */
-class EXPCL_PANDA_COLLIDE CollisionTube : public CollisionSolid {
-PUBLISHED:
-  INLINE explicit CollisionTube(const LPoint3 &a, const LPoint3 &db,
-                                PN_stdfloat radius);
-  INLINE explicit CollisionTube(PN_stdfloat ax, PN_stdfloat ay, PN_stdfloat az,
-                                PN_stdfloat bx, PN_stdfloat by, PN_stdfloat bz,
-                                PN_stdfloat radius);
-
-  virtual LPoint3 get_collision_origin() const;
-
-private:
-  INLINE CollisionTube();
-
-public:
-  INLINE CollisionTube(const CollisionTube &copy);
-  virtual CollisionSolid *make_copy();
-
-  virtual PT(CollisionEntry)
-  test_intersection(const CollisionEntry &entry) const;
-
-  virtual void xform(const LMatrix4 &mat);
-
-  virtual PStatCollector &get_volume_pcollector();
-  virtual PStatCollector &get_test_pcollector();
-
-  virtual void output(std::ostream &out) const;
-
-  INLINE static void flush_level();
-
-PUBLISHED:
-  INLINE void set_point_a(const LPoint3 &a);
-  INLINE void set_point_a(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z);
-  INLINE const LPoint3 &get_point_a() const;
-
-  INLINE void set_point_b(const LPoint3 &b);
-  INLINE void set_point_b(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z);
-  INLINE const LPoint3 &get_point_b() const;
-
-  INLINE void set_radius(PN_stdfloat radius);
-  INLINE PN_stdfloat get_radius() const;
-
-PUBLISHED:
-  MAKE_PROPERTY(point_a, get_point_a, set_point_a);
-  MAKE_PROPERTY(point_b, get_point_b, set_point_b);
-  MAKE_PROPERTY(radius, get_radius, set_radius);
-
-protected:
-  virtual PT(BoundingVolume) compute_internal_bounds() const;
-
-protected:
-  virtual PT(CollisionEntry)
-  test_intersection_from_sphere(const CollisionEntry &entry) const;
-  virtual PT(CollisionEntry)
-  test_intersection_from_line(const CollisionEntry &entry) const;
-  virtual PT(CollisionEntry)
-  test_intersection_from_ray(const CollisionEntry &entry) const;
-  virtual PT(CollisionEntry)
-  test_intersection_from_segment(const CollisionEntry &entry) const;
-  virtual PT(CollisionEntry)
-  test_intersection_from_tube(const CollisionEntry &entry) const;
-  virtual PT(CollisionEntry)
-  test_intersection_from_parabola(const CollisionEntry &entry) const;
-
-  virtual void fill_viz_geom();
-
-private:
-  void recalc_internals();
-
-  LVertex calc_sphere1_vertex(int ri, int si, int num_rings, int num_slices);
-  LVertex calc_sphere2_vertex(int ri, int si, int num_rings, int num_slices,
-                              PN_stdfloat length);
-
-  static void calc_closest_segment_points(double &t1, double &t2,
-                                          const LPoint3 &from1, const LVector3 &delta1,
-                                          const LPoint3 &from2, const LVector3 &delta2);
-  bool intersects_line(double &t1, double &t2,
-                       const LPoint3 &from, const LVector3 &delta,
-                       PN_stdfloat inflate_radius) const;
-  static bool sphere_intersects_line(double &t1, double &t2, PN_stdfloat center_y,
-                                     const LPoint3 &from, const LVector3 &delta,
-                                     PN_stdfloat radius);
-  bool intersects_parabola(double &t, const LParabola &parabola,
-                           double t1, double t2,
-                           const LPoint3 &p1, const LPoint3 &p2) const;
-  void calculate_surface_point_and_normal(const LPoint3 &surface_point,
-                                          double extra_radius,
-                                          LPoint3 &result_point,
-                                          LVector3 &result_normal) const;
-  void set_intersection_point(CollisionEntry *new_entry,
-                              const LPoint3 &into_intersection_point,
-                              double extra_radius) const;
-
-private:
-  LPoint3 _a, _b;
-  PN_stdfloat _radius;
-
-  // These are derived from the above.
-  LMatrix4 _mat;
-  LMatrix4 _inv_mat;
-  PN_stdfloat _length;
-
-  static PStatCollector _volume_pcollector;
-  static PStatCollector _test_pcollector;
-
-public:
-  static void register_with_read_factory();
-  virtual void write_datagram(BamWriter *manager, Datagram &dg);
-
-protected:
-  static TypedWritable *make_from_bam(const FactoryParams &params);
-  void fillin(DatagramIterator &scan, BamReader *manager);
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    CollisionSolid::init_type();
-    register_type(_type_handle, "CollisionTube",
-                  CollisionSolid::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
-  static TypeHandle _type_handle;
-
-  friend class CollisionBox;
-};
-
-#include "collisionTube.I"
+typedef CollisionCapsule CollisionTube;
+END_PUBLISH
 
 
 #endif
 #endif

+ 7 - 3
panda/src/collide/config_collide.cxx

@@ -13,6 +13,7 @@
 
 
 #include "config_collide.h"
 #include "config_collide.h"
 #include "collisionBox.h"
 #include "collisionBox.h"
+#include "collisionCapsule.h"
 #include "collisionEntry.h"
 #include "collisionEntry.h"
 #include "collisionHandler.h"
 #include "collisionHandler.h"
 #include "collisionHandlerEvent.h"
 #include "collisionHandlerEvent.h"
@@ -38,7 +39,6 @@
 #include "collisionSolid.h"
 #include "collisionSolid.h"
 #include "collisionSphere.h"
 #include "collisionSphere.h"
 #include "collisionTraverser.h"
 #include "collisionTraverser.h"
-#include "collisionTube.h"
 #include "collisionVisualizer.h"
 #include "collisionVisualizer.h"
 #include "dconfig.h"
 #include "dconfig.h"
 
 
@@ -126,6 +126,7 @@ init_libcollide() {
   initialized = true;
   initialized = true;
 
 
   CollisionBox::init_type();
   CollisionBox::init_type();
+  CollisionCapsule::init_type();
   CollisionEntry::init_type();
   CollisionEntry::init_type();
   CollisionHandler::init_type();
   CollisionHandler::init_type();
   CollisionHandlerEvent::init_type();
   CollisionHandlerEvent::init_type();
@@ -150,14 +151,18 @@ init_libcollide() {
   CollisionSolid::init_type();
   CollisionSolid::init_type();
   CollisionSphere::init_type();
   CollisionSphere::init_type();
   CollisionTraverser::init_type();
   CollisionTraverser::init_type();
-  CollisionTube::init_type();
 
 
 #ifdef DO_COLLISION_RECORDING
 #ifdef DO_COLLISION_RECORDING
   CollisionRecorder::init_type();
   CollisionRecorder::init_type();
   CollisionVisualizer::init_type();
   CollisionVisualizer::init_type();
 #endif
 #endif
 
 
+  // Record the old name for CollisionCapsule for backwards compatibility.
+  BamWriter::record_obsolete_type_name(CollisionCapsule::get_class_type(),
+                                       "CollisionTube", 6, 44);
+
   CollisionBox::register_with_read_factory();
   CollisionBox::register_with_read_factory();
+  CollisionCapsule::register_with_read_factory();
   CollisionInvSphere::register_with_read_factory();
   CollisionInvSphere::register_with_read_factory();
   CollisionLine::register_with_read_factory();
   CollisionLine::register_with_read_factory();
   CollisionNode::register_with_read_factory();
   CollisionNode::register_with_read_factory();
@@ -168,5 +173,4 @@ init_libcollide() {
   CollisionRay::register_with_read_factory();
   CollisionRay::register_with_read_factory();
   CollisionSegment::register_with_read_factory();
   CollisionSegment::register_with_read_factory();
   CollisionSphere::register_with_read_factory();
   CollisionSphere::register_with_read_factory();
-  CollisionTube::register_with_read_factory();
 }
 }

+ 1 - 2
panda/src/collide/p3collide_composite1.cxx

@@ -1,5 +1,6 @@
 #include "config_collide.cxx"
 #include "config_collide.cxx"
 #include "collisionBox.cxx"
 #include "collisionBox.cxx"
+#include "collisionCapsule.cxx"
 #include "collisionEntry.cxx"
 #include "collisionEntry.cxx"
 #include "collisionGeom.cxx"
 #include "collisionGeom.cxx"
 #include "collisionHandler.cxx"
 #include "collisionHandler.cxx"
@@ -12,5 +13,3 @@
 #include "collisionHandlerFluidPusher.cxx"
 #include "collisionHandlerFluidPusher.cxx"
 #include "collisionHandlerQueue.cxx"
 #include "collisionHandlerQueue.cxx"
 #include "collisionInvSphere.cxx"
 #include "collisionInvSphere.cxx"
-#include "collisionLevelStateBase.cxx"
-#include "collisionLevelState.cxx"

+ 2 - 1
panda/src/collide/p3collide_composite2.cxx

@@ -1,3 +1,5 @@
+#include "collisionLevelStateBase.cxx"
+#include "collisionLevelState.cxx"
 #include "collisionLine.cxx"
 #include "collisionLine.cxx"
 #include "collisionNode.cxx"
 #include "collisionNode.cxx"
 #include "collisionParabola.cxx"
 #include "collisionParabola.cxx"
@@ -10,5 +12,4 @@
 #include "collisionSolid.cxx"
 #include "collisionSolid.cxx"
 #include "collisionSphere.cxx"
 #include "collisionSphere.cxx"
 #include "collisionTraverser.cxx"
 #include "collisionTraverser.cxx"
-#include "collisionTube.cxx"
 #include "collisionVisualizer.cxx"
 #include "collisionVisualizer.cxx"

+ 4 - 4
panda/src/display/graphicsEngine.cxx

@@ -110,8 +110,8 @@ PStatCollector GraphicsEngine::_volume_sphere_pcollector("Collision Volumes:Coll
 PStatCollector GraphicsEngine::_test_sphere_pcollector("Collision Tests:CollisionSphere");
 PStatCollector GraphicsEngine::_test_sphere_pcollector("Collision Tests:CollisionSphere");
 PStatCollector GraphicsEngine::_volume_box_pcollector("Collision Volumes:CollisionBox");
 PStatCollector GraphicsEngine::_volume_box_pcollector("Collision Volumes:CollisionBox");
 PStatCollector GraphicsEngine::_test_box_pcollector("Collision Tests:CollisionBox");
 PStatCollector GraphicsEngine::_test_box_pcollector("Collision Tests:CollisionBox");
-PStatCollector GraphicsEngine::_volume_tube_pcollector("Collision Volumes:CollisionTube");
-PStatCollector GraphicsEngine::_test_tube_pcollector("Collision Tests:CollisionTube");
+PStatCollector GraphicsEngine::_volume_capsule_pcollector("Collision Volumes:CollisionCapsule");
+PStatCollector GraphicsEngine::_test_capsule_pcollector("Collision Tests:CollisionCapsule");
 PStatCollector GraphicsEngine::_volume_inv_sphere_pcollector("Collision Volumes:CollisionInvSphere");
 PStatCollector GraphicsEngine::_volume_inv_sphere_pcollector("Collision Volumes:CollisionInvSphere");
 PStatCollector GraphicsEngine::_test_inv_sphere_pcollector("Collision Tests:CollisionInvSphere");
 PStatCollector GraphicsEngine::_test_inv_sphere_pcollector("Collision Tests:CollisionInvSphere");
 PStatCollector GraphicsEngine::_volume_geom_pcollector("Collision Volumes:CollisionGeom");
 PStatCollector GraphicsEngine::_volume_geom_pcollector("Collision Volumes:CollisionGeom");
@@ -875,8 +875,8 @@ render_frame() {
     _test_sphere_pcollector.clear_level();
     _test_sphere_pcollector.clear_level();
     _volume_box_pcollector.clear_level();
     _volume_box_pcollector.clear_level();
     _test_box_pcollector.clear_level();
     _test_box_pcollector.clear_level();
-    _volume_tube_pcollector.clear_level();
-    _test_tube_pcollector.clear_level();
+    _volume_capsule_pcollector.clear_level();
+    _test_capsule_pcollector.clear_level();
     _volume_inv_sphere_pcollector.clear_level();
     _volume_inv_sphere_pcollector.clear_level();
     _test_inv_sphere_pcollector.clear_level();
     _test_inv_sphere_pcollector.clear_level();
     _volume_geom_pcollector.clear_level();
     _volume_geom_pcollector.clear_level();

+ 2 - 2
panda/src/display/graphicsEngine.h

@@ -401,8 +401,8 @@ private:
   static PStatCollector _test_sphere_pcollector;
   static PStatCollector _test_sphere_pcollector;
   static PStatCollector _volume_box_pcollector;
   static PStatCollector _volume_box_pcollector;
   static PStatCollector _test_box_pcollector;
   static PStatCollector _test_box_pcollector;
-  static PStatCollector _volume_tube_pcollector;
-  static PStatCollector _test_tube_pcollector;
+  static PStatCollector _volume_capsule_pcollector;
+  static PStatCollector _test_capsule_pcollector;
   static PStatCollector _volume_inv_sphere_pcollector;
   static PStatCollector _volume_inv_sphere_pcollector;
   static PStatCollector _test_inv_sphere_pcollector;
   static PStatCollector _test_inv_sphere_pcollector;
   static PStatCollector _volume_geom_pcollector;
   static PStatCollector _volume_geom_pcollector;

+ 1 - 1
panda/src/doc/collisionFlags.txt

@@ -6,7 +6,7 @@ camera-collide: for things that the camera should avoid
 trigger: for things (usually not barriers or floors) that should trigger an
 trigger: for things (usually not barriers or floors) that should trigger an
          event when avatars intersect with them
          event when avatars intersect with them
 sphere: for things that should have a collision sphere around them
 sphere: for things that should have a collision sphere around them
-tube: for things that should have a collision tube (cylinder) around them
+tube: for things that should have a collision capsule around them
 
 
 NOTES
 NOTES
 
 

+ 20 - 27
panda/src/dxml/config_dxml.cxx

@@ -30,14 +30,12 @@ ConfigureFn(config_dxml) {
   init_libdxml();
   init_libdxml();
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: init_libdxml
-//  Description: Initializes the library.  This must be called at
-//               least once before any of the functions or classes in
-//               this library can be used.  Normally it will be
-//               called by the static initializers and need not be
-//               called explicitly, but special cases exist.
-////////////////////////////////////////////////////////////////////
+/**
+ * Initializes the library.  This must be called at least once before any of
+ * the functions or classes in this library can be used.  Normally it will be
+ * called by the static initializers and need not be called explicitly, but
+ * special cases exist.
+ */
 void
 void
 init_libdxml() {
 init_libdxml() {
   static bool initialized = false;
   static bool initialized = false;
@@ -48,11 +46,10 @@ init_libdxml() {
 }
 }
 
 
 BEGIN_PUBLISH
 BEGIN_PUBLISH
-////////////////////////////////////////////////////////////////////
-//     Function: read_xml_stream
-//  Description: Reads an XML document from the indicated stream.
-//               Returns the document, or NULL on error.
-////////////////////////////////////////////////////////////////////
+/**
+ * Reads an XML document from the indicated stream.
+ * @returns the document, or NULL on error.
+ */
 TiXmlDocument *
 TiXmlDocument *
 read_xml_stream(std::istream &in) {
 read_xml_stream(std::istream &in) {
   TiXmlDocument *doc = new TiXmlDocument;
   TiXmlDocument *doc = new TiXmlDocument;
@@ -67,10 +64,9 @@ read_xml_stream(std::istream &in) {
 END_PUBLISH
 END_PUBLISH
 
 
 BEGIN_PUBLISH
 BEGIN_PUBLISH
-////////////////////////////////////////////////////////////////////
-//     Function: write_xml_stream
-//  Description: Writes an XML document to the indicated stream.
-////////////////////////////////////////////////////////////////////
+/**
+ * Writes an XML document to the indicated stream.
+ */
 void
 void
 write_xml_stream(std::ostream &out, TiXmlDocument *doc) {
 write_xml_stream(std::ostream &out, TiXmlDocument *doc) {
   out << *doc;
   out << *doc;
@@ -78,10 +74,9 @@ write_xml_stream(std::ostream &out, TiXmlDocument *doc) {
 END_PUBLISH
 END_PUBLISH
 
 
 BEGIN_PUBLISH
 BEGIN_PUBLISH
-////////////////////////////////////////////////////////////////////
-//     Function: print_xml
-//  Description: Writes an XML object to stdout, with formatting.
-////////////////////////////////////////////////////////////////////
+/**
+ * Writes an XML object to stdout, with formatting.
+ */
 void
 void
 print_xml(TiXmlNode *xnode) {
 print_xml(TiXmlNode *xnode) {
   xnode->Print(stdout, 0);
   xnode->Print(stdout, 0);
@@ -89,12 +84,10 @@ print_xml(TiXmlNode *xnode) {
 END_PUBLISH
 END_PUBLISH
 
 
 BEGIN_PUBLISH
 BEGIN_PUBLISH
-////////////////////////////////////////////////////////////////////
-//     Function: print_xml_to_file
-//  Description: Writes an XML object to the indicated file, with
-//               formatting.  Unfortunately the VFS cannot be
-//               supported; the file must be a real filename on disk.
-////////////////////////////////////////////////////////////////////
+/**
+ * Writes an XML object to the indicated file, with formatting.  Unfortunately
+ * the VFS cannot be supported; the file must be a real filename on disk.
+ */
 void
 void
 print_xml_to_file(const Filename &filename, TiXmlNode *xnode) {
 print_xml_to_file(const Filename &filename, TiXmlNode *xnode) {
   std::string os_name = filename.to_os_specific();
   std::string os_name = filename.to_os_specific();

+ 2 - 1
panda/src/egg/eggGroup.cxx

@@ -897,7 +897,8 @@ string_cs_type(const string &strval) {
   } else if (cmp_nocase_uh(strval, "inv-sphere") == 0 ||
   } else if (cmp_nocase_uh(strval, "inv-sphere") == 0 ||
              cmp_nocase_uh(strval, "invsphere") == 0) {
              cmp_nocase_uh(strval, "invsphere") == 0) {
     return CST_inv_sphere;
     return CST_inv_sphere;
-  } else if (cmp_nocase_uh(strval, "tube") == 0) {
+  } else if (cmp_nocase_uh(strval, "tube") == 0 ||
+             cmp_nocase_uh(strval, "capsule") == 0) {
     return CST_tube;
     return CST_tube;
   } else if (cmp_nocase_uh(strval, "floor-mesh") == 0 ||
   } else if (cmp_nocase_uh(strval, "floor-mesh") == 0 ||
              cmp_nocase_uh(strval, "floormesh") == 0) {
              cmp_nocase_uh(strval, "floormesh") == 0) {

+ 10 - 10
panda/src/egg2pg/eggLoader.cxx

@@ -74,7 +74,7 @@
 #include "collisionNode.h"
 #include "collisionNode.h"
 #include "collisionSphere.h"
 #include "collisionSphere.h"
 #include "collisionInvSphere.h"
 #include "collisionInvSphere.h"
-#include "collisionTube.h"
+#include "collisionCapsule.h"
 #include "collisionPlane.h"
 #include "collisionPlane.h"
 #include "collisionPolygon.h"
 #include "collisionPolygon.h"
 #include "collisionFloorMesh.h"
 #include "collisionFloorMesh.h"
@@ -2865,7 +2865,7 @@ make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
     break;
     break;
 
 
   case EggGroup::CST_tube:
   case EggGroup::CST_tube:
-    make_collision_tube(egg_group, cnode, start_group->get_collide_flags());
+    make_collision_capsule(egg_group, cnode, start_group->get_collide_flags());
     break;
     break;
 
 
   case EggGroup::CST_floor_mesh:
   case EggGroup::CST_floor_mesh:
@@ -3045,12 +3045,12 @@ make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode,
 }
 }
 
 
 /**
 /**
- * Creates a single CollisionTube corresponding to the polygons associated
+ * Creates a single CollisionCapsule corresponding to the polygons associated
  * with this group.
  * with this group.
  */
  */
 void EggLoader::
 void EggLoader::
-make_collision_tube(EggGroup *egg_group, CollisionNode *cnode,
-                    EggGroup::CollideFlags flags) {
+make_collision_capsule(EggGroup *egg_group, CollisionNode *cnode,
+                       EggGroup::CollideFlags flags) {
   EggGroup *geom_group = find_collision_geometry(egg_group, flags);
   EggGroup *geom_group = find_collision_geometry(egg_group, flags);
   if (geom_group != nullptr) {
   if (geom_group != nullptr) {
     // Collect all of the vertices.
     // Collect all of the vertices.
@@ -3175,7 +3175,7 @@ make_collision_tube(EggGroup *egg_group, CollisionNode *cnode,
 
 
         // Transform all of the points so that the major axis is along the Y
         // Transform all of the points so that the major axis is along the Y
         // axis, and the origin is the center.  This is very similar to the
         // axis, and the origin is the center.  This is very similar to the
-        // CollisionTube's idea of its canonical orientation (although not
+        // CollisionCapsule's idea of its canonical orientation (although not
         // exactly the same, since it is centered on the origin instead of
         // exactly the same, since it is centered on the origin instead of
         // having point_a on the origin).  It makes it easier to determine the
         // having point_a on the origin).  It makes it easier to determine the
         // length and radius of the cylinder.
         // length and radius of the cylinder.
@@ -3230,11 +3230,11 @@ make_collision_tube(EggGroup *egg_group, CollisionNode *cnode,
         LPoint3d point_a = center - half;
         LPoint3d point_a = center - half;
         LPoint3d point_b = center + half;
         LPoint3d point_b = center + half;
 
 
-        CollisionTube *cstube =
-          new CollisionTube(LCAST(PN_stdfloat, point_a), LCAST(PN_stdfloat, point_b),
+        CollisionCapsule *cscapsule =
+          new CollisionCapsule(LCAST(PN_stdfloat, point_a), LCAST(PN_stdfloat, point_b),
                             radius);
                             radius);
-        apply_collision_flags(cstube, flags);
-        cnode->add_solid(cstube);
+        apply_collision_flags(cscapsule, flags);
+        cnode->add_solid(cscapsule);
       }
       }
     }
     }
   }
   }

+ 2 - 2
panda/src/egg2pg/eggLoader.h

@@ -180,8 +180,8 @@ private:
                           EggGroup::CollideFlags flags);
                           EggGroup::CollideFlags flags);
   void make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode,
   void make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode,
                                  EggGroup::CollideFlags flags);
                                  EggGroup::CollideFlags flags);
-  void make_collision_tube(EggGroup *egg_group, CollisionNode *cnode,
-                           EggGroup::CollideFlags flags);
+  void make_collision_capsule(EggGroup *egg_group, CollisionNode *cnode,
+                              EggGroup::CollideFlags flags);
   void make_collision_floor_mesh(EggGroup *egg_group, CollisionNode *cnode,
   void make_collision_floor_mesh(EggGroup *egg_group, CollisionNode *cnode,
                            EggGroup::CollideFlags flags);
                            EggGroup::CollideFlags flags);
   void apply_collision_flags(CollisionSolid *solid,
   void apply_collision_flags(CollisionSolid *solid,

+ 15 - 15
panda/src/egg2pg/eggSaver.cxx

@@ -39,7 +39,7 @@
 #include "collisionSphere.h"
 #include "collisionSphere.h"
 #include "collisionBox.h"
 #include "collisionBox.h"
 #include "collisionInvSphere.h"
 #include "collisionInvSphere.h"
-#include "collisionTube.h"
+#include "collisionCapsule.h"
 #include "textureStage.h"
 #include "textureStage.h"
 #include "geomNode.h"
 #include "geomNode.h"
 #include "geom.h"
 #include "geom.h"
@@ -620,13 +620,13 @@ convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path,
         egg_poly->add_vertex(cvpool->create_unique_vertex(ev0));
         egg_poly->add_vertex(cvpool->create_unique_vertex(ev0));
         egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
         egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
 
 
-      } else if (child->is_of_type(CollisionTube::get_class_type())) {
-        CPT(CollisionTube) tube = DCAST(CollisionTube, child);
-        LPoint3 point_a = tube->get_point_a();
-        LPoint3 point_b = tube->get_point_b();
+      } else if (child->is_of_type(CollisionCapsule::get_class_type())) {
+        CPT(CollisionCapsule) capsule = DCAST(CollisionCapsule, child);
+        LPoint3 point_a = capsule->get_point_a();
+        LPoint3 point_b = capsule->get_point_b();
         LPoint3 centroid = (point_a + point_b) * 0.5f;
         LPoint3 centroid = (point_a + point_b) * 0.5f;
 
 
-        // Also get an arbitrary vector perpendicular to the tube.
+        // Also get an arbitrary vector perpendicular to the capsule.
         LVector3 axis = point_b - point_a;
         LVector3 axis = point_b - point_a;
         LVector3 sideways;
         LVector3 sideways;
         if (std::fabs(axis[2]) > std::fabs(axis[1])) {
         if (std::fabs(axis[2]) > std::fabs(axis[1])) {
@@ -635,18 +635,18 @@ convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path,
           sideways = axis.cross(LVector3(0, 0, 1));
           sideways = axis.cross(LVector3(0, 0, 1));
         }
         }
         sideways.normalize();
         sideways.normalize();
-        sideways *= tube->get_radius();
-        LVector3 extend = axis.normalized() * tube->get_radius();
+        sideways *= capsule->get_radius();
+        LVector3 extend = axis.normalized() * capsule->get_radius();
 
 
-        EggGroup *egg_tube;
+        EggGroup *egg_capsule;
         if (num_solids == 1) {
         if (num_solids == 1) {
-          egg_tube = egg_group;
+          egg_capsule = egg_group;
         } else {
         } else {
-          egg_tube = new EggGroup;
-          egg_group->add_child(egg_tube);
+          egg_capsule = new EggGroup;
+          egg_group->add_child(egg_capsule);
         }
         }
-        egg_tube->set_cs_type(EggGroup::CST_tube);
-        egg_tube->set_collide_flags(flags);
+        egg_capsule->set_cs_type(EggGroup::CST_tube);
+        egg_capsule->set_collide_flags(flags);
 
 
         // Add two points for the endcaps, and then two points around the
         // Add two points for the endcaps, and then two points around the
         // centroid to indicate the radius.
         // centroid to indicate the radius.
@@ -657,7 +657,7 @@ convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path,
         ev3.set_pos(LCAST(double, (centroid - sideways) * net_mat));
         ev3.set_pos(LCAST(double, (centroid - sideways) * net_mat));
 
 
         EggPolygon *egg_poly = new EggPolygon;
         EggPolygon *egg_poly = new EggPolygon;
-        egg_tube->add_child(egg_poly);
+        egg_capsule->add_child(egg_poly);
 
 
         egg_poly->add_vertex(cvpool->create_unique_vertex(ev0));
         egg_poly->add_vertex(cvpool->create_unique_vertex(ev0));
         egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
         egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));

+ 2 - 2
panda/src/ffmpeg/ffmpegVideoCursor.cxx

@@ -79,8 +79,6 @@ init_from(FfmpegVideo *source) {
     return;
     return;
   }
   }
 
 
-  ReMutexHolder av_holder(_av_lock);
-
 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 45, 101)
 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 45, 101)
   _frame = av_frame_alloc();
   _frame = av_frame_alloc();
   _frame_out = av_frame_alloc();
   _frame_out = av_frame_alloc();
@@ -109,6 +107,8 @@ init_from(FfmpegVideo *source) {
   _eof_known = false;
   _eof_known = false;
   _eof_frame = 0;
   _eof_frame = 0;
 
 
+  ReMutexHolder av_holder(_av_lock);
+
   // Check if we got an alpha format.  Please note that some video codecs
   // Check if we got an alpha format.  Please note that some video codecs
   // (eg. libvpx) change the pix_fmt after decoding the first frame, which is
   // (eg. libvpx) change the pix_fmt after decoding the first frame, which is
   // why we didn't do this earlier.
   // why we didn't do this earlier.

+ 3 - 2
panda/src/glstuff/glBufferContext_src.I

@@ -16,8 +16,9 @@
  */
  */
 INLINE CLP(BufferContext)::
 INLINE CLP(BufferContext)::
 CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg,
 CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg,
-                   PreparedGraphicsObjects *pgo) :
-  BufferContext(&pgo->_sbuffer_residency),
+                   PreparedGraphicsObjects *pgo,
+                   TypedWritableReferenceCount *object) :
+  BufferContext(&pgo->_sbuffer_residency, object),
   AdaptiveLruPage(0),
   AdaptiveLruPage(0),
   _glgsg(glgsg)
   _glgsg(glgsg)
 {
 {

+ 2 - 1
panda/src/glstuff/glBufferContext_src.h

@@ -21,7 +21,8 @@
 class EXPCL_GL CLP(BufferContext) : public BufferContext, public AdaptiveLruPage {
 class EXPCL_GL CLP(BufferContext) : public BufferContext, public AdaptiveLruPage {
 public:
 public:
   INLINE CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg,
   INLINE CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg,
-                            PreparedGraphicsObjects *pgo);
+                            PreparedGraphicsObjects *pgo,
+                            TypedWritableReferenceCount *object);
   ALLOC_DELETED_CHAIN(CLP(BufferContext));
   ALLOC_DELETED_CHAIN(CLP(BufferContext));
 
 
   virtual void evict_lru();
   virtual void evict_lru();

+ 1 - 1
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -1413,7 +1413,7 @@ open_buffer() {
   }
   }
 
 
   if (_rb_context == nullptr) {
   if (_rb_context == nullptr) {
-    _rb_context = new BufferContext(&(glgsg->_renderbuffer_residency));
+    _rb_context = new BufferContext(&(glgsg->_renderbuffer_residency), nullptr);
   }
   }
 
 
 /*
 /*

+ 16 - 2
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -6519,7 +6519,7 @@ prepare_shader_buffer(ShaderBuffer *data) {
   if (_supports_shader_buffers) {
   if (_supports_shader_buffers) {
     PStatGPUTimer timer(this, _prepare_shader_buffer_pcollector);
     PStatGPUTimer timer(this, _prepare_shader_buffer_pcollector);
 
 
-    CLP(BufferContext) *gbc = new CLP(BufferContext)(this, _prepared_objects);
+    CLP(BufferContext) *gbc = new CLP(BufferContext)(this, _prepared_objects, data);
     _glGenBuffers(1, &gbc->_index);
     _glGenBuffers(1, &gbc->_index);
 
 
     if (GLCAT.is_debug() && gl_debug_buffers) {
     if (GLCAT.is_debug() && gl_debug_buffers) {
@@ -7049,6 +7049,16 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z,
       } else {
       } else {
         format = Texture::F_srgb;
         format = Texture::F_srgb;
       }
       }
+    } else if (_current_properties->get_float_color()) {
+      if (_current_properties->get_alpha_bits()) {
+        format = Texture::F_rgba32;
+      } else if (_current_properties->get_blue_bits()) {
+        format = Texture::F_rgb32;
+      } else if (_current_properties->get_green_bits()) {
+        format = Texture::F_rg32;
+      } else {
+        format = Texture::F_r32;
+      }
     } else {
     } else {
       if (_current_properties->get_alpha_bits()) {
       if (_current_properties->get_alpha_bits()) {
         format = Texture::F_rgba;
         format = Texture::F_rgba;
@@ -7058,7 +7068,11 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z,
     }
     }
     if (_current_properties->get_float_color()) {
     if (_current_properties->get_float_color()) {
       component_type = Texture::T_float;
       component_type = Texture::T_float;
-    } else if (_current_properties->get_color_bits() <= 24) {
+    } else if (_current_properties->get_color_bits() <= 24
+            && _current_properties->get_red_bits() <= 8
+            && _current_properties->get_green_bits() <= 8
+            && _current_properties->get_blue_bits() <= 8
+            && _current_properties->get_alpha_bits() <= 8) {
       component_type = Texture::T_unsigned_byte;
       component_type = Texture::T_unsigned_byte;
     } else {
     } else {
       component_type = Texture::T_unsigned_short;
       component_type = Texture::T_unsigned_short;

+ 12 - 2
panda/src/glstuff/glShaderContext_src.cxx

@@ -2797,14 +2797,24 @@ update_shader_texture_bindings(ShaderContext *prev) {
     }
     }
 
 
     if (tex->get_texture_type() != spec._desired_type) {
     if (tex->get_texture_type() != spec._desired_type) {
-      if (id != nullptr) {
+      switch (spec._part) {
+      case Shader::STO_named_input:
         GLCAT.error()
         GLCAT.error()
           << "Sampler type of GLSL shader input '" << *id << "' does not "
           << "Sampler type of GLSL shader input '" << *id << "' does not "
              "match type of texture " << *tex << ".\n";
              "match type of texture " << *tex << ".\n";
-      } else {
+        break;
+
+      case Shader::STO_stage_i:
         GLCAT.error()
         GLCAT.error()
           << "Sampler type of GLSL shader input p3d_Texture" << spec._stage
           << "Sampler type of GLSL shader input p3d_Texture" << spec._stage
           << " does not match type of texture " << *tex << ".\n";
           << " does not match type of texture " << *tex << ".\n";
+        break;
+
+      case Shader::STO_light_i_shadow_map:
+        GLCAT.error()
+          << "Sampler type of GLSL shader input p3d_LightSource[" << spec._stage
+          << "].shadowMap does not match type of texture " << *tex << ".\n";
+        break;
       }
       }
       // TODO: also check whether shadow sampler textures have shadow filter
       // TODO: also check whether shadow sampler textures have shadow filter
       // enabled.
       // enabled.

+ 8 - 0
panda/src/gobj/bufferContext.I

@@ -11,6 +11,14 @@
  * @date 2006-03-16
  * @date 2006-03-16
  */
  */
 
 
+/**
+ * Returns the associated object.
+ */
+INLINE TypedWritableReferenceCount *BufferContext::
+get_object() const {
+  return _object;
+}
+
 /**
 /**
  * Returns the number of bytes previously reported for the data object.  This
  * Returns the number of bytes previously reported for the data object.  This
  * is used to track changes in the data object's allocated size; if it changes
  * is used to track changes in the data object's allocated size; if it changes

+ 2 - 1
panda/src/gobj/bufferContext.cxx

@@ -19,7 +19,8 @@ TypeHandle BufferContext::_type_handle;
  *
  *
  */
  */
 BufferContext::
 BufferContext::
-BufferContext(BufferResidencyTracker *residency) :
+BufferContext(BufferResidencyTracker *residency, TypedWritableReferenceCount *object) :
+  _object(object),
   _residency(residency),
   _residency(residency),
   _residency_state(0),
   _residency_state(0),
   _data_size_bytes(0),
   _data_size_bytes(0),

+ 11 - 1
panda/src/gobj/bufferContext.h

@@ -23,6 +23,7 @@
 #include "bufferResidencyTracker.h"
 #include "bufferResidencyTracker.h"
 
 
 class PreparedGraphicsObjects;
 class PreparedGraphicsObjects;
+class TypedWritableReferenceCount;
 
 
 /**
 /**
  * This is a base class for those kinds of SavedContexts that occupy an
  * This is a base class for those kinds of SavedContexts that occupy an
@@ -36,15 +37,19 @@ class PreparedGraphicsObjects;
  */
  */
 class EXPCL_PANDA_GOBJ BufferContext : public SavedContext, private LinkedListNode {
 class EXPCL_PANDA_GOBJ BufferContext : public SavedContext, private LinkedListNode {
 public:
 public:
-  BufferContext(BufferResidencyTracker *residency);
+  BufferContext(BufferResidencyTracker *residency, TypedWritableReferenceCount *object);
   virtual ~BufferContext();
   virtual ~BufferContext();
 
 
+  INLINE TypedWritableReferenceCount *get_object() const;
+
 PUBLISHED:
 PUBLISHED:
   INLINE size_t get_data_size_bytes() const;
   INLINE size_t get_data_size_bytes() const;
   INLINE UpdateSeq get_modified() const;
   INLINE UpdateSeq get_modified() const;
   INLINE bool get_active() const;
   INLINE bool get_active() const;
   INLINE bool get_resident() const;
   INLINE bool get_resident() const;
 
 
+  MAKE_PROPERTY(object, get_object);
+
   MAKE_PROPERTY(data_size_bytes, get_data_size_bytes);
   MAKE_PROPERTY(data_size_bytes, get_data_size_bytes);
   MAKE_PROPERTY(modified, get_modified);
   MAKE_PROPERTY(modified, get_modified);
   MAKE_PROPERTY(active, get_active);
   MAKE_PROPERTY(active, get_active);
@@ -62,6 +67,11 @@ public:
 private:
 private:
   void set_owning_chain(BufferContextChain *chain);
   void set_owning_chain(BufferContextChain *chain);
 
 
+protected:
+  // This cannot be a PT(), because the object and the GSG both own their
+  // BufferContexts!  That would create a circular reference count.
+  TypedWritableReferenceCount *_object;
+
 private:
 private:
   BufferResidencyTracker *_residency;
   BufferResidencyTracker *_residency;
   int _residency_state;
   int _residency_state;

+ 7 - 8
panda/src/gobj/indexBufferContext.I

@@ -16,9 +16,8 @@
  */
  */
 INLINE IndexBufferContext::
 INLINE IndexBufferContext::
 IndexBufferContext(PreparedGraphicsObjects *pgo, GeomPrimitive *data) :
 IndexBufferContext(PreparedGraphicsObjects *pgo, GeomPrimitive *data) :
-  BufferContext(&pgo->_ibuffer_residency),
-  AdaptiveLruPage(0),
-  _data(data)
+  BufferContext(&pgo->_ibuffer_residency, data),
+  AdaptiveLruPage(0)
 {
 {
 }
 }
 
 
@@ -27,7 +26,7 @@ IndexBufferContext(PreparedGraphicsObjects *pgo, GeomPrimitive *data) :
  */
  */
 INLINE GeomPrimitive *IndexBufferContext::
 INLINE GeomPrimitive *IndexBufferContext::
 get_data() const {
 get_data() const {
-  return _data;
+  return (GeomPrimitive *)_object;
 }
 }
 
 
 /**
 /**
@@ -36,7 +35,7 @@ get_data() const {
  */
  */
 INLINE bool IndexBufferContext::
 INLINE bool IndexBufferContext::
 changed_size(const GeomPrimitivePipelineReader *reader) const {
 changed_size(const GeomPrimitivePipelineReader *reader) const {
-  nassertr(reader->get_object() == _data, false);
+  nassertr(reader->get_object() == get_data(), false);
   return get_data_size_bytes() != (size_t)reader->get_data_size_bytes();
   return get_data_size_bytes() != (size_t)reader->get_data_size_bytes();
 }
 }
 
 
@@ -46,7 +45,7 @@ changed_size(const GeomPrimitivePipelineReader *reader) const {
  */
  */
 INLINE bool IndexBufferContext::
 INLINE bool IndexBufferContext::
 changed_usage_hint(const GeomPrimitivePipelineReader *reader) const {
 changed_usage_hint(const GeomPrimitivePipelineReader *reader) const {
-  nassertr(reader->get_object() == _data, false);
+  nassertr(reader->get_object() == get_data(), false);
   return _usage_hint != reader->get_usage_hint();
   return _usage_hint != reader->get_usage_hint();
 }
 }
 
 
@@ -56,7 +55,7 @@ changed_usage_hint(const GeomPrimitivePipelineReader *reader) const {
  */
  */
 INLINE bool IndexBufferContext::
 INLINE bool IndexBufferContext::
 was_modified(const GeomPrimitivePipelineReader *reader) const {
 was_modified(const GeomPrimitivePipelineReader *reader) const {
-  nassertr(reader->get_object() == _data, false);
+  nassertr(reader->get_object() == get_data(), false);
   return get_modified() != reader->get_modified();
   return get_modified() != reader->get_modified();
 }
 }
 
 
@@ -76,7 +75,7 @@ update_data_size_bytes(size_t new_data_size_bytes) {
  */
  */
 INLINE void IndexBufferContext::
 INLINE void IndexBufferContext::
 mark_loaded(const GeomPrimitivePipelineReader *reader) {
 mark_loaded(const GeomPrimitivePipelineReader *reader) {
-  nassertv(reader->get_object() == _data);
+  nassertv(reader->get_object() == get_data());
   update_data_size_bytes(reader->get_data_size_bytes());
   update_data_size_bytes(reader->get_data_size_bytes());
   update_modified(reader->get_modified());
   update_modified(reader->get_modified());
   _usage_hint = reader->get_usage_hint();
   _usage_hint = reader->get_usage_hint();

+ 0 - 3
panda/src/gobj/indexBufferContext.h

@@ -50,9 +50,6 @@ public:
   virtual void write(std::ostream &out, int indent_level) const;
   virtual void write(std::ostream &out, int indent_level) const;
 
 
 private:
 private:
-  // This cannot be a PT(GeomPrimitive), because the data and the GSG both own
-  // their IndexBufferContexts!  That would create a circular reference count.
-  GeomPrimitive *_data;
   GeomEnums::UsageHint _usage_hint;
   GeomEnums::UsageHint _usage_hint;
 
 
 public:
 public:

+ 25 - 18
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -270,11 +270,11 @@ void PreparedGraphicsObjects::
 release_texture(TextureContext *tc) {
 release_texture(TextureContext *tc) {
   ReMutexHolder holder(_lock);
   ReMutexHolder holder(_lock);
 
 
-  tc->_texture->clear_prepared(tc->get_view(), this);
+  tc->get_texture()->clear_prepared(tc->get_view(), this);
 
 
   // We have to set the Texture pointer to NULL at this point, since the
   // We have to set the Texture pointer to NULL at this point, since the
   // Texture itself might destruct at any time after it has been released.
   // Texture itself might destruct at any time after it has been released.
-  tc->_texture = nullptr;
+  tc->_object = nullptr;
 
 
   bool removed = (_prepared_textures.erase(tc) != 0);
   bool removed = (_prepared_textures.erase(tc) != 0);
   nassertv(removed);
   nassertv(removed);
@@ -307,8 +307,8 @@ release_all_textures() {
        tci != _prepared_textures.end();
        tci != _prepared_textures.end();
        ++tci) {
        ++tci) {
     TextureContext *tc = (*tci);
     TextureContext *tc = (*tci);
-    tc->_texture->clear_prepared(tc->get_view(), this);
-    tc->_texture = nullptr;
+    tc->get_texture()->clear_prepared(tc->get_view(), this);
+    tc->_object = nullptr;
 
 
     _released_textures.insert(tc);
     _released_textures.insert(tc);
   }
   }
@@ -946,14 +946,14 @@ void PreparedGraphicsObjects::
 release_vertex_buffer(VertexBufferContext *vbc) {
 release_vertex_buffer(VertexBufferContext *vbc) {
   ReMutexHolder holder(_lock);
   ReMutexHolder holder(_lock);
 
 
-  vbc->_data->clear_prepared(this);
+  vbc->get_data()->clear_prepared(this);
 
 
-  size_t data_size_bytes = vbc->_data->get_data_size_bytes();
-  GeomEnums::UsageHint usage_hint = vbc->_data->get_usage_hint();
+  size_t data_size_bytes = vbc->get_data()->get_data_size_bytes();
+  GeomEnums::UsageHint usage_hint = vbc->get_data()->get_usage_hint();
 
 
   // We have to set the Data pointer to NULL at this point, since the Data
   // We have to set the Data pointer to NULL at this point, since the Data
   // itself might destruct at any time after it has been released.
   // itself might destruct at any time after it has been released.
-  vbc->_data = nullptr;
+  vbc->_object = nullptr;
 
 
   bool removed = (_prepared_vertex_buffers.erase(vbc) != 0);
   bool removed = (_prepared_vertex_buffers.erase(vbc) != 0);
   nassertv(removed);
   nassertv(removed);
@@ -985,8 +985,8 @@ release_all_vertex_buffers() {
        vbci != _prepared_vertex_buffers.end();
        vbci != _prepared_vertex_buffers.end();
        ++vbci) {
        ++vbci) {
     VertexBufferContext *vbc = (VertexBufferContext *)(*vbci);
     VertexBufferContext *vbc = (VertexBufferContext *)(*vbci);
-    vbc->_data->clear_prepared(this);
-    vbc->_data = nullptr;
+    vbc->get_data()->clear_prepared(this);
+    vbc->_object = nullptr;
 
 
     _released_vertex_buffers.insert(vbc);
     _released_vertex_buffers.insert(vbc);
   }
   }
@@ -1061,7 +1061,7 @@ prepare_vertex_buffer_now(GeomVertexArrayData *data, GraphicsStateGuardianBase *
                       _vertex_buffer_cache, _vertex_buffer_cache_lru,
                       _vertex_buffer_cache, _vertex_buffer_cache_lru,
                       _vertex_buffer_cache_size);
                       _vertex_buffer_cache_size);
   if (vbc != nullptr) {
   if (vbc != nullptr) {
-    vbc->_data = data;
+    vbc->_object = data;
 
 
   } else {
   } else {
     // Ask the GSG to create a brand new VertexBufferContext.  There might be
     // Ask the GSG to create a brand new VertexBufferContext.  There might be
@@ -1144,14 +1144,14 @@ void PreparedGraphicsObjects::
 release_index_buffer(IndexBufferContext *ibc) {
 release_index_buffer(IndexBufferContext *ibc) {
   ReMutexHolder holder(_lock);
   ReMutexHolder holder(_lock);
 
 
-  ibc->_data->clear_prepared(this);
+  ibc->get_data()->clear_prepared(this);
 
 
-  size_t data_size_bytes = ibc->_data->get_data_size_bytes();
-  GeomEnums::UsageHint usage_hint = ibc->_data->get_usage_hint();
+  size_t data_size_bytes = ibc->get_data()->get_data_size_bytes();
+  GeomEnums::UsageHint usage_hint = ibc->get_data()->get_usage_hint();
 
 
   // We have to set the Data pointer to NULL at this point, since the Data
   // We have to set the Data pointer to NULL at this point, since the Data
   // itself might destruct at any time after it has been released.
   // itself might destruct at any time after it has been released.
-  ibc->_data = nullptr;
+  ibc->_object = nullptr;
 
 
   bool removed = (_prepared_index_buffers.erase(ibc) != 0);
   bool removed = (_prepared_index_buffers.erase(ibc) != 0);
   nassertv(removed);
   nassertv(removed);
@@ -1183,8 +1183,8 @@ release_all_index_buffers() {
        ibci != _prepared_index_buffers.end();
        ibci != _prepared_index_buffers.end();
        ++ibci) {
        ++ibci) {
     IndexBufferContext *ibc = (IndexBufferContext *)(*ibci);
     IndexBufferContext *ibc = (IndexBufferContext *)(*ibci);
-    ibc->_data->clear_prepared(this);
-    ibc->_data = nullptr;
+    ibc->get_data()->clear_prepared(this);
+    ibc->_object = nullptr;
 
 
     _released_index_buffers.insert(ibc);
     _released_index_buffers.insert(ibc);
   }
   }
@@ -1258,7 +1258,7 @@ prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg) {
                       _index_buffer_cache, _index_buffer_cache_lru,
                       _index_buffer_cache, _index_buffer_cache_lru,
                       _index_buffer_cache_size);
                       _index_buffer_cache_size);
   if (ibc != nullptr) {
   if (ibc != nullptr) {
-    ibc->_data = data;
+    ibc->_object = data;
 
 
   } else {
   } else {
     // Ask the GSG to create a brand new IndexBufferContext.  There might be
     // Ask the GSG to create a brand new IndexBufferContext.  There might be
@@ -1341,6 +1341,13 @@ void PreparedGraphicsObjects::
 release_shader_buffer(BufferContext *bc) {
 release_shader_buffer(BufferContext *bc) {
   ReMutexHolder holder(_lock);
   ReMutexHolder holder(_lock);
 
 
+  ShaderBuffer *buffer = (ShaderBuffer *)bc->_object;
+  buffer->clear_prepared(this);
+
+  // We have to set the ShaderBuffer pointer to NULL at this point, since the
+  // buffer itself might destruct at any time after it has been released.
+  bc->_object = nullptr;
+
   bool removed = (_prepared_shader_buffers.erase(bc) != 0);
   bool removed = (_prepared_shader_buffers.erase(bc) != 0);
   nassertv(removed);
   nassertv(removed);
 
 

+ 25 - 0
panda/src/gobj/shaderBuffer.cxx

@@ -143,6 +143,31 @@ release_all() {
   return num_freed;
   return num_freed;
 }
 }
 
 
+/**
+ * Removes the indicated PreparedGraphicsObjects table from the buffer's
+ * table, without actually releasing the texture.  This is intended to be
+ * called only from PreparedGraphicsObjects::release_shader_buffer(); it
+ * should never be called by user code.
+ */
+void ShaderBuffer::
+clear_prepared(PreparedGraphicsObjects *prepared_objects) {
+  nassertv(_contexts != nullptr);
+
+  Contexts::iterator ci;
+  ci = _contexts->find(prepared_objects);
+  if (ci != _contexts->end()) {
+    _contexts->erase(ci);
+    if (_contexts->empty()) {
+      delete _contexts;
+      _contexts = nullptr;
+    }
+  } else {
+    // If this assertion fails, clear_prepared() was given a prepared_objects
+    // which the data array didn't know about.
+    nassert_raise("unknown PreparedGraphicsObjects");
+  }
+}
+
 /**
 /**
  * Tells the BamReader how to create objects of type ParamValue.
  * Tells the BamReader how to create objects of type ParamValue.
  */
  */

+ 5 - 0
panda/src/gobj/shaderBuffer.h

@@ -57,6 +57,9 @@ PUBLISHED:
   bool release(PreparedGraphicsObjects *prepared_objects);
   bool release(PreparedGraphicsObjects *prepared_objects);
   int release_all();
   int release_all();
 
 
+private:
+  void clear_prepared(PreparedGraphicsObjects *prepared_objects);
+
 private:
 private:
   uint64_t _data_size_bytes;
   uint64_t _data_size_bytes;
   UsageHint _usage_hint;
   UsageHint _usage_hint;
@@ -91,6 +94,8 @@ public:
 
 
 private:
 private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
+
+  friend class PreparedGraphicsObjects;
 };
 };
 
 
 INLINE std::ostream &operator << (std::ostream &out, const ShaderBuffer &m) {
 INLINE std::ostream &operator << (std::ostream &out, const ShaderBuffer &m) {

+ 9 - 10
panda/src/gobj/textureContext.I

@@ -16,9 +16,8 @@
  */
  */
 INLINE TextureContext::
 INLINE TextureContext::
 TextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view) :
 TextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view) :
-  BufferContext(&pgo->_texture_residency),
+  BufferContext(&pgo->_texture_residency, tex),
   AdaptiveLruPage(0),
   AdaptiveLruPage(0),
-  _texture(tex),
   _view(view)
   _view(view)
 {
 {
 }
 }
@@ -28,7 +27,7 @@ TextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view) :
  */
  */
 INLINE Texture *TextureContext::
 INLINE Texture *TextureContext::
 get_texture() const {
 get_texture() const {
-  return _texture;
+  return (Texture *)_object;
 }
 }
 
 
 /**
 /**
@@ -56,7 +55,7 @@ was_modified() const {
  */
  */
 INLINE bool TextureContext::
 INLINE bool TextureContext::
 was_properties_modified() const {
 was_properties_modified() const {
-  return _properties_modified != _texture->get_properties_modified();
+  return _properties_modified != get_texture()->get_properties_modified();
 }
 }
 
 
 /**
 /**
@@ -65,7 +64,7 @@ was_properties_modified() const {
  */
  */
 INLINE bool TextureContext::
 INLINE bool TextureContext::
 was_image_modified() const {
 was_image_modified() const {
-  return _image_modified != _texture->get_image_modified();
+  return _image_modified != get_texture()->get_image_modified();
 }
 }
 
 
 /**
 /**
@@ -74,7 +73,7 @@ was_image_modified() const {
  */
  */
 INLINE bool TextureContext::
 INLINE bool TextureContext::
 was_simple_image_modified() const {
 was_simple_image_modified() const {
-  return _simple_image_modified != _texture->get_simple_image_modified();
+  return _simple_image_modified != get_texture()->get_simple_image_modified();
 }
 }
 
 
 /**
 /**
@@ -121,8 +120,8 @@ update_data_size_bytes(size_t new_data_size_bytes) {
 INLINE void TextureContext::
 INLINE void TextureContext::
 mark_loaded() {
 mark_loaded() {
   // _data_size_bytes = _data->get_texture_size_bytes();
   // _data_size_bytes = _data->get_texture_size_bytes();
-  _properties_modified = _texture->get_properties_modified();
-  _image_modified = _texture->get_image_modified();
+  _properties_modified = get_texture()->get_properties_modified();
+  _image_modified = get_texture()->get_image_modified();
   update_modified(std::max(_properties_modified, _image_modified));
   update_modified(std::max(_properties_modified, _image_modified));
 
 
   // Assume the texture is now resident.
   // Assume the texture is now resident.
@@ -135,8 +134,8 @@ mark_loaded() {
  */
  */
 INLINE void TextureContext::
 INLINE void TextureContext::
 mark_simple_loaded() {
 mark_simple_loaded() {
-  _properties_modified = _texture->get_properties_modified();
-  _simple_image_modified = _texture->get_simple_image_modified();
+  _properties_modified = get_texture()->get_properties_modified();
+  _simple_image_modified = get_texture()->get_simple_image_modified();
   update_modified(std::max(_properties_modified, _simple_image_modified));
   update_modified(std::max(_properties_modified, _simple_image_modified));
 
 
   // The texture's not exactly resident now, but some part of it is.
   // The texture's not exactly resident now, but some part of it is.

+ 0 - 3
panda/src/gobj/textureContext.h

@@ -60,9 +60,6 @@ public:
   virtual void write(std::ostream &out, int indent_level) const;
   virtual void write(std::ostream &out, int indent_level) const;
 
 
 private:
 private:
-  // This cannot be a PT(Texture), because the texture and the GSG both own
-  // their TextureContexts!  That would create a circular reference count.
-  Texture *_texture;
   int _view;
   int _view;
   UpdateSeq _properties_modified;
   UpdateSeq _properties_modified;
   UpdateSeq _image_modified;
   UpdateSeq _image_modified;

+ 19 - 0
panda/src/gobj/texturePeeker.cxx

@@ -140,6 +140,12 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) {
     _get_texel = get_texel_la;
     _get_texel = get_texel_la;
     break;
     break;
 
 
+  case Texture::F_rg16:
+  case Texture::F_rg32:
+  case Texture::F_rg:
+    _get_texel = get_texel_rg;
+    break;
+
   case Texture::F_rgb:
   case Texture::F_rgb:
   case Texture::F_rgb5:
   case Texture::F_rgb5:
   case Texture::F_rgb8:
   case Texture::F_rgb8:
@@ -148,6 +154,7 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) {
   case Texture::F_rgb332:
   case Texture::F_rgb332:
   case Texture::F_r11_g11_b10:
   case Texture::F_r11_g11_b10:
   case Texture::F_rgb9_e5:
   case Texture::F_rgb9_e5:
+  case Texture::F_rgb32:
     _get_texel = get_texel_rgb;
     _get_texel = get_texel_rgb;
     break;
     break;
 
 
@@ -582,6 +589,18 @@ get_texel_la(LColor &color, const unsigned char *&p, GetComponentFunc *get_compo
   color[3] = (*get_component)(p);
   color[3] = (*get_component)(p);
 }
 }
 
 
+/**
+ * Gets the color of the texel at byte p, given that the texture is in format
+ * F_rg or similar.
+ */
+void TexturePeeker::
+get_texel_rg(LColor &color, const unsigned char *&p, GetComponentFunc *get_component) {
+  color[0] = (*get_component)(p);
+  color[1] = (*get_component)(p);
+  color[2] = 0.0f;
+  color[3] = 1.0f;
+}
+
 /**
 /**
  * Gets the color of the texel at byte p, given that the texture is in format
  * Gets the color of the texel at byte p, given that the texture is in format
  * F_rgb or similar.
  * F_rgb or similar.

+ 1 - 0
panda/src/gobj/texturePeeker.h

@@ -77,6 +77,7 @@ private:
   static void get_texel_a(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_a(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_l(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_l(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_la(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_la(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
+  static void get_texel_rg(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_rgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_rgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_rgba(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_rgba(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_srgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);
   static void get_texel_srgb(LColor &color, const unsigned char *&p, GetComponentFunc *get_component);

+ 7 - 8
panda/src/gobj/vertexBufferContext.I

@@ -16,9 +16,8 @@
  */
  */
 INLINE VertexBufferContext::
 INLINE VertexBufferContext::
 VertexBufferContext(PreparedGraphicsObjects *pgo, GeomVertexArrayData *data) :
 VertexBufferContext(PreparedGraphicsObjects *pgo, GeomVertexArrayData *data) :
-  BufferContext(&pgo->_vbuffer_residency),
-  AdaptiveLruPage(0),
-  _data(data)
+  BufferContext(&pgo->_vbuffer_residency, data),
+  AdaptiveLruPage(0)
 {
 {
 }
 }
 
 
@@ -27,7 +26,7 @@ VertexBufferContext(PreparedGraphicsObjects *pgo, GeomVertexArrayData *data) :
  */
  */
 INLINE GeomVertexArrayData *VertexBufferContext::
 INLINE GeomVertexArrayData *VertexBufferContext::
 get_data() const {
 get_data() const {
-  return _data;
+  return (GeomVertexArrayData *)_object;
 }
 }
 
 
 /**
 /**
@@ -36,7 +35,7 @@ get_data() const {
  */
  */
 INLINE bool VertexBufferContext::
 INLINE bool VertexBufferContext::
 changed_size(const GeomVertexArrayDataHandle *reader) const {
 changed_size(const GeomVertexArrayDataHandle *reader) const {
-  nassertr(reader->get_object() == _data, false);
+  nassertr(reader->get_object() == get_data(), false);
   return get_data_size_bytes() != (size_t)reader->get_data_size_bytes();
   return get_data_size_bytes() != (size_t)reader->get_data_size_bytes();
 }
 }
 
 
@@ -46,7 +45,7 @@ changed_size(const GeomVertexArrayDataHandle *reader) const {
  */
  */
 INLINE bool VertexBufferContext::
 INLINE bool VertexBufferContext::
 changed_usage_hint(const GeomVertexArrayDataHandle *reader) const {
 changed_usage_hint(const GeomVertexArrayDataHandle *reader) const {
-  nassertr(reader->get_object() == _data, false);
+  nassertr(reader->get_object() == get_data(), false);
   return _usage_hint != reader->get_usage_hint();
   return _usage_hint != reader->get_usage_hint();
 }
 }
 
 
@@ -56,7 +55,7 @@ changed_usage_hint(const GeomVertexArrayDataHandle *reader) const {
  */
  */
 INLINE bool VertexBufferContext::
 INLINE bool VertexBufferContext::
 was_modified(const GeomVertexArrayDataHandle *reader) const {
 was_modified(const GeomVertexArrayDataHandle *reader) const {
-  nassertr(reader->get_object() == _data, false);
+  nassertr(reader->get_object() == get_data(), false);
   return get_modified() != reader->get_modified();
   return get_modified() != reader->get_modified();
 }
 }
 
 
@@ -77,7 +76,7 @@ update_data_size_bytes(size_t new_data_size_bytes) {
  */
  */
 INLINE void VertexBufferContext::
 INLINE void VertexBufferContext::
 mark_loaded(const GeomVertexArrayDataHandle *reader) {
 mark_loaded(const GeomVertexArrayDataHandle *reader) {
-  nassertv(reader->get_object() == _data);
+  nassertv(reader->get_object() == get_data());
   update_data_size_bytes(reader->get_data_size_bytes());
   update_data_size_bytes(reader->get_data_size_bytes());
   update_modified(reader->get_modified());
   update_modified(reader->get_modified());
   _usage_hint = reader->get_usage_hint();
   _usage_hint = reader->get_usage_hint();

+ 0 - 4
panda/src/gobj/vertexBufferContext.h

@@ -51,10 +51,6 @@ public:
   virtual void write(std::ostream &out, int indent_level) const;
   virtual void write(std::ostream &out, int indent_level) const;
 
 
 private:
 private:
-  // This cannot be a PT(GeomVertexArrayData), because the data and the GSG
-  // both own their VertexBufferContexts!  That would create a circular
-  // reference count.
-  GeomVertexArrayData *_data;
   GeomEnums::UsageHint _usage_hint;
   GeomEnums::UsageHint _usage_hint;
 
 
 public:
 public:

+ 4 - 7
panda/src/pgraph/camera.cxx

@@ -280,13 +280,10 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: Camera::complete_pointers
-//       Access: Public, Virtual
-//  Description: Receives an array of pointers, one for each time
-//               manager->read_pointer() was called in fillin().
-//               Returns the number of pointers processed.
-////////////////////////////////////////////////////////////////////
+/**
+ * Receives an array of pointers, one for each time manager->read_pointer()
+ * was called in fillin(). Returns the number of pointers processed.
+ */
 int Camera::
 int Camera::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = LensNode::complete_pointers(p_list, manager);
   int pi = LensNode::complete_pointers(p_list, manager);

+ 12 - 5
panda/src/pgraph/pandaNode.cxx

@@ -1084,6 +1084,8 @@ set_effects(const RenderEffects *effects, Thread *current_thread) {
  */
  */
 void PandaNode::
 void PandaNode::
 set_transform(const TransformState *transform, Thread *current_thread) {
 set_transform(const TransformState *transform, Thread *current_thread) {
+  nassertv(!transform->is_invalid());
+
   // Need to have this held before we grab any other locks.
   // Need to have this held before we grab any other locks.
   LightMutexHolder holder(_dirty_prev_transforms._lock);
   LightMutexHolder holder(_dirty_prev_transforms._lock);
 
 
@@ -1120,6 +1122,8 @@ set_transform(const TransformState *transform, Thread *current_thread) {
  */
  */
 void PandaNode::
 void PandaNode::
 set_prev_transform(const TransformState *transform, Thread *current_thread) {
 set_prev_transform(const TransformState *transform, Thread *current_thread) {
+  nassertv(!transform->is_invalid());
+
   // Need to have this held before we grab any other locks.
   // Need to have this held before we grab any other locks.
   LightMutexHolder holder(_dirty_prev_transforms._lock);
   LightMutexHolder holder(_dirty_prev_transforms._lock);
 
 
@@ -3510,12 +3514,12 @@ update_cached(bool update_bounds, int pipeline_stage, PandaNode::CDLockedStageRe
             const BoundingVolume **child_begin = &child_volumes[0];
             const BoundingVolume **child_begin = &child_volumes[0];
             const BoundingVolume **child_end = child_begin + child_volumes_i;
             const BoundingVolume **child_end = child_begin + child_volumes_i;
             ((BoundingVolume *)gbv)->around(child_begin, child_end);
             ((BoundingVolume *)gbv)->around(child_begin, child_end);
-          }
 
 
-          // If we have a transform, apply it to the bounding volume we just
-          // computed.
-          if (!transform->is_identity()) {
-            gbv->xform(transform->get_mat());
+            // If we have a transform, apply it to the bounding volume we just
+            // computed.
+            if (!transform->is_identity()) {
+              gbv->xform(transform->get_mat());
+            }
           }
           }
 
 
           cdataw->_external_bounds = gbv;
           cdataw->_external_bounds = gbv;
@@ -3849,6 +3853,9 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
   // Mark the bounds stale.
   // Mark the bounds stale.
   ++_next_update;
   ++_next_update;
 
 
+  nassertr(!_transform->is_invalid(), pi);
+  nassertr(!_prev_transform->is_invalid(), pi);
+
   return pi;
   return pi;
 }
 }
 
 

部分文件因为文件数量过多而无法显示