Quellcode durchsuchen

Merge branch 'master' into deploy-ng

Mitchell Stokes vor 8 Jahren
Ursprung
Commit
fcb1d9308b
100 geänderte Dateien mit 2197 neuen und 612 gelöschten Zeilen
  1. 5 1
      .gitignore
  2. 8 1
      .travis.yml
  3. 11 0
      README.md
  4. 1 1
      contrib/src/ai/aiCharacter.h
  5. 1 1
      contrib/src/ai/aiNode.h
  6. 1 1
      contrib/src/ai/flock.h
  7. 2 2
      direct/src/distributed/cConnectionRepository.h
  8. 1 1
      direct/src/interval/ActorInterval.py
  9. 3 3
      direct/src/interval/cConstrainHprInterval.h
  10. 4 4
      direct/src/interval/cConstrainPosHprInterval.h
  11. 3 3
      direct/src/interval/cConstrainPosInterval.h
  12. 3 3
      direct/src/interval/cConstrainTransformInterval.h
  13. 2 2
      direct/src/interval/cLerpAnimEffectInterval.h
  14. 4 4
      direct/src/interval/cLerpNodePathInterval.h
  15. 1 1
      direct/src/interval/cMetaInterval.h
  16. 1 1
      direct/src/interval/hideInterval.h
  17. 1 1
      direct/src/interval/showInterval.h
  18. 1 1
      direct/src/interval/waitInterval.h
  19. 42 4
      direct/src/showbase/MirrorDemo.py
  20. 2 2
      direct/src/showbase/Transitions.py
  21. 8 0
      dtool/src/cppparser/cppExtensionType.cxx
  22. 1 0
      dtool/src/cppparser/cppExtensionType.h
  23. 2 0
      dtool/src/cppparser/cppFunctionType.h
  24. 19 8
      dtool/src/cppparser/cppInstance.cxx
  25. 8 0
      dtool/src/cppparser/cppPointerType.cxx
  26. 1 0
      dtool/src/cppparser/cppPointerType.h
  27. 8 0
      dtool/src/cppparser/cppSimpleType.cxx
  28. 1 0
      dtool/src/cppparser/cppSimpleType.h
  29. 176 0
      dtool/src/cppparser/cppStructType.cxx
  30. 7 1
      dtool/src/cppparser/cppStructType.h
  31. 8 0
      dtool/src/cppparser/cppType.cxx
  32. 1 0
      dtool/src/cppparser/cppType.h
  33. 8 0
      dtool/src/cppparser/cppTypedefType.cxx
  34. 1 0
      dtool/src/cppparser/cppTypedefType.h
  35. 3 3
      dtool/src/dtoolutil/pandaFileStream.h
  36. 93 122
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  37. 0 52
      dtool/src/interrogate/typeManager.cxx
  38. 0 1
      dtool/src/interrogate/typeManager.h
  39. 1 1
      dtool/src/interrogatedb/py_panda.I
  40. 615 150
      dtool/src/interrogatedb/py_panda.cxx
  41. 7 4
      dtool/src/interrogatedb/py_panda.h
  42. 9 0
      dtool/src/parser-inc/iostream
  43. 1 1
      dtool/src/prc/configVariable.h
  44. 4 4
      dtool/src/prc/encryptStream.h
  45. 1 1
      dtool/src/prc/streamReader.h
  46. 3 3
      dtool/src/prc/streamWrapper.h
  47. 1 1
      dtool/src/prc/streamWriter.h
  48. 4 0
      dtool/src/pystub/pystub.cxx
  49. 41 17
      makepanda/makepanda.py
  50. 145 30
      makepanda/makepandacore.py
  51. 10 23
      makepanda/makewheel.py
  52. 3 2
      panda/src/audio/audioLoadRequest.h
  53. 1 2
      panda/src/bullet/bulletBaseCharacterControllerNode.h
  54. 11 1
      panda/src/bullet/bulletBoxShape.I
  55. 7 6
      panda/src/bullet/bulletBoxShape.cxx
  56. 3 2
      panda/src/bullet/bulletBoxShape.h
  57. 27 6
      panda/src/bullet/bulletCapsuleShape.I
  58. 79 1
      panda/src/bullet/bulletCapsuleShape.cxx
  59. 20 4
      panda/src/bullet/bulletCapsuleShape.h
  60. 2 2
      panda/src/bullet/bulletCharacterControllerNode.h
  61. 19 7
      panda/src/bullet/bulletConeShape.I
  62. 79 1
      panda/src/bullet/bulletConeShape.cxx
  63. 14 1
      panda/src/bullet/bulletConeShape.h
  64. 6 6
      panda/src/bullet/bulletConeTwistConstraint.h
  65. 5 1
      panda/src/bullet/bulletConvexHullShape.cxx
  66. 11 0
      panda/src/bullet/bulletConvexPointCloudShape.I
  67. 71 1
      panda/src/bullet/bulletConvexPointCloudShape.cxx
  68. 14 2
      panda/src/bullet/bulletConvexPointCloudShape.h
  69. 11 1
      panda/src/bullet/bulletCylinderShape.I
  70. 81 1
      panda/src/bullet/bulletCylinderShape.cxx
  71. 14 2
      panda/src/bullet/bulletCylinderShape.h
  72. 1 2
      panda/src/bullet/bulletDebugNode.h
  73. 8 9
      panda/src/bullet/bulletGenericConstraint.h
  74. 1 2
      panda/src/bullet/bulletGhostNode.h
  75. 22 7
      panda/src/bullet/bulletHeightfieldShape.I
  76. 85 2
      panda/src/bullet/bulletHeightfieldShape.cxx
  77. 14 2
      panda/src/bullet/bulletHeightfieldShape.h
  78. 20 21
      panda/src/bullet/bulletHingeConstraint.h
  79. 10 9
      panda/src/bullet/bulletMinkowskiSumShape.I
  80. 102 4
      panda/src/bullet/bulletMinkowskiSumShape.cxx
  81. 18 4
      panda/src/bullet/bulletMinkowskiSumShape.h
  82. 76 0
      panda/src/bullet/bulletMultiSphereShape.cxx
  83. 11 1
      panda/src/bullet/bulletMultiSphereShape.h
  84. 4 1
      panda/src/bullet/bulletPlaneShape.cxx
  85. 1 1
      panda/src/bullet/bulletPlaneShape.h
  86. 3 0
      panda/src/bullet/bulletRigidBodyNode.cxx
  87. 8 9
      panda/src/bullet/bulletSliderConstraint.h
  88. 5 4
      panda/src/bullet/bulletSphereShape.I
  89. 8 4
      panda/src/bullet/bulletSphereShape.cxx
  90. 2 1
      panda/src/bullet/bulletSphereShape.h
  91. 6 7
      panda/src/bullet/bulletSphericalConstraint.h
  92. 7 1
      panda/src/bullet/bulletTriangleMeshShape.cxx
  93. 4 1
      panda/src/bullet/bulletTriangleMeshShape.h
  94. 7 0
      panda/src/bullet/config_bullet.cxx
  95. 1 1
      panda/src/chan/animBundle.h
  96. 1 1
      panda/src/chan/animBundleNode.h
  97. 1 1
      panda/src/chan/animChannelMatrixXfmTable.h
  98. 1 1
      panda/src/chan/animGroup.h
  99. 7 7
      panda/src/chan/bindAnimRequest.h
  100. 1 1
      panda/src/chan/partBundle.h

+ 5 - 1
.gitignore

@@ -52,6 +52,10 @@ Thumbs.db
 ehthumbs.db
 
 # Python
-__pycache__
+__pycache__/
 *.pyc
 *.pyo
+
+# Test tool cache directories
+.tox/
+.cache/

+ 8 - 1
.travis.yml

@@ -34,9 +34,16 @@ addons:
     - nvidia-cg-toolkit
     - python-dev
     - python3-dev
+    - python-virtualenv
     - zlib1g-dev
     - fakeroot
-script: $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT $FLAGS --threads 4 && LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV makepanda/test_imports.py
+install:
+    - virtualenv --python=$PYTHONV venv && source venv/bin/activate
+    - $PYTHONV -m pip install pytest
+script:
+    - $PYTHONV makepanda/makepanda.py --everything --git-commit $TRAVIS_COMMIT $FLAGS --threads 4
+    - LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV makepanda/test_imports.py
+    - LD_LIBRARY_PATH=built/lib PYTHONPATH=built $PYTHONV -m pytest tests
 notifications:
   irc:
     channels:

+ 11 - 0
README.md

@@ -156,6 +156,17 @@ python3.6 makepanda/makepanda.py --everything --installer --no-egl --no-gles --n
 If successful, this will produce a .pkg file in the root of the source
 directory which you can install using `pkg install`.
 
+Running Tests
+=============
+
+Install [PyTest](https://docs.pytest.org/en/latest/getting-started.html#installation)
+and run the `pytest` command.  If you have not installed Panda3D, you will
+need to configure your enviroment by pointing the `PYTHONPATH` variable at
+the `built` directory.  On Linux, you will also need to point the
+`LD_LIBRARY_PATH` variable at the `built/lib` directory.
+
+As a convenience, you can alternatively pass the `--tests` option to makepanda.
+
 Reporting Issues
 ================
 

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

@@ -62,7 +62,7 @@ PUBLISHED:
     // This function is used to enable or disable the guides for path finding.
     void set_pf_guide(bool pf_guide);
 
-    AICharacter(string model_name, NodePath model_np, double mass, double movt_force, double max_force);
+    explicit AICharacter(string model_name, NodePath model_np, double mass, double movt_force, double max_force);
     ~AICharacter();
 };
 

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

@@ -66,7 +66,7 @@ public:
   AINode *_next;
 
 PUBLISHED:
-  AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h);
+  explicit AINode(int grid_x, int grid_y, LVecBase3 pos, float w, float l, float h);
   ~AINode();
 
   bool contains(float x, float y);

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

@@ -44,7 +44,7 @@ public:
   AICharList _ai_char_list;
 
 PUBLISHED:
-  Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, unsigned int separation_wt = 2,
+  explicit Flock(unsigned int flock_id, double vcone_angle, double vcone_radius, unsigned int separation_wt = 2,
     unsigned int cohesion_wt = 4, unsigned int alignment_wt = 1);
   ~Flock();
 

+ 2 - 2
direct/src/distributed/cConnectionRepository.h

@@ -55,8 +55,8 @@ class SocketStream;
  */
 class EXPCL_DIRECT CConnectionRepository {
 PUBLISHED:
-  CConnectionRepository(bool has_owner_view = false,
-                        bool threaded_net = false);
+  explicit CConnectionRepository(bool has_owner_view = false,
+                                 bool threaded_net = false);
   ~CConnectionRepository();
 
 /*

+ 1 - 1
direct/src/interval/ActorInterval.py

@@ -83,7 +83,7 @@ class ActorInterval(Interval.Interval):
                 if startTime == None:
                     startTime = float(self.startFrame) / float(self.frameRate)
                 endTime = startTime + duration
-                self.endFrame = duration * self.frameRate
+                self.endFrame = endTime * self.frameRate
             else:
                 # No end frame specified.  Choose the maximum of all
                 # of the controls' numbers of frames.

+ 3 - 3
direct/src/interval/cConstrainHprInterval.h

@@ -26,9 +26,9 @@
  */
 class EXPCL_DIRECT CConstrainHprInterval : public CConstraintInterval {
 PUBLISHED:
-  CConstrainHprInterval(const string &name, double duration,
-                        const NodePath &node, const NodePath &target,
-                        bool wrt, const LVecBase3 hprOffset=LVector3::zero());
+  explicit CConstrainHprInterval(const string &name, double duration,
+                                 const NodePath &node, const NodePath &target,
+                                 bool wrt, const LVecBase3 hprOffset=LVector3::zero());
 
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_target() const;

+ 4 - 4
direct/src/interval/cConstrainPosHprInterval.h

@@ -26,10 +26,10 @@
  */
 class EXPCL_DIRECT CConstrainPosHprInterval : public CConstraintInterval {
 PUBLISHED:
-  CConstrainPosHprInterval(const string &name, double duration,
-                           const NodePath &node, const NodePath &target,
-                           bool wrt, const LVecBase3 posOffset=LVector3::zero(),
-                           const LVecBase3 hprOffset=LVector3::zero());
+  explicit CConstrainPosHprInterval(const string &name, double duration,
+                                    const NodePath &node, const NodePath &target,
+                                    bool wrt, const LVecBase3 posOffset=LVector3::zero(),
+                                    const LVecBase3 hprOffset=LVector3::zero());
 
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_target() const;

+ 3 - 3
direct/src/interval/cConstrainPosInterval.h

@@ -25,9 +25,9 @@
  */
 class EXPCL_DIRECT CConstrainPosInterval : public CConstraintInterval {
 PUBLISHED:
-  CConstrainPosInterval(const string &name, double duration,
-                        const NodePath &node, const NodePath &target,
-                        bool wrt, const LVecBase3 posOffset=LVector3::zero());
+  explicit CConstrainPosInterval(const string &name, double duration,
+                                 const NodePath &node, const NodePath &target,
+                                 bool wrt, const LVecBase3 posOffset=LVector3::zero());
 
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_target() const;

+ 3 - 3
direct/src/interval/cConstrainTransformInterval.h

@@ -24,9 +24,9 @@
  */
 class EXPCL_DIRECT CConstrainTransformInterval : public CConstraintInterval {
 PUBLISHED:
-  CConstrainTransformInterval(const string &name, double duration,
-                              const NodePath &node, const NodePath &target,
-                              bool wrt);
+  explicit CConstrainTransformInterval(const string &name, double duration,
+                                       const NodePath &node,
+                                       const NodePath &target, bool wrt);
 
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_target() const;

+ 2 - 2
direct/src/interval/cLerpAnimEffectInterval.h

@@ -31,8 +31,8 @@
  */
 class EXPCL_DIRECT CLerpAnimEffectInterval : public CLerpInterval {
 PUBLISHED:
-  INLINE CLerpAnimEffectInterval(const string &name, double duration,
-                                 BlendType blend_type);
+  INLINE explicit CLerpAnimEffectInterval(const string &name, double duration,
+                                          BlendType blend_type);
 
   INLINE void add_control(AnimControl *control, const string &name,
                           float begin_effect, float end_effect);

+ 4 - 4
direct/src/interval/cLerpNodePathInterval.h

@@ -25,10 +25,10 @@
  */
 class EXPCL_DIRECT CLerpNodePathInterval : public CLerpInterval {
 PUBLISHED:
-  CLerpNodePathInterval(const string &name, double duration,
-                        BlendType blend_type, bool bake_in_start,
-                        bool fluid,
-                        const NodePath &node, const NodePath &other);
+  explicit CLerpNodePathInterval(const string &name, double duration,
+                                 BlendType blend_type, bool bake_in_start,
+                                 bool fluid,
+                                 const NodePath &node, const NodePath &other);
 
   INLINE const NodePath &get_node() const;
   INLINE const NodePath &get_other() const;

+ 1 - 1
direct/src/interval/cMetaInterval.h

@@ -31,7 +31,7 @@
  */
 class EXPCL_DIRECT CMetaInterval : public CInterval {
 PUBLISHED:
-  CMetaInterval(const string &name);
+  explicit CMetaInterval(const string &name);
   virtual ~CMetaInterval();
 
   enum RelativeStart {

+ 1 - 1
direct/src/interval/hideInterval.h

@@ -23,7 +23,7 @@
  */
 class EXPCL_DIRECT HideInterval : public CInterval {
 PUBLISHED:
-  HideInterval(const NodePath &node, const string &name = string());
+  explicit HideInterval(const NodePath &node, const string &name = string());
 
   virtual void priv_instant();
   virtual void priv_reverse_instant();

+ 1 - 1
direct/src/interval/showInterval.h

@@ -23,7 +23,7 @@
  */
 class EXPCL_DIRECT ShowInterval : public CInterval {
 PUBLISHED:
-  ShowInterval(const NodePath &node, const string &name = string());
+  explicit ShowInterval(const NodePath &node, const string &name = string());
 
   virtual void priv_instant();
   virtual void priv_reverse_instant();

+ 1 - 1
direct/src/interval/waitInterval.h

@@ -23,7 +23,7 @@
  */
 class EXPCL_DIRECT WaitInterval : public CInterval {
 PUBLISHED:
-  INLINE WaitInterval(double duration);
+  INLINE explicit WaitInterval(double duration);
 
   virtual void priv_step(double t);
 

+ 42 - 4
direct/src/showbase/MirrorDemo.py

@@ -24,7 +24,8 @@ __all__ = ['setupMirror', 'showFrustum']
 from panda3d.core import *
 from direct.task import Task
 
-def setupMirror(name, width, height, rootCamera = None):
+def setupMirror(name, width, height, rootCamera = None,
+                bufferSize = 256, clearColor = None):
     # The return value is a NodePath that contains a rectangle that
     # reflects render.  You can reparent, reposition, and rotate it
     # anywhere you like.
@@ -49,9 +50,12 @@ def setupMirror(name, width, height, rootCamera = None):
     # Now create an offscreen buffer for rendering the mirror's point
     # of view.  The parameters here control the resolution of the
     # texture.
-    buffer = base.win.makeTextureBuffer(name, 256, 256)
-    #buffer.setClearColor(base.win.getClearColor())
-    buffer.setClearColor(VBase4(0, 0, 1, 1))
+    buffer = base.win.makeTextureBuffer(name, bufferSize, bufferSize)
+    if clearColor is None:
+        buffer.setClearColor(base.win.getClearColor())
+        #buffer.setClearColor(VBase4(0, 0, 1, 1))
+    else:
+        buffer.setClearColor(clearColor)
 
     # Set up a display region on this buffer, and create a camera.
     dr = buffer.makeDisplayRegion()
@@ -85,6 +89,10 @@ def setupMirror(name, width, height, rootCamera = None):
         # Set the camera to the mirror-image position of the main camera.
         cameraNP.setMat(rootCamera.getMat(planeNP) * plane.getReflectionMat())
 
+        # Set the cameras roll to the roll of the mirror. Otherwise
+        # mirrored objects will be moved unexpectedly
+        cameraNP.setR(planeNP.getR()-180)
+
         # And reset the frustum to exactly frame the mirror's corners.
         # This is a minor detail, but it helps to provide a realistic
         # reflection and keep the subject centered.
@@ -92,6 +100,18 @@ def setupMirror(name, width, height, rootCamera = None):
         ur = cameraNP.getRelativePoint(card, Point3(width / 2.0, 0, height / 2.0))
         ll = cameraNP.getRelativePoint(card, Point3(-width / 2.0, 0, -height / 2.0))
         lr = cameraNP.getRelativePoint(card, Point3(width / 2.0, 0, -height / 2.0))
+
+        # get the distance from the mirrors camera to the mirror plane
+        camvec = planeNP.getPos() - cameraNP.getPos()
+        camdist = camvec.length()
+
+        # set the discance on the mirrors corners so it will keep correct
+        # sizes of the mirrored objects
+        ul.setY(camdist)
+        ur.setY(camdist)
+        ll.setY(camdist)
+        lr.setY(camdist)
+
         lens.setFrustumFromCorners(ul, ur, ll, lr, Lens.FCCameraPlane | Lens.FCOffAxis | Lens.FCAspectRatio)
 
         return Task.cont
@@ -116,3 +136,21 @@ def showFrustum(np):
     geomNode.addGeom(lens.makeGeometry())
     cameraNP.attachNewNode(geomNode)
 
+if __name__ == "__main__":
+    from direct.showbase.ShowBase import ShowBase
+    base = ShowBase()
+
+    panda = loader.loadModel("panda")
+    panda.setH(180)
+    panda.setPos(0, 10, -2.5)
+    panda.setScale(0.5)
+    panda.reparentTo(render)
+
+    myMirror = setupMirror("mirror", 10, 10, bufferSize=1024, clearColor=(0, 0, 1, 1))
+    myMirror.setPos(0, 15, 2.5)
+    myMirror.setH(180)
+
+    # Uncomment this to show the frustum of the camera in the mirror
+    #showFrustum(render)
+
+    base.run()

+ 2 - 2
direct/src/showbase/Transitions.py

@@ -347,7 +347,7 @@ class Transitions:
                 frameColor = (0, 0, 0, 1),
                 borderWidth = (0, 0),
                 frameSize = (-1, 1, 0, 0.2),
-                pos = (0, 0, 0.8),
+                pos = (0, 0, 1.0),
                 image = barImage,
                 image_scale = (2.25,1,.5),
                 image_pos = (0,0,.1),
@@ -362,7 +362,7 @@ class Transitions:
                 frameColor = (0, 0, 0, 1),
                 borderWidth = (0, 0),
                 frameSize = (-1, 1, 0, 0.2),
-                pos = (0, 0, -1),
+                pos = (0, 0, -1.2),
                 image = barImage,
                 image_scale = (2.25,1,.5),
                 image_pos = (0,0,.1),

+ 8 - 0
dtool/src/cppparser/cppExtensionType.cxx

@@ -131,6 +131,14 @@ is_copy_constructible() const {
   return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct);
 }
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPExtensionType::
+is_copy_assignable() const {
+  return (_type == T_enum || _type == T_enum_class || _type == T_enum_struct);
+}
+
 /**
  *
  */

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

@@ -52,6 +52,7 @@ public:
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
 
   virtual CPPDeclaration *substitute_decl(SubstDecl &subst,
                                           CPPScope *current_scope,

+ 2 - 0
dtool/src/cppparser/cppFunctionType.h

@@ -43,6 +43,8 @@ public:
     F_volatile_method   = 0x4000,
     F_lvalue_method     = 0x8000,
     F_rvalue_method     = 0x10000,
+    F_copy_assignment_operator = 0x20000,
+    F_move_assignment_operator = 0x40000,
   };
 
   CPPFunctionType(CPPType *return_type, CPPParameterList *parameters,

+ 19 - 8
dtool/src/cppparser/cppInstance.cxx

@@ -328,8 +328,8 @@ get_fully_scoped_name() const {
 
 /**
  * If this is a function type instance, checks whether the function name
- * matches the class name (or ~name), and if so, flags it as a constructor (or
- * destructor).
+ * matches the class name (or ~name), and if so, flags it as a constructor,
+ * destructor or assignment operator
  */
 void CPPInstance::
 check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) {
@@ -344,13 +344,16 @@ check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) {
     string class_name = scope->get_local_name();
 
     if (!method_name.empty() && !class_name.empty()) {
-      if (method_name == class_name) {
+      // Check either a constructor or assignment operator.
+      if (method_name == class_name || method_name == "operator =") {
         CPPType *void_type = CPPType::new_type
           (new CPPSimpleType(CPPSimpleType::T_void));
 
-        int flags = func->_flags | CPPFunctionType::F_constructor;
+        int flags = func->_flags;
+        if (method_name == class_name) {
+          flags |= CPPFunctionType::F_constructor;
+        }
 
-        // Check if it might be a copy or move constructor.
         CPPParameterList *params = func->_parameters;
         if (params->_parameters.size() == 1 && !params->_includes_ellipsis) {
           CPPType *param_type = params->_parameters[0]->_type;
@@ -360,10 +363,18 @@ check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) {
             param_type = ref_type->_pointing_at->remove_cv();
 
             if (class_name == param_type->get_simple_name()) {
-              if (ref_type->_value_category == CPPReferenceType::VC_rvalue) {
-                flags |= CPPFunctionType::F_move_constructor;
+              if (flags & CPPFunctionType::F_constructor) {
+                if (ref_type->_value_category == CPPReferenceType::VC_rvalue) {
+                  flags |= CPPFunctionType::F_move_constructor;
+                } else {
+                  flags |= CPPFunctionType::F_copy_constructor;
+                }
               } else {
-                flags |= CPPFunctionType::F_copy_constructor;
+                if (ref_type->_value_category == CPPReferenceType::VC_rvalue) {
+                  flags |= CPPFunctionType::F_move_assignment_operator;
+                } else {
+                  flags |= CPPFunctionType::F_copy_assignment_operator;
+                }
               }
             }
           }

+ 8 - 0
dtool/src/cppparser/cppPointerType.cxx

@@ -177,6 +177,14 @@ is_copy_constructible() const {
   return true;
 }
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPPointerType::
+is_copy_assignable() const {
+  return true;
+}
+
 /**
  * This is a little more forgiving than is_equal(): it returns true if the
  * types appear to be referring to the same thing, even if they may have

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

@@ -41,6 +41,7 @@ public:
   virtual bool is_constructible(const CPPType *other) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
   virtual bool is_equivalent(const CPPType &other) const;
 
   virtual void output(ostream &out, int indent_level, CPPScope *scope,

+ 8 - 0
dtool/src/cppparser/cppSimpleType.cxx

@@ -103,6 +103,14 @@ is_copy_constructible() const {
   return (_type != T_void);
 }
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPSimpleType::
+is_copy_assignable() const {
+  return (_type != T_void);
+}
+
 /**
  * Returns true if the type is destructible.
  */

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

@@ -75,6 +75,7 @@ public:
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
   virtual bool is_destructible() const;
   virtual bool is_parameter_expr() const;
 

+ 176 - 0
dtool/src/cppparser/cppStructType.cxx

@@ -435,6 +435,17 @@ is_copy_constructible() const {
   return is_copy_constructible(V_public);
 }
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPStructType::
+is_copy_assignable() const {
+  if (is_abstract()) {
+    return false;
+  }
+  return is_copy_assignable(V_public);
+}
+
 /**
  * Returns true if the type is destructible.
  */
@@ -606,6 +617,97 @@ is_move_constructible(CPPVisibility min_vis) const {
   return is_copy_constructible(min_vis);
 }
 
+/**
+ * Returns true if the type is copy-assignable, without checking whether the
+ * class is abstract.
+ */
+bool CPPStructType::
+is_copy_assignable(CPPVisibility min_vis) const {
+  CPPInstance *assignment_operator = get_copy_assignment_operator();
+  if (assignment_operator != (CPPInstance *)NULL) {
+    // It has a copy assignment operator.
+    if (assignment_operator->_vis > min_vis) {
+      // Inaccessible copy assignment operator.
+      return false;
+    }
+
+    if (assignment_operator->_storage_class & CPPInstance::SC_deleted) {
+      // Deleted copy assignment operator.
+      return false;
+    }
+
+    // NB: if it's defaulted, it may still be deleted.
+    if ((assignment_operator->_storage_class & CPPInstance::SC_defaulted) == 0) {
+      return true;
+    }
+  }
+
+  // Implicit copy assignment operator.  Check if the implicit or defaulted
+  // copy assignment operator is deleted.
+  if (!assignment_operator && (get_move_constructor() || get_move_assignment_operator())) {
+    // It's not explicitly defaulted, and there is a move constructor or move
+    // assignment operator, so the implicitly-declared one is deleted.
+    return false;
+  }
+
+  Derivation::const_iterator di;
+  for (di = _derivation.begin(); di != _derivation.end(); ++di) {
+    CPPStructType *base = (*di)._base->as_struct_type();
+    if (base != NULL) {
+      if (!base->is_copy_assignable(V_protected)) {
+        return false;
+      }
+    }
+  }
+
+  // Make sure all members are assignable.
+  CPPScope::Variables::const_iterator vi;
+  for (vi = _scope->_variables.begin(); vi != _scope->_variables.end(); ++vi) {
+    CPPInstance *instance = (*vi).second;
+    assert(instance != NULL);
+
+    if (instance->_storage_class & CPPInstance::SC_static) {
+      // Static members don't count.
+      continue;
+    }
+
+    if (!instance->_type->is_copy_assignable()) {
+      // Const or reference member, can't do it.
+      return false;
+    }
+  }
+
+  return true;
+}
+
+/**
+ * Returns true if the type is move-assignable.
+ */
+bool CPPStructType::
+is_move_assignable(CPPVisibility min_vis) const {
+  CPPInstance *assignment_operator = get_move_assignment_operator();
+  if (assignment_operator != (CPPInstance *)NULL) {
+    // It has a user-declared move assignment_operator.
+    if (assignment_operator->_vis > min_vis) {
+      // Inaccessible move assignment_operator.
+      return false;
+    }
+
+    if (assignment_operator->_storage_class & CPPInstance::SC_deleted) {
+      // It is deleted.
+      return false;
+    }
+
+    if (is_abstract()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  return is_copy_assignable(min_vis);
+}
+
 /**
  * Returns true if the type is destructible.
  */
@@ -886,6 +988,80 @@ get_move_constructor() const {
   return (CPPInstance *)NULL;
 }
 
+/**
+ * Returns the assignment operator defined for the struct type, if any, or
+ * NULL if no assignment operator is found.
+ */
+CPPFunctionGroup *CPPStructType::
+get_assignment_operator() const {
+  // Just look for the function with the name "operator ="
+  CPPScope::Functions::const_iterator fi;
+  fi = _scope->_functions.find("operator =");
+  if (fi != _scope->_functions.end()) {
+    return fi->second;
+  } else {
+    return (CPPFunctionGroup *)NULL;
+  }
+}
+
+/**
+ * Returns the copy assignment operator defined for the struct type, or NULL
+ * if no user-declared copy assignment operator exists.
+ */
+CPPInstance *CPPStructType::
+get_copy_assignment_operator() const {
+  CPPFunctionGroup *fgroup = get_assignment_operator();
+  if (fgroup == (CPPFunctionGroup *)NULL) {
+    return (CPPInstance *)NULL;
+  }
+
+  CPPFunctionGroup::Instances::const_iterator ii;
+  for (ii = fgroup->_instances.begin();
+       ii != fgroup->_instances.end();
+       ++ii) {
+    CPPInstance *inst = (*ii);
+    assert(inst->_type != (CPPType *)NULL);
+
+    CPPFunctionType *ftype = inst->_type->as_function_type();
+    assert(ftype != (CPPFunctionType *)NULL);
+
+    if ((ftype->_flags & CPPFunctionType::F_copy_assignment_operator) != 0) {
+      return inst;
+    }
+  }
+
+  return (CPPInstance *)NULL;
+}
+
+/**
+ * Returns the move assignment operator defined for the struct type, or NULL
+ * if no user-declared move assignment operator exists.
+ */
+CPPInstance *CPPStructType::
+get_move_assignment_operator() const {
+  CPPFunctionGroup *fgroup = get_assignment_operator();
+  if (fgroup == (CPPFunctionGroup *)NULL) {
+    return (CPPInstance *)NULL;
+  }
+
+  CPPFunctionGroup::Instances::const_iterator ii;
+  for (ii = fgroup->_instances.begin();
+       ii != fgroup->_instances.end();
+       ++ii) {
+    CPPInstance *inst = (*ii);
+    assert(inst->_type != (CPPType *)NULL);
+
+    CPPFunctionType *ftype = inst->_type->as_function_type();
+    assert(ftype != (CPPFunctionType *)NULL);
+
+    if ((ftype->_flags & CPPFunctionType::F_move_assignment_operator) != 0) {
+      return inst;
+    }
+  }
+
+  return (CPPInstance *)NULL;
+}
+
 /**
  * Returns the destructor defined for the struct type, if any, or NULL if no
  * user-declared destructor is found.

+ 7 - 1
dtool/src/cppparser/cppStructType.h

@@ -56,10 +56,13 @@ public:
   virtual bool is_constructible(const CPPType *arg_type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
   virtual bool is_destructible() const;
   bool is_default_constructible(CPPVisibility min_vis) const;
   bool is_copy_constructible(CPPVisibility min_vis) const;
-  bool is_move_constructible(CPPVisibility min_vis) const;
+  bool is_move_constructible(CPPVisibility min_vis  = V_public) const;
+  bool is_copy_assignable(CPPVisibility min_vis) const;
+  bool is_move_assignable(CPPVisibility min_vis = V_public) const;
   bool is_destructible(CPPVisibility min_vis) const;
   virtual bool is_convertible_to(const CPPType *other) const;
 
@@ -69,6 +72,9 @@ public:
   CPPInstance *get_default_constructor() const;
   CPPInstance *get_copy_constructor() const;
   CPPInstance *get_move_constructor() const;
+  CPPFunctionGroup *get_assignment_operator() const;
+  CPPInstance *get_copy_assignment_operator() const;
+  CPPInstance *get_move_assignment_operator() const;
   CPPInstance *get_destructor() const;
 
   virtual CPPDeclaration *

+ 8 - 0
dtool/src/cppparser/cppType.cxx

@@ -110,6 +110,14 @@ is_copy_constructible() const {
   return false;
 }
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPType::
+is_copy_assignable() const {
+  return false;
+}
+
 /**
  * Returns true if the type is destructible.
  */

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

@@ -51,6 +51,7 @@ public:
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
   virtual bool is_destructible() const;
   virtual bool is_parameter_expr() const;
 

+ 8 - 0
dtool/src/cppparser/cppTypedefType.cxx

@@ -205,6 +205,14 @@ is_copy_constructible() const {
   return _type->is_copy_constructible();
 }
 
+/**
+ * Returns true if the type is copy-assignable.
+ */
+bool CPPTypedefType::
+is_copy_assignable() const {
+  return _type->is_copy_assignable();
+}
+
 /**
  * Returns true if the type is destructible.
  */

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

@@ -48,6 +48,7 @@ public:
   virtual bool is_constructible(const CPPType *type) const;
   virtual bool is_default_constructible() const;
   virtual bool is_copy_constructible() const;
+  virtual bool is_copy_assignable() const;
   virtual bool is_destructible() const;
 
   virtual bool is_fully_specified() const;

+ 3 - 3
dtool/src/dtoolutil/pandaFileStream.h

@@ -29,7 +29,7 @@
 class EXPCL_DTOOL IFileStream : public istream {
 PUBLISHED:
   INLINE IFileStream();
-  INLINE IFileStream(const char *filename, ios::openmode mode = ios::in);
+  INLINE explicit IFileStream(const char *filename, ios::openmode mode = ios::in);
   INLINE ~IFileStream();
 
   INLINE void open(const char *filename, ios::openmode mode = ios::in);
@@ -57,7 +57,7 @@ private:
 class EXPCL_DTOOL OFileStream : public ostream {
 PUBLISHED:
   INLINE OFileStream();
-  INLINE OFileStream(const char *filename, ios::openmode mode = ios::out);
+  INLINE explicit OFileStream(const char *filename, ios::openmode mode = ios::out);
   INLINE ~OFileStream();
 
   INLINE void open(const char *filename, ios::openmode mode = ios::out);
@@ -86,7 +86,7 @@ private:
 class EXPCL_DTOOL FileStream : public iostream {
 PUBLISHED:
   INLINE FileStream();
-  INLINE FileStream(const char *filename, ios::openmode mode = ios::in);
+  INLINE explicit FileStream(const char *filename, ios::openmode mode = ios::in);
   INLINE ~FileStream();
 
   INLINE void open(const char *filename, ios::openmode mode = ios::in);

+ 93 - 122
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -745,11 +745,31 @@ write_python_instance(ostream &out, int indent_level, const string &return_expr,
       << "  return Py_None;\n";
     indent(out, indent_level)
       << "} else {\n";
-    indent(out, indent_level)
-      << "  return DTool_CreatePyInstanceTyped((void *)" << return_expr
-      << ", *Dtool_Ptr_" << make_safe_name(class_name) << ", "
-      << owns_memory << ", " << is_const << ", "
-      << return_expr << "->as_typed_object()->get_type_index());\n";
+    // Special exception if we are returning TypedWritable, which might
+    // actually be a derived class that inherits from ReferenceCount.
+    if (!owns_memory && !is_const && class_name == "TypedWritable") {
+      indent(out, indent_level)
+        << "  ReferenceCount *rc = " << return_expr << "->as_reference_count();\n";
+      indent(out, indent_level)
+        << "  bool is_refcount = (rc != (ReferenceCount *)NULL);\n";
+      indent(out, indent_level)
+        << "  if (is_refcount) {\n";
+      indent(out, indent_level)
+        << "    rc->ref();\n";
+      indent(out, indent_level)
+        << "  }\n";
+      indent(out, indent_level)
+        << "  return DTool_CreatePyInstanceTyped((void *)" << return_expr
+        << ", *Dtool_Ptr_" << make_safe_name(class_name) << ", is_refcount, "
+        << is_const << ", " << return_expr
+        << "->get_type_index());\n";
+    } else {
+      indent(out, indent_level)
+        << "  return DTool_CreatePyInstanceTyped((void *)" << return_expr
+        << ", *Dtool_Ptr_" << make_safe_name(class_name) << ", "
+        << owns_memory << ", " << is_const << ", "
+        << return_expr << "->as_typed_object()->get_type_index());\n";
+    }
     indent(out, indent_level)
       << "}\n";
   } else {
@@ -855,31 +875,13 @@ write_prototypes(ostream &out_code, ostream *out_h) {
             << "  return ((bool (*)(PyObject *, PT(" << class_name << ") &))Dtool_Ptr_" << safe_name << "->_Dtool_Coerce)(args, coerced);\n"
             << "}\n";
         }
-
-      } else if (TypeManager::is_trivial(type)) {
+      } else {
         out_code
           << "inline static " << class_name << " *Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " &coerced) {\n"
           << "  nassertr(Dtool_Ptr_" << safe_name << " != NULL, NULL);\n"
           << "  nassertr(Dtool_Ptr_" << safe_name << "->_Dtool_Coerce != NULL, NULL);\n"
           << "  return ((" << class_name << " *(*)(PyObject *, " << class_name << " &))Dtool_Ptr_" << safe_name << "->_Dtool_Coerce)(args, coerced);\n"
           << "}\n";
-
-      } else {
-        out_code
-          << "inline static bool Dtool_ConstCoerce_" << safe_name << "(PyObject *args, " << class_name << " const *&coerced, bool &manage) {\n"
-          << "  nassertr(Dtool_Ptr_" << safe_name << " != NULL, false);\n"
-          << "  nassertr(Dtool_Ptr_" << safe_name << "->_Dtool_ConstCoerce != NULL, false);\n"
-          << "  return ((bool (*)(PyObject *, " << class_name << " const *&, bool&))Dtool_Ptr_" << safe_name << "->_Dtool_ConstCoerce)(args, coerced, manage);\n"
-          << "}\n";
-
-        if (has_coerce > 1) {
-          out_code
-            << "inline static bool Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " *&coerced, bool &manage) {\n"
-            << "  nassertr(Dtool_Ptr_" << safe_name << " != NULL, false);\n"
-            << "  nassertr(Dtool_Ptr_" << safe_name << "->_Dtool_Coerce != NULL, false);\n"
-            << "  return ((bool (*)(PyObject *, " << class_name << " *&, bool&))Dtool_Ptr_" << safe_name << "->_Dtool_Coerce)(args, coerced, manage);\n"
-            << "}\n";
-        }
       }
     }
     out_code << "#else\n";
@@ -888,20 +890,12 @@ write_prototypes(ostream &out_code, ostream *out_h) {
 
     if (has_coerce > 0) {
       if (TypeManager::is_reference_count(type)) {
-        assert(!type->is_trivial());
         out_code << "extern bool Dtool_ConstCoerce_" << safe_name << "(PyObject *args, CPT(" << class_name << ") &coerced);\n";
         if (has_coerce > 1) {
           out_code << "extern bool Dtool_Coerce_" << safe_name << "(PyObject *args, PT(" << class_name << ") &coerced);\n";
         }
-
-      } else if (TypeManager::is_trivial(type)) {
-        out_code << "extern " << class_name << " *Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " &coerced);\n";
-
       } else {
-        out_code << "extern bool Dtool_ConstCoerce_" << safe_name << "(PyObject *args, " << class_name << " const *&coerced, bool &manage);\n";
-        if (has_coerce > 1) {
-          out_code << "extern bool Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " *&coerced, bool &manage);\n";
-        }
+        out_code << "extern " << class_name << " *Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " &coerced);\n";
       }
     }
     out_code << "#endif\n";
@@ -1062,7 +1056,7 @@ write_class_details(ostream &out, Object *obj) {
   int has_coerce = has_coerce_constructor(cpptype->as_struct_type());
   if (has_coerce > 0) {
     write_coerce_constructor(out, obj, true);
-    if (has_coerce > 1 && !TypeManager::is_trivial(obj->_itype._cpptype)) {
+    if (has_coerce > 1 && TypeManager::is_reference_count(obj->_itype._cpptype)) {
       write_coerce_constructor(out, obj, false);
     }
   }
@@ -1182,20 +1176,12 @@ write_class_declarations(ostream &out, ostream *out_h, Object *obj) {
   int has_coerce = has_coerce_constructor(type->as_struct_type());
   if (has_coerce > 0) {
     if (TypeManager::is_reference_count(type)) {
-      assert(!type->is_trivial());
       out << "bool Dtool_ConstCoerce_" << class_name << "(PyObject *args, CPT(" << c_class_name << ") &coerced);\n";
       if (has_coerce > 1) {
         out << "bool Dtool_Coerce_" << class_name << "(PyObject *args, PT(" << c_class_name << ") &coerced);\n";
       }
-
-    } else if (TypeManager::is_trivial(type)) {
-      out << "" << c_class_name << " *Dtool_Coerce_" << class_name << "(PyObject *args, " << c_class_name << " &coerced);\n";
-
     } else {
-      out << "bool Dtool_ConstCoerce_" << class_name << "(PyObject *args, " << c_class_name << " const *&coerced, bool &manage);\n";
-      if (has_coerce > 1) {
-        out << "bool Dtool_Coerce_" << class_name << "(PyObject *args, " << c_class_name << " *&coerced, bool &manage);\n";
-      }
+      out << "" << c_class_name << " *Dtool_Coerce_" << class_name << "(PyObject *args, " << c_class_name << " &coerced);\n";
     }
   }
 
@@ -3046,8 +3032,7 @@ write_module_class(ostream &out, Object *obj) {
 
   int has_coerce = has_coerce_constructor(obj->_itype._cpptype->as_struct_type());
   if (has_coerce > 0) {
-    if (TypeManager::is_reference_count(obj->_itype._cpptype) ||
-        !TypeManager::is_trivial(obj->_itype._cpptype)) {
+    if (TypeManager::is_reference_count(obj->_itype._cpptype)) {
       out << "  (CoerceFunction)Dtool_ConstCoerce_" << ClassName << ",\n";
       if (has_coerce > 1) {
         out << "  (CoerceFunction)Dtool_Coerce_" << ClassName << ",\n";
@@ -3920,7 +3905,7 @@ write_coerce_constructor(ostream &out, Object *obj, bool is_const) {
     }
     return_flags |= RF_err_false;
 
-  } else if (TypeManager::is_trivial(obj->_itype._cpptype)) {
+  } else {
     out << cClassName << " *Dtool_Coerce_" << ClassName << "(PyObject *args, " << cClassName << " &coerced) {\n";
 
     out << "  " << cClassName << " *local_this;\n";
@@ -3934,26 +3919,6 @@ write_coerce_constructor(ostream &out, Object *obj, bool is_const) {
     out << "    return local_this;\n";
 
     return_flags |= RF_err_null;
-
-  } else {
-    if (is_const) {
-      out << "bool Dtool_ConstCoerce_" << ClassName << "(PyObject *args, " << cClassName << " const *&coerced, bool &manage) {\n";
-    } else {
-      out << "bool Dtool_Coerce_" << ClassName << "(PyObject *args, " << cClassName << " *&coerced, bool &manage) {\n";
-    }
-
-    out << "  DTOOL_Call_ExtractThisPointerForType(args, &Dtool_" << ClassName << ", (void**)&coerced);\n";
-    out << "  if (coerced != NULL) {\n";
-    if (!is_const) {
-      out << "    if (!((Dtool_PyInstDef *)args)->_is_const) {\n";
-      out << "      // A non-const instance is required, which this is.\n";
-      out << "      return true;\n";
-      out << "    }\n";
-    } else {
-      out << "    return true;\n";
-    }
-
-    return_flags |= RF_err_false;
   }
 
   out << "  }\n\n";
@@ -4863,18 +4828,9 @@ write_function_instance(ostream &out, FunctionRemap *remap,
 
         if (args_type == AT_single_arg) {
           out << "#if PY_MAJOR_VERSION >= 3\n";
-          // As a special hack to fix pickling in Python 3, if the method name
-          // starts with py_decode_, we take a bytes object instead of a str.
-          if (remap->_cppfunc->get_local_name().substr(0, 10) == "py_decode_") {
-            indent(out, indent_level) << "if (PyBytes_AsStringAndSize(arg, (char **)&"
-              << param_name << "_str, &" << param_name << "_len) == -1) {\n";
-            indent(out, indent_level + 2) << param_name << "_str = NULL;\n";
-            indent(out, indent_level) << "}\n";
-          } else {
-            indent(out, indent_level)
-              << param_name << "_str = PyUnicode_AsUTF8AndSize(arg, &"
-              << param_name << "_len);\n";
-          }
+          indent(out, indent_level)
+            << param_name << "_str = PyUnicode_AsUTF8AndSize(arg, &"
+            << param_name << "_len);\n";
           out << "#else\n"; // NB. PyString_AsStringAndSize also accepts a PyUnicode.
           indent(out, indent_level) << "if (PyString_AsStringAndSize(arg, (char **)&"
             << param_name << "_str, &" << param_name << "_len) == -1) {\n";
@@ -5498,8 +5454,8 @@ write_function_instance(ostream &out, FunctionRemap *remap,
           // actual PointerTo.  This eliminates an unref()ref() pair.
           pexpr_string = "MOVE(" + param_name + "_this)";
 
-        } else if (TypeManager::is_trivial(obj_type)) {
-          // This is a trivial type, such as TypeHandle or LVecBase4.
+        } else {
+          // This is a move-assignable type, such as TypeHandle or LVecBase4.
           obj_type->output_instance(extra_convert, param_name + "_local", &parser);
           extra_convert << ";\n";
 
@@ -5522,29 +5478,6 @@ write_function_instance(ostream &out, FunctionRemap *remap,
           coerce_call = "(" + param_name + "_this != NULL)";
 
           pexpr_string = param_name + "_this";
-
-        } else  {
-          // This is a bit less elegant: we use a bool to store whether we're
-          // supposed to clean up the reference afterward.
-          type->output_instance(extra_convert, param_name + "_this", &parser);
-          extra_convert
-            << default_expr << ";\n"
-            << "bool " << param_name << "_manage = false;\n";
-
-          if (TypeManager::is_const_pointer_or_ref(orig_type)) {
-            coerce_call = "Dtool_ConstCoerce_" + make_safe_name(class_name) +
-              "(" + param_name + ", " + param_name + "_this, " + param_name + "_manage)";
-          } else {
-            coerce_call = "Dtool_Coerce_" + make_safe_name(class_name) +
-              "(" + param_name + ", " + param_name + "_this, " + param_name + "_manage)";
-          }
-
-          extra_cleanup
-            << "if (" << param_name << "_manage) {\n"
-            << "  delete " << param_name << "_this;\n"
-            << "}\n";
-
-          pexpr_string = param_name + "_this";
         }
 
         if (report_errors) {
@@ -5848,7 +5781,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
     manage_return = remap->_return_value_needs_management;
     return_expr = "return_value";
 
-  } else if ((return_flags & RF_coerced) != 0 && TypeManager::is_trivial(remap->_cpptype)) {
+  } else if ((return_flags & RF_coerced) != 0 && !TypeManager::is_reference_count(remap->_cpptype)) {
     // Another special case is the coerce constructor for a trivial type.  We
     // don't want to invoke "operator new" unnecessarily.
     if (is_constructor && remap->_extension) {
@@ -6063,7 +5996,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
 
   // Okay, we're past all the error conditions and special cases.  Now return
   // the return type in the way that was requested.
-  if (return_flags & RF_int) {
+  if ((return_flags & RF_int) != 0 && (return_flags & RF_raise_keyerror) == 0) {
     CPPType *orig_type = remap->_return_type->get_orig_type();
     if (is_constructor) {
       // Special case for constructor.
@@ -6152,13 +6085,8 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       indent(out, indent_level) << "coerced = MOVE(" << return_expr << ");\n";
       indent(out, indent_level) << "return true;\n";
 
-    } else if (TypeManager::is_trivial(remap->_cpptype)) {
-      indent(out, indent_level) << "return &coerced;\n";
-
     } else {
-      indent(out, indent_level) << "coerced = " << return_expr << ";\n";
-      indent(out, indent_level) << "manage = true;\n";
-      indent(out, indent_level) << "return true;\n";
+      indent(out, indent_level) << "return &coerced;\n";
     }
 
   } else if (return_flags & RF_raise_keyerror) {
@@ -6555,6 +6483,12 @@ write_getset(ostream &out, Object *obj, Property *property) {
         out << "  }\n\n";
       }
 
+      out << "  if (index < 0 || index >= (Py_ssize_t)"
+          << len_remap->get_call_str("local_this", pexprs) << ") {\n";
+      out << "    PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n";
+      out << "    return -1;\n";
+      out << "  }\n";
+
       out << "  if (arg == (PyObject *)NULL) {\n";
       if (property->_deleter != NULL) {
         if (property->_deleter->_has_this) {
@@ -6701,6 +6635,19 @@ write_getset(ostream &out, Object *obj, Property *property) {
       out << "  if (value == (PyObject *)NULL) {\n";
       if (property->_deleter != NULL) {
         out << "    PyObject *arg = key;\n";
+
+        if (property->_has_function != NULL) {
+          std::set<FunctionRemap*> remaps;
+          remaps.insert(property->_has_function->_remaps.begin(),
+                        property->_has_function->_remaps.end());
+
+          out << "    {\n";
+          string expected_params;
+          write_function_forset(out, remaps, 1, 1, expected_params, 6, true, true,
+                                AT_single_arg, RF_raise_keyerror | RF_int, false, true);
+          out << "    }\n";
+        }
+
         std::set<FunctionRemap*> remaps;
         remaps.insert(property->_deleter->_remaps.begin(),
                       property->_deleter->_remaps.end());
@@ -6761,16 +6708,22 @@ write_getset(ostream &out, Object *obj, Property *property) {
       out << "  Py_XINCREF(self);\n";
     }
     out << "  Dtool_SeqMapWrapper *wrap = PyObject_New(Dtool_SeqMapWrapper, &Dtool_SeqMapWrapper_Type);\n"
-           "  wrap->_seq._base._self = self;\n"
-           "  wrap->_seq._len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
-           "  wrap->_seq._getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n"
-           "  wrap->_map_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n";
+           "  wrap->_map._base._self = self;\n"
+           "  wrap->_map._base._name = \"" << ClassName << "." << ielem.get_name() << "\";\n"
+           "  wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
+           "  wrap->_seq_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n"
+           "  wrap->_map._getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n";
     if (!property->_setter_remaps.empty()) {
-      out << "  wrap->_seq._setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n";
-      out << "  wrap->_map_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n";
+      out << "  if (!((Dtool_PyInstDef *)self)->_is_const) {\n"
+             "    wrap->_seq_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n"
+             "    wrap->_map._setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n"
+             "  } else {\n"
+             "    wrap->_seq_setitem_func = NULL;\n"
+             "    wrap->_map._setitem_func = NULL;\n"
+             "  }\n";
     } else {
-      out << "  wrap->_seq._setitem_func = NULL;\n";
-      out << "  wrap->_map_setitem_func = NULL;\n";
+      out << "  wrap->_seq_setitem_func = NULL;\n";
+      out << "  wrap->_map._setitem_func = NULL;\n";
     }
     out << "  return (PyObject *)wrap;\n"
             "}\n\n";
@@ -6785,9 +6738,14 @@ write_getset(ostream &out, Object *obj, Property *property) {
     }
     out << "  Dtool_MappingWrapper *wrap = PyObject_New(Dtool_MappingWrapper, &Dtool_MappingWrapper_Type);\n"
            "  wrap->_base._self = self;\n"
+           "  wrap->_base._name = \"" << ClassName << "." << ielem.get_name() << "\";\n"
            "  wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n";
     if (!property->_setter_remaps.empty()) {
-      out << "  wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n";
+      out << "  if (!((Dtool_PyInstDef *)self)->_is_const) {\n";
+      out << "    wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n";
+      out << "  } else {\n";
+      out << "    wrap->_setitem_func = NULL;\n";
+      out << "  }\n";
     } else {
       out << "  wrap->_setitem_func = NULL;\n";
     }
@@ -6804,10 +6762,15 @@ write_getset(ostream &out, Object *obj, Property *property) {
     }
     out << "  Dtool_SequenceWrapper *wrap = PyObject_New(Dtool_SequenceWrapper, &Dtool_SequenceWrapper_Type);\n"
            "  wrap->_base._self = self;\n"
+           "  wrap->_base._name = \"" << ClassName << "." << ielem.get_name() << "\";\n"
            "  wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
            "  wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n";
     if (!property->_setter_remaps.empty()) {
-      out << "  wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n";
+      out << "  if (!((Dtool_PyInstDef *)self)->_is_const) {\n";
+      out << "    wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n";
+      out << "  } else {\n";
+      out << "    wrap->_setitem_func = NULL;\n";
+      out << "  }\n";
     } else {
       out << "  wrap->_setitem_func = NULL;\n";
     }
@@ -7213,7 +7176,7 @@ is_cpp_type_legal(CPPType *in_ctype) {
     return true;
   } else if (TypeManager::is_basic_string_wchar(type)) {
     return true;
-  } else if (TypeManager::is_vector_unsigned_char(type)) {
+  } else if (TypeManager::is_vector_unsigned_char(in_ctype)) {
     return true;
   } else if (TypeManager::is_simple(type)) {
     return true;
@@ -7322,6 +7285,14 @@ has_coerce_constructor(CPPStructType *type) {
     return 0;
   }
 
+  // It is convenient to set default-constructability and move-assignability
+  // as requirement for non-reference-counted objects, since it simplifies the
+  // implementation and it holds for all classes we need it for.
+  if (!TypeManager::is_reference_count(type) &&
+      (!type->is_default_constructible() || !type->is_move_assignable())) {
+    return 0;
+  }
+
   CPPScope *scope = type->get_scope();
   if (scope == NULL) {
     return 0;

+ 0 - 52
dtool/src/interrogate/typeManager.cxx

@@ -2517,55 +2517,3 @@ is_local(CPPType *source_type) {
  return false;
  */
 }
-
-/**
- * Returns true if the type is trivial (or trivial enough for our purposes).
- */
-bool TypeManager::
-is_trivial(CPPType *source_type) {
-  switch (source_type->get_subtype()) {
-  case CPPDeclaration::ST_const:
-    return is_trivial(source_type->as_const_type()->_wrapped_around);
-
-  case CPPDeclaration::ST_reference:
-    return false;
-
-  case CPPDeclaration::ST_pointer:
-    return true;
-
-  case CPPDeclaration::ST_simple:
-    return true;
-
-  case CPPDeclaration::ST_typedef:
-    return is_trivial(source_type->as_typedef_type()->_type);
-
-  default:
-    if (source_type->is_trivial() || is_handle(source_type)) {
-      return true;
-    } else {
-      // This is a bit of a hack.  is_trivial() returns false for types that
-      // have an empty constructor (since we can't use =default yet). For the
-      // other classes, it's just convenient to consider them trivial even if
-      // they aren't, since they are simple enough.
-      string name = source_type->get_simple_name();
-      return (name == "ButtonHandle" || name == "DatagramIterator" ||
-              name == "BitMask" || name == "Filename" || name == "pixel" ||
-              name == "NodePath" || name == "LoaderOptions" ||
-              name == "PointerToArray" || name == "ConstPointerToArray" ||
-              name == "PStatThread" ||
-              (name.size() >= 6 && name.substr(0, 6) == "LPlane") ||
-              (name.size() > 6 && name.substr(0, 6) == "LPoint") ||
-              (name.size() > 7 && name.substr(0, 7) == "LVector") ||
-              (name.size() > 7 && name.substr(0, 7) == "LMatrix") ||
-              (name.size() > 8 && name.substr(0, 8) == "LVecBase") ||
-              (name.size() >= 9 && name.substr(0, 9) == "LParabola") ||
-              (name.size() >= 9 && name.substr(0, 9) == "LRotation") ||
-              (name.size() >= 11 && name.substr(0, 11) == "LQuaternion") ||
-              (name.size() >= 12 && name.substr(0, 12) == "LOrientation") ||
-              (name.size() > 16 && name.substr(0, 16) == "UnalignedLMatrix") ||
-              (name.size() > 17 && name.substr(0, 17) == "UnalignedLVecBase"));
-    }
-  }
-
-  return false;
-}

+ 0 - 1
dtool/src/interrogate/typeManager.h

@@ -149,7 +149,6 @@ public:
 
   static bool is_exported(CPPType *type);
   static bool is_local(CPPType *type);
-  static bool is_trivial(CPPType *type);
 };
 
 #endif

+ 1 - 1
dtool/src/interrogatedb/py_panda.I

@@ -222,7 +222,7 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value) {
   return value;
 }
 
-ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector<unsigned char> &value) {
+ALWAYS_INLINE PyObject *Dtool_WrapValue(const vector_uchar &value) {
 #if PY_MAJOR_VERSION >= 3
   return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
 #else

+ 615 - 150
dtool/src/interrogatedb/py_panda.cxx

@@ -1049,11 +1049,50 @@ static void Dtool_WrapperBase_dealloc(PyObject *self) {
   Py_XDECREF(wrap->_self);
 }
 
+static PyObject *Dtool_WrapperBase_repr(PyObject *self) {
+  Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
+  nassertr(wrap, nullptr);
+
+  PyObject *repr = PyObject_Repr(wrap->_self);
+  PyObject *result;
+#if PY_MAJOR_VERSION >= 3
+  result = PyUnicode_FromFormat("<%s[] of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
+#else
+  result = PyString_FromFormat("<%s[] of %s>", wrap->_name, PyString_AS_STRING(repr));
+#endif
+  Py_DECREF(repr);
+  return result;
+}
+
+static PyObject *Dtool_SequenceWrapper_repr(PyObject *self) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+
+  Py_ssize_t len = -1;
+  if (wrap->_len_func != nullptr) {
+    len = wrap->_len_func(wrap->_base._self);
+  }
+
+  if (len < 0) {
+    PyErr_Restore(nullptr, nullptr, nullptr);
+    return Dtool_WrapperBase_repr(self);
+  }
+
+  PyObject *repr = PyObject_Repr(wrap->_base._self);
+  PyObject *result;
+#if PY_MAJOR_VERSION >= 3
+  result = PyUnicode_FromFormat("<%s[%zd] of %s>", wrap->_base._name, len, PyUnicode_AsUTF8(repr));
+#else
+  result = PyString_FromFormat("<%s[%zd] of %s>", wrap->_base._name, len, PyString_AS_STRING(repr));
+#endif
+  Py_DECREF(repr);
+  return result;
+}
+
 static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) {
   Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
   nassertr(wrap, -1);
   if (wrap->_len_func != nullptr) {
-    nassertr(wrap->_len_func, -1);
     return wrap->_len_func(wrap->_base._self);
   } else {
     Dtool_Raise_TypeError("property does not support len()");
@@ -1079,6 +1118,208 @@ static int Dtool_SequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObj
   }
 }
 
+/**
+ * Implementation of property.index(x) which returns the index of the first
+ * occurrence of x in the sequence, or raises a ValueError if it isn't found.
+ */
+static PyObject *Dtool_SequenceWrapper_index(PyObject *self, PyObject *value) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  Py_ssize_t length = 0;
+  if (wrap->_len_func != nullptr && wrap->_setitem_func != nullptr) {
+    length = wrap->_len_func(wrap->_base._self);
+  } else {
+    return Dtool_Raise_TypeError("property does not support remove()");
+  }
+
+  // Iterate through the items, invoking the equality function for each, until
+  // we have found the right one.
+  nassertr(wrap->_getitem_func, nullptr);
+  for (Py_ssize_t index = 0; index < length; ++index) {
+    PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
+    if (item != nullptr) {
+      int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
+      if (cmp > 0) {
+        return Dtool_WrapValue(index);
+      }
+      if (cmp < 0) {
+        return nullptr;
+      }
+    } else {
+      return nullptr;
+    }
+  }
+  // Not found, raise ValueError.
+  return PyErr_Format(PyExc_ValueError, "%s.index() did not find value", wrap->_base._name);
+}
+
+/**
+ * Implementation of property.count(x) which returns the number of occurrences
+ * of x in the sequence.
+ */
+static PyObject *Dtool_SequenceWrapper_count(PyObject *self, PyObject *value) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  Py_ssize_t index = 0;
+  if (wrap->_len_func != nullptr) {
+    index = wrap->_len_func(wrap->_base._self);
+  } else {
+    return Dtool_Raise_TypeError("property does not support count()");
+  }
+  // Iterate through the items, invoking the == operator for each.
+  long count = 0;
+  nassertr(wrap->_getitem_func, nullptr);
+  while (index > 0) {
+    --index;
+    PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
+    if (item == nullptr) {
+      return nullptr;
+    }
+    int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
+    if (cmp > 0) {
+      ++count;
+    }
+    if (cmp < 0) {
+      return nullptr;
+    }
+  }
+#if PY_MAJOR_VERSION >= 3
+  return PyLong_FromLong(count);
+#else
+  return PyInt_FromLong(count);
+#endif
+}
+
+/**
+ * Implementation of property.clear() which removes all elements in the
+ * sequence, starting with the last.
+ */
+static PyObject *Dtool_SequenceWrapper_clear(PyObject *self, PyObject *) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  Py_ssize_t index = 0;
+  if (wrap->_len_func != nullptr && wrap->_setitem_func != nullptr) {
+    index = wrap->_len_func(wrap->_base._self);
+  } else {
+    return Dtool_Raise_TypeError("property does not support clear()");
+  }
+
+  // Iterate through the items, invoking the delete function for each.  We do
+  // this in reverse order, which may be more efficient.
+  while (index > 0) {
+    --index;
+    if (wrap->_setitem_func(wrap->_base._self, index, nullptr) != 0) {
+      return nullptr;
+    }
+  }
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+/**
+ * Implementation of property.remove(x) which removes the first occurrence of
+ * x in the sequence, or raises a ValueError if it isn't found.
+ */
+static PyObject *Dtool_SequenceWrapper_remove(PyObject *self, PyObject *value) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  Py_ssize_t length = 0;
+  if (wrap->_len_func != nullptr && wrap->_setitem_func != nullptr) {
+    length = wrap->_len_func(wrap->_base._self);
+  } else {
+    return Dtool_Raise_TypeError("property does not support remove()");
+  }
+
+  // Iterate through the items, invoking the equality function for each, until
+  // we have found the right one.
+  nassertr(wrap->_getitem_func, nullptr);
+  for (Py_ssize_t index = 0; index < length; ++index) {
+    PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
+    if (item != nullptr) {
+      int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
+      if (cmp > 0) {
+        if (wrap->_setitem_func(wrap->_base._self, index, nullptr) == 0) {
+          Py_INCREF(Py_None);
+          return Py_None;
+        } else {
+          return nullptr;
+        }
+      }
+      if (cmp < 0) {
+        return nullptr;
+      }
+    } else {
+      return nullptr;
+    }
+  }
+  // Not found, raise ValueError.
+  return PyErr_Format(PyExc_ValueError, "%s.remove() did not find value", wrap->_base._name);
+}
+
+/**
+ * Implementation of property.pop([i=-1]) which returns and removes the
+ * element at the indicated index in the sequence.  If no index is provided,
+ * it removes from the end of the list.
+ */
+static PyObject *Dtool_SequenceWrapper_pop(PyObject *self, PyObject *args) {
+  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
+  nassertr(wrap, nullptr);
+  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr ||
+      wrap->_len_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support pop()");
+  }
+
+  Py_ssize_t length = wrap->_len_func(wrap->_base._self);
+  Py_ssize_t index;
+  switch (PyTuple_GET_SIZE(args)) {
+  case 0:
+    index = length - 1;
+    break;
+  case 1:
+    index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(args, 0), PyExc_IndexError);
+    if (index == -1 && _PyErr_OCCURRED()) {
+      return nullptr;
+    }
+    if (index < 0) {
+      index += length;
+    }
+    break;
+  default:
+    return Dtool_Raise_TypeError("pop([i=-1]) takes 0 or 1 arguments");
+  }
+
+  if (length <= 0) {
+    return PyErr_Format(PyExc_IndexError, "%s.pop() from empty sequence", wrap->_base._name);
+  }
+
+  // Index error will be caught by getitem_func.
+  PyObject *value = wrap->_getitem_func(wrap->_base._self, index);
+  if (value != nullptr) {
+    if (wrap->_setitem_func(wrap->_base._self, index, nullptr) != 0) {
+      return nullptr;
+    }
+    return value;
+  }
+  return nullptr;
+}
+
+static int Dtool_MappingWrapper_contains(PyObject *self, PyObject *key) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, -1);
+  nassertr(wrap->_getitem_func, -1);
+  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+  if (value != nullptr) {
+    Py_DECREF(value);
+    return 1;
+  } else if (_PyErr_OCCURRED() == PyExc_KeyError ||
+             _PyErr_OCCURRED() == PyExc_TypeError) {
+    PyErr_Restore(nullptr, nullptr, nullptr);
+    return 0;
+  } else {
+    return -1;
+  }
+}
+
 static PyObject *Dtool_MappingWrapper_getitem(PyObject *self, PyObject *key) {
   Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
   nassertr(wrap, nullptr);
@@ -1097,31 +1338,200 @@ static int Dtool_MappingWrapper_setitem(PyObject *self, PyObject *key, PyObject
   }
 }
 
-static PyObject *Dtool_SeqMapWrapper_getitem(PyObject *self, PyObject *key) {
-  Dtool_SeqMapWrapper *wrap = (Dtool_SeqMapWrapper *)self;
+/**
+ * Implementation of property.get(key[,def=None]) which returns the value with
+ * the given key in the mapping, or the given default value (which defaults to
+ * None) if the key isn't found in the mapping.
+ */
+static PyObject *Dtool_MappingWrapper_get(PyObject *self, PyObject *args) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
   nassertr(wrap, nullptr);
-  nassertr(wrap->_map_getitem_func, nullptr);
-  return wrap->_map_getitem_func(wrap->_seq._base._self, key);
+  nassertr(wrap->_getitem_func, nullptr);
+  Py_ssize_t size = PyTuple_GET_SIZE(args);
+  if (size != 1 && size != 2) {
+    return PyErr_Format(PyExc_TypeError, "%s.get() takes 1 or 2 arguments", wrap->_base._name);
+  }
+  PyObject *defvalue = Py_None;
+  if (size >= 2) {
+    defvalue = PyTuple_GET_ITEM(args, 1);
+  }
+  PyObject *key = PyTuple_GET_ITEM(args, 0);
+  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+  if (value != nullptr) {
+    return value;
+  } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
+    PyErr_Restore(nullptr, nullptr, nullptr);
+    Py_INCREF(defvalue);
+    return defvalue;
+  } else {
+    return nullptr;
+  }
+}
+
+/**
+ * Implementation of property.pop(key[,def=None]) which is the same as get()
+ * except that it also removes the element from the mapping.
+ */
+static PyObject *Dtool_MappingWrapper_pop(PyObject *self, PyObject *args) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support pop()");
+  }
+
+  Py_ssize_t size = PyTuple_GET_SIZE(args);
+  if (size != 1 && size != 2) {
+    return PyErr_Format(PyExc_TypeError, "%s.pop() takes 1 or 2 arguments", wrap->_base._name);
+  }
+  PyObject *defvalue = Py_None;
+  if (size >= 2) {
+    defvalue = PyTuple_GET_ITEM(args, 1);
+  }
+
+  PyObject *key = PyTuple_GET_ITEM(args, 0);
+  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+  if (value != nullptr) {
+    // OK, now set unset this value.
+    if (wrap->_setitem_func(wrap->_base._self, key, nullptr) == 0) {
+      return value;
+    } else {
+      Py_DECREF(value);
+      return nullptr;
+    }
+  } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
+    PyErr_Restore(nullptr, nullptr, nullptr);
+    Py_INCREF(defvalue);
+    return defvalue;
+  } else {
+    return nullptr;
+  }
+}
+
+/**
+ * Implementation of property.setdefault(key[,def=None]) which is the same as
+ * get() except that it also writes the default value back to the mapping if
+ * the key was not found is missing.
+ */
+static PyObject *Dtool_MappingWrapper_setdefault(PyObject *self, PyObject *args) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+
+  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support setdefault()");
+  }
+
+  Py_ssize_t size = PyTuple_GET_SIZE(args);
+  if (size != 1 && size != 2) {
+    return PyErr_Format(PyExc_TypeError, "%s.setdefault() takes 1 or 2 arguments", wrap->_base._name);
+  }
+  PyObject *defvalue = Py_None;
+  if (size >= 2) {
+    defvalue = PyTuple_GET_ITEM(args, 1);
+  }
+  PyObject *key = PyTuple_GET_ITEM(args, 0);
+  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
+  if (value != nullptr) {
+    return value;
+  } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
+    PyErr_Restore(nullptr, nullptr, nullptr);
+    if (wrap->_setitem_func(wrap->_base._self, key, defvalue) == 0) {
+      Py_INCREF(defvalue);
+      return defvalue;
+    }
+  }
+  return nullptr;
 }
 
-static int Dtool_SeqMapWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) {
+/**
+ * Implementation of property.update(...) which sets multiple values in one
+ * go.  It accepts either a single dictionary or keyword arguments, not both.
+ */
+static PyObject *Dtool_MappingWrapper_update(PyObject *self, PyObject *args, PyObject *kwargs) {
+  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
+  nassertr(wrap, nullptr);
+
+  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
+    return Dtool_Raise_TypeError("property does not support update()");
+  }
+
+  // We accept either a dict argument or keyword arguments, but not both.
+  PyObject *dict;
+  switch (PyTuple_GET_SIZE(args)) {
+  case 0:
+    if (kwargs == nullptr) {
+      // This is legal.
+      Py_INCREF(Py_None);
+      return Py_None;
+    }
+    dict = kwargs;
+    break;
+  case 1:
+    if (PyDict_Check(PyTuple_GET_ITEM(args, 0)) && (kwargs == nullptr || Py_SIZE(kwargs) == 0)) {
+      dict = PyTuple_GET_ITEM(args, 0);
+      break;
+    }
+    // Fall through
+  default:
+    return PyErr_Format(PyExc_TypeError, "%s.update() takes either a dict argument or keyword arguments", wrap->_base._name);
+  }
+
+  PyObject *key, *value;
+  Py_ssize_t pos = 0;
+  while (PyDict_Next(dict, &pos, &key, &value)) {
+    if (wrap->_setitem_func(wrap->_base._self, key, value) != 0) {
+      return nullptr;
+    }
+  }
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+/**
+ * Implementation of len(property) for mapping types.
+ */
+static Py_ssize_t Dtool_SeqMapWrapper_length(PyObject *self) {
   Dtool_SeqMapWrapper *wrap = (Dtool_SeqMapWrapper *)self;
   nassertr(wrap, -1);
-  if (wrap->_map_setitem_func != nullptr) {
-    return wrap->_map_setitem_func(wrap->_seq._base._self, key, value);
+  if (wrap->_len_func != nullptr) {
+    return wrap->_len_func(wrap->_map._base._self);
   } else {
-    Dtool_Raise_TypeError("property does not support item assignment");
+    Dtool_Raise_TypeError("property does not support len()");
     return -1;
   }
 }
 
-static PyObject *Dtool_GeneratorWrapper_iternext(PyObject *self) {
-  Dtool_GeneratorWrapper *wrap = (Dtool_GeneratorWrapper *)self;
+/**
+ * Implementation of property.values() which returns a tuple containing the
+ * dictionary values.
+ */
+static PyObject *Dtool_SeqMapWrapper_values(PyObject *self, PyObject *) {
+  Dtool_SeqMapWrapper *wrap = (Dtool_SeqMapWrapper *)self;
   nassertr(wrap, nullptr);
-  nassertr(wrap->_iternext_func, nullptr);
-  return wrap->_iternext_func(wrap->_base._self);
+  nassertr(wrap->_len_func, nullptr);
+  nassertr(wrap->_seq_getitem_func, nullptr);
+
+  Py_ssize_t length = wrap->_len_func(wrap->_map._base._self);
+  PyObject *values = PyTuple_New(length);
+  if (UNLIKELY(values == nullptr)) {
+    return nullptr;
+  }
+
+  for (Py_ssize_t i = 0; i < length; ++i) {
+    PyObject *value = wrap->_seq_getitem_func(wrap->_map._base._self, i);
+    if (value != nullptr) {
+      PyTuple_SET_ITEM(values, i, value);
+    } else {
+      Py_DECREF(values);
+      return nullptr;
+    }
+  }
+
+  return values;
 }
 
+/**
+ * This variant defines only a sequence interface.
+ */
 static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
   Dtool_SequenceWrapper_length,
   0, // sq_concat
@@ -1135,21 +1545,15 @@ static PySequenceMethods Dtool_SequenceWrapper_SequenceMethods = {
   0, // sq_inplace_repeat
 };
 
-static PyMappingMethods Dtool_MappingWrapper_MappingMethods = {
-  0, // mp_length
-  Dtool_MappingWrapper_getitem,
-  Dtool_MappingWrapper_setitem,
-};
-
-static PyMappingMethods Dtool_SeqMapWrapper_MappingMethods = {
-  Dtool_SequenceWrapper_length,
-  Dtool_SeqMapWrapper_getitem,
-  Dtool_SeqMapWrapper_setitem,
+static PyMethodDef Dtool_SequenceWrapper_Methods[] = {
+  {"index", &Dtool_SequenceWrapper_index, METH_O, nullptr},
+  {"count", &Dtool_SequenceWrapper_count, METH_O, nullptr},
+  {"clear", &Dtool_SequenceWrapper_clear, METH_NOARGS, nullptr},
+  {"pop", &Dtool_SequenceWrapper_pop, METH_VARARGS, nullptr},
+  {"remove", &Dtool_SequenceWrapper_remove, METH_O, nullptr},
+  {nullptr, nullptr, 0, nullptr}
 };
 
-/**
- * This variant defines only a sequence interface.
- */
 PyTypeObject Dtool_SequenceWrapper_Type = {
   PyVarObject_HEAD_INIT(NULL, 0)
   "sequence wrapper",
@@ -1164,7 +1568,7 @@ PyTypeObject Dtool_SequenceWrapper_Type = {
 #else
   0, // tp_compare
 #endif
-  0, // tp_repr
+  Dtool_SequenceWrapper_repr,
   0, // tp_as_number
   &Dtool_SequenceWrapper_SequenceMethods,
   0, // tp_as_mapping
@@ -1182,7 +1586,7 @@ PyTypeObject Dtool_SequenceWrapper_Type = {
   0, // tp_weaklistoffset
   0, // tp_iter
   0, // tp_iternext
-  0, // tp_methods
+  Dtool_SequenceWrapper_Methods,
   0, // tp_members
   0, // tp_getset
   0, // tp_base
@@ -1210,122 +1614,79 @@ PyTypeObject Dtool_SequenceWrapper_Type = {
 };
 
 /**
- * This is a variant of the Python getset mechanism that permits static
- * properties.
+ * This variant defines only a mapping interface.
  */
-PyObject *
-Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
-  PyGetSetDescrObject *descr;
-  descr = (PyGetSetDescrObject *)PyType_GenericAlloc(&Dtool_StaticProperty_Type, 0);
-  if (descr != nullptr) {
-    Py_XINCREF(type);
-    descr->d_getset = (PyGetSetDef *)getset;
-#if PY_MAJOR_VERSION >= 3
-    descr->d_common.d_type = type;
-    descr->d_common.d_name = PyUnicode_InternFromString(getset->name);
-#if PY_VERSION_HEX >= 0x03030000
-    descr->d_common.d_qualname = nullptr;
-#endif
-#else
-    descr->d_type = type;
-    descr->d_name = PyString_InternFromString(getset->name);
-#endif
-  }
-  return (PyObject *)descr;
-}
-
-static void
-Dtool_StaticProperty_dealloc(PyDescrObject *descr) {
-  _PyObject_GC_UNTRACK(descr);
-  Py_XDECREF(descr->d_type);
-  Py_XDECREF(descr->d_name);
-//#if PY_MAJOR_VERSION >= 3
-//  Py_XDECREF(descr->d_qualname);
-//#endif
-  PyObject_GC_Del(descr);
-}
-
-static PyObject *
-Dtool_StaticProperty_repr(PyDescrObject *descr, const char *format) {
-#if PY_MAJOR_VERSION >= 3
-  return PyUnicode_FromFormat("<attribute '%V' of '%s'>", descr->d_name, "?", descr->d_type->tp_name);
-#else
-  return PyString_FromFormat("<attribute '%V' of '%s'>", descr->d_name, "?", descr->d_type->tp_name);
-#endif
-}
-
-static int
-Dtool_StaticProperty_traverse(PyObject *self, visitproc visit, void *arg) {
-  PyDescrObject *descr = (PyDescrObject *)self;
-  Py_VISIT(descr->d_type);
-  return 0;
-}
+static PySequenceMethods Dtool_MappingWrapper_SequenceMethods = {
+  0, // sq_length
+  0, // sq_concat
+  0, // sq_repeat
+  0, // sq_item
+  0, // sq_slice
+  0, // sq_ass_item
+  0, // sq_ass_slice
+  Dtool_MappingWrapper_contains,
+  0, // sq_inplace_concat
+  0, // sq_inplace_repeat
+};
 
-static PyObject *
-Dtool_StaticProperty_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type) {
-  if (descr->d_getset->get != nullptr) {
-    return descr->d_getset->get(obj, descr->d_getset->closure);
-  } else {
-    return PyErr_Format(PyExc_AttributeError,
-                        "attribute '%V' of type '%.100s' is not readable",
-                        ((PyDescrObject *)descr)->d_name, "?",
-                        ((PyDescrObject *)descr)->d_type->tp_name);
-  }
-}
+static PyMappingMethods Dtool_MappingWrapper_MappingMethods = {
+  0, // mp_length
+  Dtool_MappingWrapper_getitem,
+  Dtool_MappingWrapper_setitem,
+};
 
-static int
-Dtool_StaticProperty_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) {
-  if (descr->d_getset->set != nullptr) {
-    return descr->d_getset->set(obj, value, descr->d_getset->closure);
-  } else {
-    PyErr_Format(PyExc_AttributeError,
-                 "attribute '%V' of type '%.100s' is not writable",
-                 ((PyDescrObject *)descr)->d_name, "?",
-                 ((PyDescrObject *)descr)->d_type->tp_name);
-    return -1;
-  }
-}
+static PyMethodDef Dtool_MappingWrapper_Methods[] = {
+  {"get", &Dtool_MappingWrapper_get, METH_VARARGS, nullptr},
+  {"pop", &Dtool_MappingWrapper_pop, METH_VARARGS, nullptr},
+  {"setdefault", &Dtool_MappingWrapper_setdefault, METH_VARARGS, nullptr},
+  {"update", (PyCFunction) &Dtool_MappingWrapper_update, METH_VARARGS | METH_KEYWORDS, nullptr},
+  {nullptr, nullptr, 0, nullptr}
+};
 
-PyTypeObject Dtool_StaticProperty_Type = {
-  PyVarObject_HEAD_INIT(&PyType_Type, 0)
-  "getset_descriptor",
-  sizeof(PyGetSetDescrObject),
+PyTypeObject Dtool_MappingWrapper_Type = {
+  PyVarObject_HEAD_INIT(NULL, 0)
+  "mapping wrapper",
+  sizeof(Dtool_MappingWrapper),
   0, // tp_itemsize
-  (destructor)Dtool_StaticProperty_dealloc,
+  Dtool_WrapperBase_dealloc,
   0, // tp_print
   0, // tp_getattr
   0, // tp_setattr
+#if PY_MAJOR_VERSION >= 3
   0, // tp_reserved
-  (reprfunc)Dtool_StaticProperty_repr,
+#else
+  0, // tp_compare
+#endif
+  Dtool_WrapperBase_repr,
   0, // tp_as_number
-  0, // tp_as_sequence
-  0, // tp_as_mapping
+  &Dtool_MappingWrapper_SequenceMethods,
+  &Dtool_MappingWrapper_MappingMethods,
   0, // tp_hash
   0, // tp_call
   0, // tp_str
   PyObject_GenericGetAttr,
-  0, // tp_setattro
+  PyObject_GenericSetAttr,
   0, // tp_as_buffer
-  Py_TPFLAGS_DEFAULT,
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
   0, // tp_doc
-  Dtool_StaticProperty_traverse,
+  0, // tp_traverse
   0, // tp_clear
   0, // tp_richcompare
   0, // tp_weaklistoffset
   0, // tp_iter
   0, // tp_iternext
-  0, // tp_methods
+  Dtool_MappingWrapper_Methods,
   0, // tp_members
   0, // tp_getset
   0, // tp_base
   0, // tp_dict
-  (descrgetfunc)Dtool_StaticProperty_get,
-  (descrsetfunc)Dtool_StaticProperty_set,
+  0, // tp_descr_get
+  0, // tp_descr_set
   0, // tp_dictoffset
   0, // tp_init
-  0, // tp_alloc
+  PyType_GenericAlloc,
   0, // tp_new
-  0, // tp_del
+  PyObject_Del,
   0, // tp_is_gc
   0, // tp_bases
   0, // tp_mro
@@ -1342,12 +1703,27 @@ PyTypeObject Dtool_StaticProperty_Type = {
 };
 
 /**
- * This variant defines only a mapping interface.
+ * This variant defines both a sequence and mapping interface.
  */
-PyTypeObject Dtool_MappingWrapper_Type = {
+static PyMappingMethods Dtool_SeqMapWrapper_MappingMethods = {
+  Dtool_SeqMapWrapper_length,
+  Dtool_MappingWrapper_getitem,
+  Dtool_MappingWrapper_setitem,
+};
+
+static PyMethodDef Dtool_SeqMapWrapper_Methods[] = {
+  {"get", &Dtool_MappingWrapper_get, METH_VARARGS, nullptr},
+  {"pop", &Dtool_MappingWrapper_pop, METH_VARARGS, nullptr},
+  {"setdefault", &Dtool_MappingWrapper_setdefault, METH_VARARGS, nullptr},
+  {"update", (PyCFunction) &Dtool_MappingWrapper_update, METH_VARARGS | METH_KEYWORDS, nullptr},
+  {"values", &Dtool_SeqMapWrapper_values, METH_NOARGS, nullptr},
+  {nullptr, nullptr, 0, nullptr}
+};
+
+PyTypeObject Dtool_SeqMapWrapper_Type = {
   PyVarObject_HEAD_INIT(NULL, 0)
-  "mapping wrapper",
-  sizeof(Dtool_MappingWrapper),
+  "sequence/mapping wrapper",
+  sizeof(Dtool_SeqMapWrapper),
   0, // tp_itemsize
   Dtool_WrapperBase_dealloc,
   0, // tp_print
@@ -1358,7 +1734,7 @@ PyTypeObject Dtool_MappingWrapper_Type = {
 #else
   0, // tp_compare
 #endif
-  0, // tp_repr
+  Dtool_WrapperBase_repr,
   0, // tp_as_number
   0, // tp_as_sequence
   &Dtool_MappingWrapper_MappingMethods,
@@ -1376,7 +1752,7 @@ PyTypeObject Dtool_MappingWrapper_Type = {
   0, // tp_weaklistoffset
   0, // tp_iter
   0, // tp_iternext
-  0, // tp_methods
+  Dtool_SeqMapWrapper_Methods,
   0, // tp_members
   0, // tp_getset
   0, // tp_base
@@ -1404,12 +1780,19 @@ PyTypeObject Dtool_MappingWrapper_Type = {
 };
 
 /**
- * This variant defines both a sequence and mapping interface.
+ * This variant defines only a generator interface.
  */
-PyTypeObject Dtool_SeqMapWrapper_Type = {
+static PyObject *Dtool_GeneratorWrapper_iternext(PyObject *self) {
+  Dtool_GeneratorWrapper *wrap = (Dtool_GeneratorWrapper *)self;
+  nassertr(wrap, nullptr);
+  nassertr(wrap->_iternext_func, nullptr);
+  return wrap->_iternext_func(wrap->_base._self);
+}
+
+PyTypeObject Dtool_GeneratorWrapper_Type = {
   PyVarObject_HEAD_INIT(NULL, 0)
-  "sequence/mapping wrapper",
-  sizeof(Dtool_SeqMapWrapper),
+  "generator wrapper",
+  sizeof(Dtool_GeneratorWrapper),
   0, // tp_itemsize
   Dtool_WrapperBase_dealloc,
   0, // tp_print
@@ -1422,8 +1805,8 @@ PyTypeObject Dtool_SeqMapWrapper_Type = {
 #endif
   0, // tp_repr
   0, // tp_as_number
-  &Dtool_SequenceWrapper_SequenceMethods,
-  &Dtool_SeqMapWrapper_MappingMethods,
+  0, // tp_as_sequence
+  0, // tp_as_mapping
   0, // tp_hash
   0, // tp_call
   0, // tp_str
@@ -1436,8 +1819,8 @@ PyTypeObject Dtool_SeqMapWrapper_Type = {
   0, // tp_clear
   0, // tp_richcompare
   0, // tp_weaklistoffset
-  0, // tp_iter
-  0, // tp_iternext
+  PyObject_SelfIter,
+  Dtool_GeneratorWrapper_iternext,
   0, // tp_methods
   0, // tp_members
   0, // tp_getset
@@ -1466,23 +1849,105 @@ PyTypeObject Dtool_SeqMapWrapper_Type = {
 };
 
 /**
- * This variant defines only a generator interface.
+ * This is a variant of the Python getset mechanism that permits static
+ * properties.
  */
-PyTypeObject Dtool_GeneratorWrapper_Type = {
-  PyVarObject_HEAD_INIT(NULL, 0)
-  "generator wrapper",
-  sizeof(Dtool_GeneratorWrapper),
+PyObject *
+Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
+  PyGetSetDescrObject *descr;
+  descr = (PyGetSetDescrObject *)PyType_GenericAlloc(&Dtool_StaticProperty_Type, 0);
+  if (descr != nullptr) {
+    Py_XINCREF(type);
+    descr->d_getset = (PyGetSetDef *)getset;
+#if PY_MAJOR_VERSION >= 3
+    descr->d_common.d_type = type;
+    descr->d_common.d_name = PyUnicode_InternFromString(getset->name);
+#if PY_VERSION_HEX >= 0x03030000
+    descr->d_common.d_qualname = nullptr;
+#endif
+#else
+    descr->d_type = type;
+    descr->d_name = PyString_InternFromString(getset->name);
+#endif
+  }
+  return (PyObject *)descr;
+}
+
+static void
+Dtool_StaticProperty_dealloc(PyDescrObject *descr) {
+  _PyObject_GC_UNTRACK(descr);
+  Py_XDECREF(descr->d_type);
+  Py_XDECREF(descr->d_name);
+//#if PY_MAJOR_VERSION >= 3
+//  Py_XDECREF(descr->d_qualname);
+//#endif
+  PyObject_GC_Del(descr);
+}
+
+static PyObject *
+Dtool_StaticProperty_repr(PyDescrObject *descr, const char *format) {
+#if PY_MAJOR_VERSION >= 3
+  return PyUnicode_FromFormat("<attribute '%s' of '%s'>",
+                              PyUnicode_AsUTF8(descr->d_name),
+                              descr->d_type->tp_name);
+#else
+  return PyString_FromFormat("<attribute '%s' of '%s'>",
+                             PyString_AS_STRING(descr->d_name),
+                             descr->d_type->tp_name);
+#endif
+}
+
+static int
+Dtool_StaticProperty_traverse(PyObject *self, visitproc visit, void *arg) {
+  PyDescrObject *descr = (PyDescrObject *)self;
+  Py_VISIT(descr->d_type);
+  return 0;
+}
+
+static PyObject *
+Dtool_StaticProperty_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type) {
+  if (descr->d_getset->get != nullptr) {
+    return descr->d_getset->get(obj, descr->d_getset->closure);
+  } else {
+    return PyErr_Format(PyExc_AttributeError,
+                        "attribute '%s' of type '%.100s' is not readable",
+#if PY_MAJOR_VERSION >= 3
+                        PyUnicode_AsUTF8(((PyDescrObject *)descr)->d_name),
+#else
+                        PyString_AS_STRING(((PyDescrObject *)descr)->d_name),
+#endif
+                        ((PyDescrObject *)descr)->d_type->tp_name);
+  }
+}
+
+static int
+Dtool_StaticProperty_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) {
+  if (descr->d_getset->set != nullptr) {
+    return descr->d_getset->set(obj, value, descr->d_getset->closure);
+  } else {
+    PyErr_Format(PyExc_AttributeError,
+                 "attribute '%s' of type '%.100s' is not writable",
+#if PY_MAJOR_VERSION >= 3
+                 PyUnicode_AsUTF8(((PyDescrObject *)descr)->d_name),
+#else
+                 PyString_AS_STRING(((PyDescrObject *)descr)->d_name),
+#endif
+                 ((PyDescrObject *)descr)->d_type->tp_name);
+    return -1;
+  }
+}
+
+PyTypeObject Dtool_StaticProperty_Type = {
+  PyVarObject_HEAD_INIT(&PyType_Type, 0)
+  "getset_descriptor",
+  sizeof(PyGetSetDescrObject),
   0, // tp_itemsize
-  Dtool_WrapperBase_dealloc,
+  (destructor)Dtool_StaticProperty_dealloc,
   0, // tp_print
   0, // tp_getattr
   0, // tp_setattr
-#if PY_MAJOR_VERSION >= 3
   0, // tp_reserved
-#else
-  0, // tp_compare
-#endif
-  0, // tp_repr
+  (reprfunc)Dtool_StaticProperty_repr,
   0, // tp_as_number
   0, // tp_as_sequence
   0, // tp_as_mapping
@@ -1490,28 +1955,28 @@ PyTypeObject Dtool_GeneratorWrapper_Type = {
   0, // tp_call
   0, // tp_str
   PyObject_GenericGetAttr,
-  PyObject_GenericSetAttr,
+  0, // tp_setattro
   0, // tp_as_buffer
-  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
+  Py_TPFLAGS_DEFAULT,
   0, // tp_doc
-  0, // tp_traverse
+  Dtool_StaticProperty_traverse,
   0, // tp_clear
   0, // tp_richcompare
   0, // tp_weaklistoffset
-  PyObject_SelfIter,
-  Dtool_GeneratorWrapper_iternext,
+  0, // tp_iter
+  0, // tp_iternext
   0, // tp_methods
   0, // tp_members
   0, // tp_getset
   0, // tp_base
   0, // tp_dict
-  0, // tp_descr_get
-  0, // tp_descr_set
+  (descrgetfunc)Dtool_StaticProperty_get,
+  (descrsetfunc)Dtool_StaticProperty_set,
   0, // tp_dictoffset
   0, // tp_init
-  PyType_GenericAlloc,
+  0, // tp_alloc
   0, // tp_new
-  PyObject_Del,
+  0, // tp_del
   0, // tp_is_gc
   0, // tp_bases
   0, // tp_mro

+ 7 - 4
dtool/src/interrogatedb/py_panda.h

@@ -29,6 +29,7 @@
 #endif
 
 #include "pnotify.h"
+#include "vector_uchar.h"
 
 #if defined(HAVE_PYTHON) && !defined(CPPPARSER)
 
@@ -474,6 +475,7 @@ map_deepcopy_to_copy(PyObject *self, PyObject *args);
 struct Dtool_WrapperBase {
   PyObject_HEAD;
   PyObject *_self;
+  const char *_name;
 };
 
 struct Dtool_SequenceWrapper {
@@ -490,9 +492,10 @@ struct Dtool_MappingWrapper {
 };
 
 struct Dtool_SeqMapWrapper {
-  Dtool_SequenceWrapper _seq;
-  binaryfunc _map_getitem_func;
-  objobjargproc _map_setitem_func;
+  Dtool_MappingWrapper _map;
+  lenfunc _len_func;
+  ssizeargfunc _seq_getitem_func;
+  ssizeobjargproc _seq_setitem_func;
 };
 
 struct Dtool_GeneratorWrapper {
@@ -547,7 +550,7 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(char value);
 ALWAYS_INLINE PyObject *Dtool_WrapValue(wchar_t value);
 ALWAYS_INLINE PyObject *Dtool_WrapValue(nullptr_t);
 ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value);
-ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector<unsigned char> &value);
+ALWAYS_INLINE PyObject *Dtool_WrapValue(const vector_uchar &value);
 
 #if PY_MAJOR_VERSION >= 0x02060000
 ALWAYS_INLINE PyObject *Dtool_WrapValue(Py_buffer *value);

+ 9 - 0
dtool/src/parser-inc/iostream

@@ -88,6 +88,9 @@ __published:
   streampos tellp();
   void seekp(streampos pos);
   void seekp(streamoff off, ios_base::seekdir dir);
+
+protected:
+  ostream(ostream &&);
 };
 class istream : virtual public ios {
 __published:
@@ -97,12 +100,18 @@ __published:
   streampos tellg();
   void seekg(streampos pos);
   void seekg(streamoff off, ios_base::seekdir dir);
+
+protected:
+  istream(istream &&);
 };
 class iostream : public istream, public ostream {
 __published:
   iostream(const iostream&) = delete;
 
   void flush();
+
+protected:
+  iostream(iostream &&);
 };
 
 class ofstream : public ostream {

+ 1 - 1
dtool/src/prc/configVariable.h

@@ -35,7 +35,7 @@ protected:
                         const string &description, int flags);
 
 PUBLISHED:
-  INLINE ConfigVariable(const string &name);
+  INLINE explicit ConfigVariable(const string &name);
   INLINE ~ConfigVariable();
 
   INLINE const string &get_string_value() const;

+ 4 - 4
dtool/src/prc/encryptStream.h

@@ -34,8 +34,8 @@
 class EXPCL_DTOOLCONFIG IDecryptStream : public istream {
 PUBLISHED:
   INLINE IDecryptStream();
-  INLINE IDecryptStream(istream *source, bool owns_source,
-                        const string &password);
+  INLINE explicit IDecryptStream(istream *source, bool owns_source,
+                                 const string &password);
 
 #if _MSC_VER >= 1800
   INLINE IDecryptStream(const IDecryptStream &copy) = delete;
@@ -69,8 +69,8 @@ private:
 class EXPCL_DTOOLCONFIG OEncryptStream : public ostream {
 PUBLISHED:
   INLINE OEncryptStream();
-  INLINE OEncryptStream(ostream *dest, bool owns_dest,
-                        const string &password);
+  INLINE explicit OEncryptStream(ostream *dest, bool owns_dest,
+                                 const string &password);
 
 #if _MSC_VER >= 1800
   INLINE OEncryptStream(const OEncryptStream &copy) = delete;

+ 1 - 1
dtool/src/prc/streamReader.h

@@ -28,7 +28,7 @@ class EXPCL_DTOOLCONFIG StreamReader {
 public:
   INLINE StreamReader(istream &in);
 PUBLISHED:
-  INLINE StreamReader(istream *in, bool owns_stream);
+  INLINE explicit StreamReader(istream *in, bool owns_stream);
   INLINE StreamReader(const StreamReader &copy);
   INLINE void operator = (const StreamReader &copy);
   INLINE ~StreamReader();

+ 3 - 3
dtool/src/prc/streamWrapper.h

@@ -52,7 +52,7 @@ class EXPCL_DTOOLCONFIG IStreamWrapper : virtual public StreamWrapperBase {
 public:
   INLINE IStreamWrapper(istream *stream, bool owns_pointer);
 PUBLISHED:
-  INLINE IStreamWrapper(istream &stream);
+  INLINE explicit IStreamWrapper(istream &stream);
   ~IStreamWrapper();
 
   INLINE istream *get_istream() const;
@@ -79,7 +79,7 @@ class EXPCL_DTOOLCONFIG OStreamWrapper : virtual public StreamWrapperBase {
 public:
   INLINE OStreamWrapper(ostream *stream, bool owns_pointer, bool stringstream_hack = false);
 PUBLISHED:
-  INLINE OStreamWrapper(ostream &stream);
+  INLINE explicit OStreamWrapper(ostream &stream);
   ~OStreamWrapper();
 
   INLINE ostream *get_ostream() const;
@@ -115,7 +115,7 @@ class EXPCL_DTOOLCONFIG StreamWrapper : public IStreamWrapper, public OStreamWra
 public:
   INLINE StreamWrapper(iostream *stream, bool owns_pointer, bool stringstream_hack = false);
 PUBLISHED:
-  INLINE StreamWrapper(iostream &stream);
+  INLINE explicit StreamWrapper(iostream &stream);
   ~StreamWrapper();
 
   INLINE iostream *get_iostream() const;

+ 1 - 1
dtool/src/prc/streamWriter.h

@@ -30,7 +30,7 @@ class EXPCL_DTOOLCONFIG StreamWriter {
 public:
   INLINE StreamWriter(ostream &out);
 PUBLISHED:
-  INLINE StreamWriter(ostream *out, bool owns_stream);
+  INLINE explicit StreamWriter(ostream *out, bool owns_stream);
   INLINE StreamWriter(const StreamWriter &copy);
   INLINE void operator = (const StreamWriter &copy);
   INLINE ~StreamWriter();

+ 4 - 0
dtool/src/pystub/pystub.cxx

@@ -91,6 +91,7 @@ extern "C" {
   EXPCL_PYSTUB int PyModule_AddStringConstant(...);
   EXPCL_PYSTUB int PyModule_Create2(...);
   EXPCL_PYSTUB int PyModule_Create2TraceRefs(...);
+  EXPCL_PYSTUB int PyNumber_AsSsize_t(...);
   EXPCL_PYSTUB int PyNumber_Check(...);
   EXPCL_PYSTUB int PyNumber_Float(...);
   EXPCL_PYSTUB int PyNumber_Int(...);
@@ -216,6 +217,7 @@ extern "C" {
   EXPCL_PYSTUB extern void *PyExc_FutureWarning;
   EXPCL_PYSTUB extern void *PyExc_ImportError;
   EXPCL_PYSTUB extern void *PyExc_IndexError;
+  EXPCL_PYSTUB extern void *PyExc_KeyError;
   EXPCL_PYSTUB extern void *PyExc_OSError;
   EXPCL_PYSTUB extern void *PyExc_OverflowError;
   EXPCL_PYSTUB extern void *PyExc_RuntimeError;
@@ -313,6 +315,7 @@ int PyModule_AddObject(...) { return 0; };
 int PyModule_AddStringConstant(...) { return 0; };
 int PyModule_Create2(...) { return 0; };
 int PyModule_Create2TraceRefs(...) { return 0; };
+int PyNumber_AsSsize_t(...) { return 0; }
 int PyNumber_Check(...) { return 0; }
 int PyNumber_Float(...) { return 0; }
 int PyNumber_Int(...) { return 0; }
@@ -444,6 +447,7 @@ void *PyExc_Exception = (void *)NULL;
 void *PyExc_FutureWarning = (void *)NULL;
 void *PyExc_ImportError = (void *)NULL;
 void *PyExc_IndexError = (void *)NULL;
+void *PyExc_KeyError = (void *)NULL;
 void *PyExc_OSError = (void *)NULL;
 void *PyExc_OverflowError = (void *)NULL;
 void *PyExc_RuntimeError = (void *)NULL;

+ 41 - 17
makepanda/makepanda.py

@@ -41,6 +41,7 @@ import sys
 COMPILER=0
 INSTALLER=0
 WHEEL=0
+RUNTESTS=0
 GENMAN=0
 COMPRESSOR="zlib"
 THREADCOUNT=0
@@ -128,6 +129,7 @@ def usage(problem):
     print("  --help            (print the help message you're reading now)")
     print("  --verbose         (print out more information)")
     print("  --runtime         (build a runtime build instead of an SDK build)")
+    print("  --tests           (run the test suite)")
     print("  --installer       (build an installer)")
     print("  --wheel           (build a pip-installable .whl)")
     print("  --optimize X      (optimization level can be 1,2,3,4)")
@@ -165,12 +167,12 @@ def usage(problem):
     os._exit(1)
 
 def parseopts(args):
-    global INSTALLER,WHEEL,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION
+    global INSTALLER,WHEEL,RUNTESTS,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION
     global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL
     global DEBVERSION,WHLVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION
     global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER
     longopts = [
-        "help","distributor=","verbose","runtime","osxtarget=",
+        "help","distributor=","verbose","runtime","osxtarget=","tests",
         "optimize=","everything","nothing","installer","wheel","rtdist","nocolor",
         "version=","lzma","no-python","threads=","outputdir=","override=",
         "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=",
@@ -194,6 +196,7 @@ def parseopts(args):
             if (option=="--help"): raise Exception
             elif (option=="--optimize"): optimize=value
             elif (option=="--installer"): INSTALLER=1
+            elif (option=="--tests"): RUNTESTS=1
             elif (option=="--wheel"): WHEEL=1
             elif (option=="--verbose"): SetVerbose(True)
             elif (option=="--distributor"): DISTRIBUTOR=value
@@ -316,12 +319,14 @@ def parseopts(args):
     if GetTarget() == 'windows':
         if not MSVC_VERSION:
             print("No MSVC version specified. Defaulting to 10 (Visual Studio 2010).")
-            MSVC_VERSION = 10
-
-        try:
-            MSVC_VERSION = int(MSVC_VERSION)
-        except:
-            usage("Invalid setting for --msvc-version")
+            MSVC_VERSION = (10, 0)
+        else:
+            try:
+                MSVC_VERSION = tuple(int(d) for d in MSVC_VERSION.split('.'))[:2]
+                if (len(MSVC_VERSION) == 1):
+                    MSVC_VERSION += (0,)
+            except:
+                usage("Invalid setting for --msvc-version")
 
         if not WINDOWS_SDK:
             print("No Windows SDK version specified. Defaulting to '7.1'.")
@@ -369,7 +374,8 @@ if VERSION is None:
     if RUNTIME:
         VERSION = PLUGIN_VERSION
     else:
-        VERSION = ParsePandaVersion("dtool/PandaVersion.pp")
+        # Take the value from the setup.cfg file.
+        VERSION = GetMetadataValue('version')
 
 if WHLVERSION is None:
     WHLVERSION = VERSION
@@ -635,7 +641,7 @@ if (COMPILER == "MSVC"):
             #LibName(pkg, 'ddraw.lib')
             LibName(pkg, 'dxguid.lib')
 
-            if SDK.get("VISUALSTUDIO_VERSION") == '14.0':
+            if SDK.get("VISUALSTUDIO_VERSION") >= (14,0):
                 # dxerr needs this for __vsnwprintf definition.
                 LibName(pkg, 'legacy_stdio_definitions.lib')
 
@@ -702,7 +708,9 @@ if (COMPILER == "MSVC"):
     if (PkgSkip("NVIDIACG")==0): LibName("CGDX9",    GetThirdpartyDir() + "nvidiacg/lib/cgD3D9.lib")
     if (PkgSkip("NVIDIACG")==0): LibName("NVIDIACG", GetThirdpartyDir() + "nvidiacg/lib/cg.lib")
     if (PkgSkip("FREETYPE")==0): LibName("FREETYPE", GetThirdpartyDir() + "freetype/lib/freetype.lib")
-    if (PkgSkip("HARFBUZZ")==0): LibName("HARFBUZZ", GetThirdpartyDir() + "harfbuzz/lib/harfbuzz.lib")
+    if (PkgSkip("HARFBUZZ")==0):
+        LibName("HARFBUZZ", GetThirdpartyDir() + "harfbuzz/lib/harfbuzz.lib")
+        IncDirectory("HARFBUZZ", GetThirdpartyDir() + "harfbuzz/include/harfbuzz")
     if (PkgSkip("FFTW")==0):     LibName("FFTW",     GetThirdpartyDir() + "fftw/lib/rfftw.lib")
     if (PkgSkip("FFTW")==0):     LibName("FFTW",     GetThirdpartyDir() + "fftw/lib/fftw.lib")
     if (PkgSkip("ARTOOLKIT")==0):LibName("ARTOOLKIT",GetThirdpartyDir() + "artoolkit/lib/libAR.lib")
@@ -1147,7 +1155,7 @@ def CompileCxx(obj,src,opts):
             # We still target Windows XP.
             cmd += "/DWINVER=0x501 "
             # Work around a WinXP/2003 bug when using VS 2015+.
-            if SDK.get("VISUALSTUDIO_VERSION") == '14.0':
+            if SDK.get("VISUALSTUDIO_VERSION") >= (14,0):
                 cmd += "/Zc:threadSafeInit- "
 
             cmd += "/Fo" + obj + " /nologo /c"
@@ -1188,7 +1196,7 @@ def CompileCxx(obj,src,opts):
             if GetTargetArch() == 'x64':
                 cmd += " /DWIN64_VC /DWIN64"
 
-            if WINDOWS_SDK.startswith('7.') and MSVC_VERSION > 10:
+            if WINDOWS_SDK.startswith('7.') and MSVC_VERSION > (10,):
                 # To preserve Windows XP compatibility.
                 cmd += " /D_USING_V110_SDK71_"
 
@@ -2768,7 +2776,9 @@ for basename in del_files:
 
 # Write an appropriate panda3d/__init__.py
 p3d_init = """"Python bindings for the Panda3D libraries"
-"""
+
+__version__ = '%s'
+""" % (WHLVERSION)
 
 if GetTarget() == 'windows':
     p3d_init += """
@@ -3070,9 +3080,13 @@ if tp_dir is not None:
 
 # Copy over the MSVC runtime.
 if GetTarget() == 'windows' and "VISUALSTUDIO" in SDK:
-    vcver = SDK["VISUALSTUDIO_VERSION"].replace('.', '')
-    crtname = "Microsoft.VC%s.CRT" % (vcver)
-    dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "redist", GetTargetArch(), crtname)
+    vsver = "%s%s" % SDK["VISUALSTUDIO_VERSION"]
+    vcver = "%s%s" % (SDK["MSVC_VERSION"][0], 0)        # ignore minor version.
+    crtname = "Microsoft.VC%s.CRT" % (vsver)
+    if ("VCTOOLSVERSION" in SDK):
+        dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "Redist", "MSVC", SDK["VCTOOLSVERSION"], "onecore", GetTargetArch(), crtname)
+    else:
+        dir = os.path.join(SDK["VISUALSTUDIO"], "VC", "redist", GetTargetArch(), crtname)
 
     if os.path.isfile(os.path.join(dir, "msvcr" + vcver + ".dll")):
         CopyFile(GetOutputDir() + "/bin/", os.path.join(dir, "msvcr" + vcver + ".dll"))
@@ -6802,6 +6816,16 @@ except:
     SaveDependencyCache()
     raise
 
+# Run the test suite.
+if RUNTESTS:
+    cmdstr = BracketNameWithQuotes(SDK["PYTHONEXEC"].replace('\\', '/'))
+    if sys.version_info >= (2, 6):
+        cmdstr += " -B"
+    cmdstr += " -m pytest tests"
+    if GetVerbose():
+        cmdstr += " --verbose"
+    oscmd(cmdstr)
+
 ##########################################################################################
 #
 # The Installers

+ 145 - 30
makepanda/makepandacore.py

@@ -12,9 +12,11 @@ from distutils import sysconfig
 if sys.version_info >= (3, 0):
     import pickle
     import _thread as thread
+    import configparser
 else:
     import cPickle as pickle
     import thread
+    import ConfigParser as configparser
 
 SUFFIX_INC = [".cxx",".cpp",".c",".h",".I",".yxx",".lxx",".mm",".rc",".r"]
 SUFFIX_DLL = [".dll",".dlo",".dle",".dli",".dlm",".mll",".exe",".pyd",".ocx"]
@@ -55,6 +57,20 @@ else:
     # case.
     host_64 = (platform.architecture()[0] == '64bit')
 
+########################################################################
+##
+## Visual C++ Version (MSVC) and Visual Studio Information Map
+##
+########################################################################
+
+MSVCVERSIONINFO = {
+    (10,0): {"vsversion":(10,0), "vsname":"Visual Studio 2010"},
+    (11,0): {"vsversion":(11,0), "vsname":"Visual Studio 2012"},
+    (12,0): {"vsversion":(12,0), "vsname":"Visual Studio 2013"},
+    (14,0): {"vsversion":(14,0), "vsname":"Visual Studio 2015"},
+    (14,1): {"vsversion":(15,0), "vsname":"Visual Studio 2017"},
+}
+
 ########################################################################
 ##
 ## Maya and Max Version List (with registry keys)
@@ -919,6 +935,17 @@ def GetProgramFiles():
         return "E:\\Program Files"
     return 0
 
+def GetProgramFiles_x86():
+    if ("ProgramFiles(x86)" in os.environ):
+        return os.environ["ProgramFiles(x86)"]
+    elif (os.path.isdir("C:\\Program Files (x86)")):
+        return "C:\\Program Files (x86)"
+    elif (os.path.isdir("D:\\Program Files (x86)")):
+        return "D:\\Program Files (x86)"
+    elif (os.path.isdir("E:\\Program Files (x86)")):
+        return "E:\\Program Files (x86)"
+    return GetProgramFiles()
+
 ########################################################################
 ##
 ## Parsing Compiler Option Lists
@@ -1145,7 +1172,7 @@ def GetThirdpartyDir():
     target_arch = GetTargetArch()
 
     if (target == 'windows'):
-        vc = SDK["VISUALSTUDIO_VERSION"].split('.')[0]
+        vc = str(SDK["MSVC_VERSION"][0])
 
         if target_arch == 'x64':
             THIRDPARTYDIR = base + "/win-libs-vc" + vc + "-x64/"
@@ -2040,21 +2067,64 @@ def SdkLocatePython(prefer_thirdparty_python=False):
     else:
         print("Using Python %s" % (SDK["PYTHONVERSION"][6:9]))
 
-def SdkLocateVisualStudio(version=10):
+def SdkLocateVisualStudio(version=(10,0)):
     if (GetHost() != "windows"): return
 
-    version = str(version) + '.0'
+    try:
+        msvcinfo = MSVCVERSIONINFO[version]
+    except:
+        exit("Couldn't get Visual Studio infomation with MSVC %s.%s version." % version)
+
+    vsversion = msvcinfo["vsversion"]
+    vsversion_str = "%s.%s" % vsversion
+    version_str = "%s.%s" % version
+
+    # try to use vswhere.exe
+    vswhere_path = LocateBinary("vswhere.exe")
+    if not vswhere_path:
+        if sys.platform == 'cygwin':
+            vswhere_path = "/cygdrive/c/Program Files/Microsoft Visual Studio/Installer/vswhere.exe"
+        else:
+            vswhere_path = "%s\\Microsoft Visual Studio\\Installer\\vswhere.exe" % GetProgramFiles()
+        if not os.path.isfile(vswhere_path):
+            vswhere_path = None
 
-    vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", version)
-    if (vcdir != 0) and (vcdir[-4:] == "\\VC\\"):
+    if not vswhere_path:
+        if sys.platform == 'cygwin':
+            vswhere_path = "/cygdrive/c/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe"
+        else:
+            vswhere_path = "%s\\Microsoft Visual Studio\\Installer\\vswhere.exe" % GetProgramFiles_x86()
+        if not os.path.isfile(vswhere_path):
+            vswhere_path = None
+
+    vsdir = 0
+    if vswhere_path:
+        min_vsversion = vsversion_str
+        max_vsversion = "%s.%s" % (vsversion[0]+1, 0)
+        vswhere_cmd = ["vswhere.exe", "-legacy", "-property", "installationPath",
+            "-version", "[{},{})".format(min_vsversion, max_vsversion)]
+        handle = subprocess.Popen(vswhere_cmd, executable=vswhere_path, stdout=subprocess.PIPE)
+        found_paths = handle.communicate()[0].splitlines()
+        if found_paths:
+            vsdir = found_paths[0].decode("utf-8") + "\\"
+
+    # try to use registry
+    if (vsdir == 0):
+        vsdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", vsversion_str)
+    vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", version_str)
+
+    if (vsdir != 0):
+        SDK["VISUALSTUDIO"] = vsdir
+
+    elif (vcdir != 0) and (vcdir[-4:] == "\\VC\\"):
         vcdir = vcdir[:-3]
         SDK["VISUALSTUDIO"] = vcdir
 
-    elif (os.path.isfile("C:\\Program Files\\Microsoft Visual Studio %s\\VC\\bin\\cl.exe" % (version))):
-        SDK["VISUALSTUDIO"] = "C:\\Program Files\\Microsoft Visual Studio %s\\" % (version)
+    elif (os.path.isfile("C:\\Program Files\\Microsoft Visual Studio %s\\VC\\bin\\cl.exe" % (vsversion_str))):
+        SDK["VISUALSTUDIO"] = "C:\\Program Files\\Microsoft Visual Studio %s\\" % (vsversion_str)
 
-    elif (os.path.isfile("C:\\Program Files (x86)\\Microsoft Visual Studio %s\\VC\\bin\\cl.exe" % (version))):
-        SDK["VISUALSTUDIO"] = "C:\\Program Files (x86)\\Microsoft Visual Studio %s\\" % (version)
+    elif (os.path.isfile("C:\\Program Files (x86)\\Microsoft Visual Studio %s\\VC\\bin\\cl.exe" % (vsversion_str))):
+        SDK["VISUALSTUDIO"] = "C:\\Program Files (x86)\\Microsoft Visual Studio %s\\" % (vsversion_str)
 
     elif "VCINSTALLDIR" in os.environ:
         vcdir = os.environ["VCINSTALLDIR"]
@@ -2066,14 +2136,17 @@ def SdkLocateVisualStudio(version=10):
         SDK["VISUALSTUDIO"] = vcdir
 
     else:
-        exit("Couldn't find Visual Studio %s.  To use a different version, use the --msvc-version option." % version)
+        exit("Couldn't find %s.  To use a different version, use the --msvc-version option." % msvcinfo["vsname"])
 
-    SDK["VISUALSTUDIO_VERSION"] = version
+    SDK["MSVC_VERSION"] = version
+    SDK["VISUALSTUDIO_VERSION"] = vsversion
 
     if GetVerbose():
-        print("Using Visual Studio %s located at %s" % (version, SDK["VISUALSTUDIO"]))
+        print("Using %s located at %s" % (msvcinfo["vsname"], SDK["VISUALSTUDIO"]))
     else:
-        print("Using Visual Studio %s" % (version))
+        print("Using %s" % (msvcinfo["vsname"]))
+
+    print("Using MSVC %s" % version_str)
 
 def SdkLocateWindows(version = '7.1'):
     if GetTarget() != "windows" or GetHost() != "windows":
@@ -2381,7 +2454,19 @@ def SetupVisualStudioEnviron():
         exit("Could not find Visual Studio install directory")
     if ("MSPLATFORM" not in SDK):
         exit("Could not find the Microsoft Platform SDK")
-    os.environ["VCINSTALLDIR"] = SDK["VISUALSTUDIO"] + "VC"
+
+    if (SDK["VISUALSTUDIO_VERSION"] >= (15,0)):
+        try:
+            vsver_file = open(os.path.join(SDK["VISUALSTUDIO"],
+                "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"), "r")
+            SDK["VCTOOLSVERSION"] = vsver_file.readline().strip()
+            vcdir_suffix = "VC\\Tools\\MSVC\\%s\\" % SDK["VCTOOLSVERSION"]
+        except:
+            exit("Couldn't find tool version of %s." % MSVCVERSIONINFO[SDK["MSVC_VERSION"]]["vsname"])
+    else:
+        vcdir_suffix = "VC\\"
+
+    os.environ["VCINSTALLDIR"] = SDK["VISUALSTUDIO"] + vcdir_suffix
     os.environ["WindowsSdkDir"] = SDK["MSPLATFORM"]
 
     winsdk_ver = SDK["MSPLATFORM_VERSION"]
@@ -2390,18 +2475,22 @@ def SetupVisualStudioEnviron():
     arch = GetTargetArch()
     bindir = ""
     libdir = ""
-    if (arch == 'x64'):
-        bindir = 'amd64'
-        libdir = 'amd64'
-    elif (arch != 'x86'):
-        bindir = arch
+    if ("VCTOOLSVERSION" in SDK):
+        bindir = "Host" + GetHostArch().upper() + "\\" + arch
         libdir = arch
-
-    if (arch != 'x86' and GetHostArch() == 'x86'):
-        # Special version of the tools that run on x86.
-        bindir = 'x86_' + bindir
-
-    vc_binpath = SDK["VISUALSTUDIO"] + "VC\\bin"
+    else:
+        if (arch == 'x64'):
+            bindir = 'amd64'
+            libdir = 'amd64'
+        elif (arch != 'x86'):
+            bindir = arch
+            libdir = arch
+
+        if (arch != 'x86' and GetHostArch() == 'x86'):
+            # Special version of the tools that run on x86.
+            bindir = 'x86_' + bindir
+
+    vc_binpath = SDK["VISUALSTUDIO"] + vcdir_suffix + "bin"
     binpath = os.path.join(vc_binpath, bindir)
     if not os.path.isfile(binpath + "\\cl.exe"):
         # Try the x86 tools, those should work just as well.
@@ -2414,10 +2503,10 @@ def SetupVisualStudioEnviron():
 
     AddToPathEnv("PATH",    binpath)
     AddToPathEnv("PATH",    SDK["VISUALSTUDIO"] + "Common7\\IDE")
-    AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\include")
-    AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\atlmfc\\include")
-    AddToPathEnv("LIB",     SDK["VISUALSTUDIO"] + "VC\\lib\\" + libdir)
-    AddToPathEnv("LIB",     SDK["VISUALSTUDIO"] + "VC\\atlmfc\\lib\\" + libdir)
+    AddToPathEnv("INCLUDE", os.environ["VCINSTALLDIR"] + "include")
+    AddToPathEnv("INCLUDE", os.environ["VCINSTALLDIR"] + "atlmfc\\include")
+    AddToPathEnv("LIB",     os.environ["VCINSTALLDIR"] + "lib\\" + libdir)
+    AddToPathEnv("LIB",     os.environ["VCINSTALLDIR"] + "atlmfc\\lib\\" + libdir)
 
     winsdk_ver = SDK["MSPLATFORM_VERSION"]
     if winsdk_ver.startswith('10.'):
@@ -2432,6 +2521,16 @@ def SetupVisualStudioEnviron():
         AddToPathEnv("INCLUDE", inc_dir + "um")
         AddToPathEnv("LIB", lib_dir + "ucrt\\" + arch)
         AddToPathEnv("LIB", lib_dir + "um\\" + arch)
+    elif winsdk_ver == '8.1':
+        AddToPathEnv("PATH",    SDK["MSPLATFORM"] + "bin\\" + arch)
+
+        inc_dir = SDK["MSPLATFORM"] + "Include\\"
+        lib_dir = SDK["MSPLATFORM"] + "Lib\\winv6.3\\"
+        AddToPathEnv("INCLUDE", inc_dir + "shared")
+        AddToPathEnv("INCLUDE", inc_dir + "ucrt")
+        AddToPathEnv("INCLUDE", inc_dir + "um")
+        AddToPathEnv("LIB", lib_dir + "ucrt\\" + arch)
+        AddToPathEnv("LIB", lib_dir + "um\\" + arch)
     else:
         AddToPathEnv("PATH",    SDK["MSPLATFORM"] + "bin")
         AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include")
@@ -2454,7 +2553,7 @@ def SetupVisualStudioEnviron():
 
     # Targeting the 7.1 SDK (which is the only way to have Windows XP support)
     # with Visual Studio 2015 requires use of the Universal CRT.
-    if winsdk_ver == '7.1' and SDK["VISUALSTUDIO_VERSION"] == '14.0':
+    if winsdk_ver == '7.1' and SDK["VISUALSTUDIO_VERSION"] >= (14,0):
         win_kit = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
 
         # Fallback in case we can't read the registry.
@@ -2849,6 +2948,22 @@ def CopyPythonTree(dstdir, srcdir, lib2to3_fixers=[], threads=0):
 ##
 ########################################################################
 
+cfg_parser = None
+
+def GetMetadataValue(key):
+    global cfg_parser
+    if not cfg_parser:
+        # Parse the metadata from the setup.cfg file.
+        cfg_parser = configparser.ConfigParser()
+        path = os.path.join(os.path.dirname(__file__), '..', 'setup.cfg')
+        assert cfg_parser.read(path), "Could not read setup.cfg file."
+
+    value = cfg_parser.get('metadata', key)
+    if key == 'classifiers':
+        value = value.strip().split('\n')
+    return value
+
+# This function is being phased out.
 def ParsePandaVersion(fn):
     try:
         f = open(fn, "r")

+ 10 - 23
makepanda/makewheel.py

@@ -20,7 +20,7 @@ import tempfile
 import subprocess
 from distutils.sysconfig import get_config_var
 from optparse import OptionParser
-from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose
+from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose, GetMetadataValue
 from base64 import urlsafe_b64encode
 
 
@@ -101,16 +101,15 @@ Tag: {0}-{1}-{2}
 """
 
 METADATA = {
-    "license": "BSD",
-    "name": "Panda3D",
+    "license": GetMetadataValue('license'),
+    "name": GetMetadataValue('name'),
     "metadata_version": "2.0",
     "generator": "makepanda",
-    "summary": "Panda3D is a game engine, a framework for 3D rendering and "
-               "game development for Python and C++ programs.",
+    "summary": GetMetadataValue('description'),
     "extensions": {
         "python.details": {
             "project_urls": {
-                "Home": "https://www.panda3d.org/"
+                "Home": GetMetadataValue('url'),
             },
             "document_names": {
                 "license": "LICENSE.txt"
@@ -118,25 +117,13 @@ METADATA = {
             "contacts": [
                 {
                     "role": "author",
-                    "email": "[email protected]",
-                    "name": "Panda3D Team"
+                    "name": GetMetadataValue('author'),
+                    "email": GetMetadataValue('author_email'),
                 }
             ]
         }
     },
-    "classifiers": [
-        "Development Status :: 5 - Production/Stable",
-        "Intended Audience :: Developers",
-        "Intended Audience :: End Users/Desktop",
-        "License :: OSI Approved :: BSD License",
-        "Operating System :: OS Independent",
-        "Programming Language :: C++",
-        "Programming Language :: Python",
-        "Topic :: Games/Entertainment",
-        "Topic :: Multimedia",
-        "Topic :: Multimedia :: Graphics",
-        "Topic :: Multimedia :: Graphics :: 3D Rendering"
-    ]
+    "classifiers": GetMetadataValue('classifiers'),
 }
 
 PANDA3D_TOOLS_INIT = """import os, sys
@@ -402,7 +389,7 @@ class WheelFile(object):
         fp.close()
 
         # Save it in PEP-0376 format for writing out later.
-        digest = str(urlsafe_b64encode(sha.digest()))
+        digest = urlsafe_b64encode(sha.digest()).decode('ascii')
         digest = digest.rstrip('=')
         self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, size))
 
@@ -418,7 +405,7 @@ class WheelFile(object):
 
         sha = hashlib.sha256()
         sha.update(source_data.encode())
-        digest = str(urlsafe_b64encode(sha.digest()))
+        digest = urlsafe_b64encode(sha.digest()).decode('ascii')
         digest = digest.rstrip('=')
         self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, len(source_data)))
 

+ 3 - 2
panda/src/audio/audioLoadRequest.h

@@ -32,8 +32,9 @@ public:
   ALLOC_DELETED_CHAIN(AudioLoadRequest);
 
 PUBLISHED:
-  INLINE AudioLoadRequest(AudioManager *audio_manager, const string &filename,
-                          bool positional);
+  INLINE explicit AudioLoadRequest(AudioManager *audio_manager,
+                                   const string &filename,
+                                   bool positional);
 
   INLINE AudioManager *get_audio_manager() const;
   INLINE const string &get_filename() const;

+ 1 - 2
panda/src/bullet/bulletBaseCharacterControllerNode.h

@@ -27,9 +27,8 @@
  *
  */
 class EXPCL_PANDABULLET BulletBaseCharacterControllerNode : public PandaNode {
-
 PUBLISHED:
-  BulletBaseCharacterControllerNode(const char *name="character");
+  explicit BulletBaseCharacterControllerNode(const char *name="character");
 
 public:
   virtual CollideMask get_legal_collide_mask() const;

+ 11 - 1
panda/src/bullet/bulletBoxShape.I

@@ -11,6 +11,15 @@
  * @date 2010-01-24
  */
 
+/**
+ * Only used by make_from_bam.
+ */
+INLINE BulletBoxShape::
+BulletBoxShape() :
+  _shape(nullptr),
+  _half_extents(LVecBase3::zero()) {
+}
+
 /**
  *
  */
@@ -25,7 +34,7 @@ INLINE BulletBoxShape::
  */
 INLINE BulletBoxShape::
 BulletBoxShape(const BulletBoxShape &copy) :
-  _shape(copy._shape) {
+  _shape(copy._shape), _half_extents(copy._half_extents) {
 }
 
 /**
@@ -34,4 +43,5 @@ BulletBoxShape(const BulletBoxShape &copy) :
 INLINE void BulletBoxShape::
 operator = (const BulletBoxShape &copy) {
   _shape = copy._shape;
+  _half_extents = copy._half_extents;
 }

+ 7 - 6
panda/src/bullet/bulletBoxShape.cxx

@@ -20,7 +20,7 @@ TypeHandle BulletBoxShape::_type_handle;
  *
  */
 BulletBoxShape::
-BulletBoxShape(const LVecBase3 &halfExtents) {
+BulletBoxShape(const LVecBase3 &halfExtents) : _half_extents(halfExtents) {
 
   btVector3 btHalfExtents = LVecBase3_to_btVector3(halfExtents);
 
@@ -85,8 +85,9 @@ register_with_read_factory() {
  */
 void BulletBoxShape::
 write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
   dg.add_stdfloat(get_margin());
-  get_half_extents_with_margin().write_datagram(dg);
+  _half_extents.write_datagram(dg);
 }
 
 /**
@@ -112,14 +113,14 @@ make_from_bam(const FactoryParams &params) {
  */
 void BulletBoxShape::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  nassertv(_shape == NULL);
+  BulletShape::fillin(scan, manager);
+  nassertv(_shape == nullptr);
 
   PN_stdfloat margin = scan.get_stdfloat();
 
-  LVector3 half_extents;
-  half_extents.read_datagram(scan);
+  _half_extents.read_datagram(scan);
 
-  _shape = new btBoxShape(LVecBase3_to_btVector3(half_extents));
+  _shape = new btBoxShape(LVecBase3_to_btVector3(_half_extents));
   _shape->setUserPointer(this);
   _shape->setMargin(margin);
 }

+ 3 - 2
panda/src/bullet/bulletBoxShape.h

@@ -29,10 +29,10 @@
 class EXPCL_PANDABULLET BulletBoxShape : public BulletShape {
 private:
   // Only used by make_from_bam
-  INLINE BulletBoxShape() : _shape(NULL) {};
+  INLINE BulletBoxShape();
 
 PUBLISHED:
-  BulletBoxShape(const LVecBase3 &halfExtents);
+  explicit BulletBoxShape(const LVecBase3 &halfExtents);
   INLINE BulletBoxShape(const BulletBoxShape &copy);
   INLINE void operator = (const BulletBoxShape &copy);
   INLINE ~BulletBoxShape();
@@ -50,6 +50,7 @@ public:
 
 private:
   btBoxShape *_shape;
+  LVecBase3 _half_extents;
 
 public:
   static void register_with_read_factory();

+ 27 - 6
panda/src/bullet/bulletCapsuleShape.I

@@ -11,6 +11,16 @@
  * @date 2010-01-27
  */
 
+/**
+ * Only used by make_from_bam.
+ */
+INLINE BulletCapsuleShape::
+BulletCapsuleShape() :
+  _shape(nullptr),
+  _radius(0),
+  _height(0) {
+}
+
 /**
  *
  */
@@ -25,7 +35,9 @@ INLINE BulletCapsuleShape::
  */
 INLINE BulletCapsuleShape::
 BulletCapsuleShape(const BulletCapsuleShape &copy) :
-  _shape(copy._shape) {
+  _shape(copy._shape),
+  _radius(copy._radius),
+  _height(copy._height) {
 }
 
 /**
@@ -34,22 +46,31 @@ BulletCapsuleShape(const BulletCapsuleShape &copy) :
 INLINE void BulletCapsuleShape::
 operator = (const BulletCapsuleShape &copy) {
   _shape = copy._shape;
+  _radius = copy._radius;
+  _height = copy._height;
 }
 
 /**
- *
+ * Returns the radius that was used to construct this capsule.
  */
 INLINE PN_stdfloat BulletCapsuleShape::
 get_radius() const {
-
-  return (PN_stdfloat)_shape->getRadius();
+  return _radius;
 }
 
 /**
- *
+ * Returns half of get_height().
+ * @deprecated see get_height() instead.
  */
 INLINE PN_stdfloat BulletCapsuleShape::
 get_half_height() const {
+  return _height * 0.5;
+}
 
-  return (PN_stdfloat)_shape->getHalfHeight();
+/**
+ * Returns the height that was used to construct this capsule.
+ */
+INLINE PN_stdfloat BulletCapsuleShape::
+get_height() const {
+  return _height;
 }

+ 79 - 1
panda/src/bullet/bulletCapsuleShape.cxx

@@ -19,7 +19,9 @@ TypeHandle BulletCapsuleShape::_type_handle;
  *
  */
 BulletCapsuleShape::
-BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) {
+BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) :
+  _radius(radius),
+  _height(height) {
 
   switch (up) {
   case X_up:
@@ -47,3 +49,79 @@ ptr() const {
 
   return _shape;
 }
+
+/**
+ * Tells the BamReader how to create objects of type BulletShape.
+ */
+void BulletCapsuleShape::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void BulletCapsuleShape::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
+  dg.add_stdfloat(get_margin());
+
+  // parameters to serialize: radius, height, up
+  dg.add_stdfloat(_radius);
+  dg.add_stdfloat(_height);
+  dg.add_int8((int8_t)_shape->getUpAxis());
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type BulletShape is encountered in the Bam file.  It should create the
+ * BulletShape and extract its information from the file.
+ */
+TypedWritable *BulletCapsuleShape::
+make_from_bam(const FactoryParams &params) {
+  // create a default BulletCapsuleShape
+  BulletCapsuleShape *param = new BulletCapsuleShape;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new BulletShape.
+ */
+void BulletCapsuleShape::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  BulletShape::fillin(scan, manager);
+  nassertv(_shape == nullptr);
+
+  PN_stdfloat margin = scan.get_stdfloat();
+
+  // parameters to serialize: radius, height, up
+  _radius = scan.get_stdfloat();
+  _height = scan.get_stdfloat();
+  int up = (int) scan.get_int8();
+
+  switch (up) {
+  case X_up:
+    _shape = new btCapsuleShapeX(_radius, _height);
+    break;
+  case Y_up:
+    _shape = new btCapsuleShape(_radius, _height);
+    break;
+  case Z_up:
+    _shape = new btCapsuleShapeZ(_radius, _height);
+    break;
+  default:
+    bullet_cat.error() << "invalid up-axis:" << up << endl;
+    break;
+  }
+
+  _shape->setUserPointer(this);
+  _shape->setMargin(margin);
+}

+ 20 - 4
panda/src/bullet/bulletCapsuleShape.h

@@ -24,9 +24,12 @@
  *
  */
 class EXPCL_PANDABULLET BulletCapsuleShape : public BulletShape {
+private:
+  // Only used by make_from_bam
+  INLINE BulletCapsuleShape();
 
 PUBLISHED:
-  BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up);
+  explicit BulletCapsuleShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up);
   INLINE BulletCapsuleShape(const BulletCapsuleShape &copy);
   INLINE void operator = (const BulletCapsuleShape &copy);
   INLINE ~BulletCapsuleShape();
@@ -34,14 +37,27 @@ PUBLISHED:
   INLINE PN_stdfloat get_radius() const;
   INLINE PN_stdfloat get_half_height() const;
 
-  MAKE_PROPERTY(radius, get_radius);
-  MAKE_PROPERTY(half_height, get_half_height);
-
 public:
+  INLINE PN_stdfloat get_height() const;
+
   virtual btCollisionShape *ptr() const;
 
+PUBLISHED:
+  MAKE_PROPERTY(radius, get_radius);
+  MAKE_PROPERTY(height, get_height);
+
 private:
   btCapsuleShape *_shape;
+  PN_stdfloat _radius;
+  PN_stdfloat _height;
+
+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() {

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

@@ -29,9 +29,9 @@
  *
  */
 class EXPCL_PANDABULLET BulletCharacterControllerNode : public BulletBaseCharacterControllerNode {
-
 PUBLISHED:
-  BulletCharacterControllerNode(BulletShape *shape, PN_stdfloat step_height, const char *name="character");
+  explicit BulletCharacterControllerNode(BulletShape *shape, PN_stdfloat step_height,
+                                         const char *name="character");
   INLINE ~BulletCharacterControllerNode();
 
   void set_linear_movement(const LVector3 &velocity, bool is_local);

+ 19 - 7
panda/src/bullet/bulletConeShape.I

@@ -11,6 +11,16 @@
  * @date 2010-01-24
  */
 
+/**
+ * Only used by make_from_bam.
+ */
+INLINE BulletConeShape::
+BulletConeShape() :
+  _shape(nullptr),
+  _radius(0),
+  _height(0) {
+}
+
 /**
  *
  */
@@ -25,7 +35,9 @@ INLINE BulletConeShape::
  */
 INLINE BulletConeShape::
 BulletConeShape(const BulletConeShape &copy) :
-  _shape(copy._shape) {
+  _shape(copy._shape),
+  _radius(copy._radius),
+  _height(copy._height) {
 }
 
 /**
@@ -34,22 +46,22 @@ BulletConeShape(const BulletConeShape &copy) :
 INLINE void BulletConeShape::
 operator = (const BulletConeShape &copy) {
   _shape = copy._shape;
+  _radius = copy._radius;
+  _height = copy._height;
 }
 
 /**
- *
+ * Returns the radius that was passed into the constructor.
  */
 INLINE PN_stdfloat BulletConeShape::
 get_radius() const {
-
-  return (PN_stdfloat)_shape->getRadius();
+  return _radius;
 }
 
 /**
- *
+ * Returns the height that was passed into the constructor.
  */
 INLINE PN_stdfloat BulletConeShape::
 get_height() const {
-
-  return (PN_stdfloat)_shape->getHeight();
+  return _height;
 }

+ 79 - 1
panda/src/bullet/bulletConeShape.cxx

@@ -19,7 +19,9 @@ TypeHandle BulletConeShape::_type_handle;
  *
  */
 BulletConeShape::
-BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) {
+BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) :
+  _radius(radius),
+  _height(height) {
 
   switch (up) {
   case X_up:
@@ -47,3 +49,79 @@ ptr() const {
 
   return _shape;
 }
+
+/**
+ * Tells the BamReader how to create objects of type BulletShape.
+ */
+void BulletConeShape::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void BulletConeShape::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
+  dg.add_stdfloat(get_margin());
+
+  // parameters to serialize: radius, height, upIndex
+  dg.add_stdfloat(_radius);
+  dg.add_stdfloat(_height);
+  dg.add_int8((int8_t)_shape->getConeUpIndex());
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type BulletShape is encountered in the Bam file.  It should create the
+ * BulletShape and extract its information from the file.
+ */
+TypedWritable *BulletConeShape::
+make_from_bam(const FactoryParams &params) {
+  // create a default BulletConeShape
+  BulletConeShape *param = new BulletConeShape;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new BulletShape.
+ */
+void BulletConeShape::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  BulletShape::fillin(scan, manager);
+  nassertv(_shape == nullptr);
+
+  PN_stdfloat margin = scan.get_stdfloat();
+
+  // parameters to serialize: radius, height, up
+  _radius = scan.get_stdfloat();
+  _height = scan.get_stdfloat();
+
+  int up_index = (int) scan.get_int8();
+  switch (up_index) {
+  case 0:
+    _shape = new btConeShapeX((btScalar)_radius, (btScalar)_height);
+    break;
+  case 1:
+    _shape = new btConeShape((btScalar)_radius, (btScalar)_height);
+    break;
+  case 2:
+    _shape = new btConeShapeZ((btScalar)_radius, (btScalar)_height);
+    break;
+  default:
+    bullet_cat.error() << "invalid up-axis:" << up_index << endl;
+    break;
+  }
+
+  _shape->setUserPointer(this);
+  _shape->setMargin(margin);
+}

+ 14 - 1
panda/src/bullet/bulletConeShape.h

@@ -24,9 +24,12 @@
  *
  */
 class EXPCL_PANDABULLET BulletConeShape : public BulletShape {
+private:
+  // Only used by make_from_bam
+  INLINE BulletConeShape();
 
 PUBLISHED:
-  BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up);
+  explicit BulletConeShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up);
   INLINE BulletConeShape(const BulletConeShape &copy);
   INLINE void operator = (const BulletConeShape &copy);
   INLINE ~BulletConeShape();
@@ -42,6 +45,16 @@ public:
 
 private:
   btConeShape *_shape;
+  PN_stdfloat _radius;
+  PN_stdfloat _height;
+
+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() {

+ 6 - 6
panda/src/bullet/bulletConeTwistConstraint.h

@@ -30,12 +30,12 @@ class BulletRigidBodyNode;
 class EXPCL_PANDABULLET BulletConeTwistConstraint : public BulletConstraint {
 
 PUBLISHED:
-  BulletConeTwistConstraint(const BulletRigidBodyNode *node_a,
-                            const TransformState *frame_a);
-  BulletConeTwistConstraint(const BulletRigidBodyNode *node_a,
-                            const BulletRigidBodyNode *node_b,
-                            const TransformState *frame_a,
-                            const TransformState *frame_b);
+  explicit BulletConeTwistConstraint(const BulletRigidBodyNode *node_a,
+                                     const TransformState *frame_a);
+  explicit BulletConeTwistConstraint(const BulletRigidBodyNode *node_a,
+                                     const BulletRigidBodyNode *node_b,
+                                     const TransformState *frame_a,
+                                     const TransformState *frame_b);
   INLINE ~BulletConeTwistConstraint();
 
   void set_limit(int index, PN_stdfloat value);

+ 5 - 1
panda/src/bullet/bulletConvexHullShape.cxx

@@ -125,6 +125,7 @@ register_with_read_factory() {
  */
 void BulletConvexHullShape::
 write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
   dg.add_stdfloat(get_margin());
 
   unsigned int num_points = _shape->getNumPoints();
@@ -161,7 +162,10 @@ make_from_bam(const FactoryParams &params) {
  */
 void BulletConvexHullShape::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  PN_stdfloat margin = scan.get_stdfloat();
+  BulletShape::fillin(scan, manager);
+  nassertv(_shape == nullptr);
+
+  _shape->setMargin(scan.get_stdfloat());
   unsigned int num_points = scan.get_uint32();
 
 #if BT_BULLET_VERSION >= 282

+ 11 - 0
panda/src/bullet/bulletConvexPointCloudShape.I

@@ -11,6 +11,15 @@
  * @date 2010-01-30
  */
 
+/**
+ * Only used by make_from_bam.
+ */
+INLINE BulletConvexPointCloudShape::
+BulletConvexPointCloudShape() :
+  _scale(1),
+  _shape(nullptr) {
+}
+
 /**
  *
  */
@@ -25,6 +34,7 @@ INLINE BulletConvexPointCloudShape::
  */
 INLINE BulletConvexPointCloudShape::
 BulletConvexPointCloudShape(const BulletConvexPointCloudShape &copy) :
+  _scale(copy._scale),
   _shape(copy._shape) {
 }
 
@@ -33,6 +43,7 @@ BulletConvexPointCloudShape(const BulletConvexPointCloudShape &copy) :
  */
 INLINE void BulletConvexPointCloudShape::
 operator = (const BulletConvexPointCloudShape &copy) {
+  _scale = copy._scale;
   _shape = copy._shape;
 }
 

+ 71 - 1
panda/src/bullet/bulletConvexPointCloudShape.cxx

@@ -21,7 +21,8 @@ TypeHandle BulletConvexPointCloudShape::_type_handle;
  *
  */
 BulletConvexPointCloudShape::
-BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale) {
+BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale) :
+  _scale(scale) {
 
   btVector3 btScale = LVecBase3_to_btVector3(scale);
 
@@ -56,6 +57,7 @@ BulletConvexPointCloudShape::
 BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale) {
 
   btVector3 btScale = LVecBase3_to_btVector3(scale);
+  _scale = scale;
 
   // Collect points
   pvector<LPoint3> points;
@@ -81,3 +83,71 @@ BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale) {
   _shape = new btConvexPointCloudShape(btPoints, points.size(), btScale);
   _shape->setUserPointer(this);
 }
+
+/**
+ * Tells the BamReader how to create objects of type BulletShape.
+ */
+void BulletConvexPointCloudShape::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void BulletConvexPointCloudShape::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
+
+  // parameters to serialize: num points, points, scale
+  _scale.write_datagram(dg);
+
+  dg.add_int32(get_num_points());
+  for (int i = 0; i < get_num_points(); ++i){
+    btVector3_to_LVector3(_shape->getUnscaledPoints()[i]).write_datagram(dg);
+  }
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type BulletShape is encountered in the Bam file.  It should create the
+ * BulletShape and extract its information from the file.
+ */
+TypedWritable *BulletConvexPointCloudShape::
+make_from_bam(const FactoryParams &params) {
+  // create a default BulletConvexPointCloudShape
+  BulletConvexPointCloudShape *param = new BulletConvexPointCloudShape;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new BulletShape.
+ */
+void BulletConvexPointCloudShape::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  BulletShape::fillin(scan, manager);
+
+  // parameters to serialize: num points, points, scale
+  _scale.read_datagram(scan);
+
+  unsigned int num_points = scan.get_uint32();
+
+  btVector3 *btPoints = new btVector3[num_points];
+  for (unsigned int i = 0; i < num_points; ++i) {
+    LPoint3 point;
+    point.read_datagram(scan);
+    btPoints[i] = LVecBase3_to_btVector3(point);
+  }
+
+  // Create shape
+  _shape = new btConvexPointCloudShape(btPoints, num_points, LVecBase3_to_btVector3(_scale));
+  _shape->setUserPointer(this);
+}

+ 14 - 2
panda/src/bullet/bulletConvexPointCloudShape.h

@@ -26,10 +26,13 @@
  *
  */
 class EXPCL_PANDABULLET BulletConvexPointCloudShape : public BulletShape {
+private:
+  // Only used by make_from_bam
+  INLINE BulletConvexPointCloudShape();
 
 PUBLISHED:
-  BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale=LVecBase3(1.));
-  BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale=LVecBase3(1.));
+  explicit BulletConvexPointCloudShape(const PTA_LVecBase3 &points, LVecBase3 scale=LVecBase3(1.));
+  explicit BulletConvexPointCloudShape(const Geom *geom, LVecBase3 scale=LVecBase3(1.));
   INLINE BulletConvexPointCloudShape(const BulletConvexPointCloudShape &copy);
   INLINE void operator = (const BulletConvexPointCloudShape &copy);
   INLINE ~BulletConvexPointCloudShape();
@@ -43,6 +46,15 @@ public:
 
 private:
   btConvexPointCloudShape *_shape;
+  LVecBase3 _scale;
+
+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() {

+ 11 - 1
panda/src/bullet/bulletCylinderShape.I

@@ -11,6 +11,15 @@
  * @date 2010-02-17
  */
 
+/**
+ * Only used by make_from_bam.
+ */
+INLINE BulletCylinderShape::
+BulletCylinderShape() :
+  _half_extents(LVector3::zero()),
+  _shape(nullptr) {
+}
+
 /**
  *
  */
@@ -25,7 +34,7 @@ INLINE BulletCylinderShape::
  */
 INLINE BulletCylinderShape::
 BulletCylinderShape(const BulletCylinderShape &copy) :
-  _shape(copy._shape) {
+  _shape(copy._shape), _half_extents(copy._half_extents) {
 }
 
 /**
@@ -34,6 +43,7 @@ BulletCylinderShape(const BulletCylinderShape &copy) :
 INLINE void BulletCylinderShape::
 operator = (const BulletCylinderShape &copy) {
   _shape = copy._shape;
+  _half_extents = copy._half_extents;
 }
 
 /**

+ 81 - 1
panda/src/bullet/bulletCylinderShape.cxx

@@ -19,7 +19,8 @@ TypeHandle BulletCylinderShape::_type_handle;
  *
  */
 BulletCylinderShape::
-BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up) {
+BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up) :
+  _half_extents(half_extents){
 
   btVector3 btHalfExtents = LVecBase3_to_btVector3(half_extents);
 
@@ -50,12 +51,15 @@ BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up) {
   switch (up) {
   case X_up:
     _shape = new btCylinderShapeX(btVector3(0.5 * height, radius, radius));
+    _half_extents = btVector3_to_LVector3(btVector3(0.5 * height, radius, radius));
     break;
   case Y_up:
     _shape = new btCylinderShape(btVector3(radius, 0.5 * height, radius));
+    _half_extents = btVector3_to_LVector3(btVector3(radius, 0.5 * height, radius));
     break;
   case Z_up:
     _shape = new btCylinderShapeZ(btVector3(radius, radius, 0.5 * height));
+    _half_extents = btVector3_to_LVector3(btVector3(radius, radius, 0.5 * height));
     break;
   default:
     bullet_cat.error() << "invalid up-axis:" << up << endl;
@@ -73,3 +77,79 @@ ptr() const {
 
   return _shape;
 }
+
+/**
+ * Tells the BamReader how to create objects of type BulletShape.
+ */
+void BulletCylinderShape::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void BulletCylinderShape::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
+  dg.add_stdfloat(get_margin());
+
+  // parameters to serialize: radius, height, up
+  _half_extents.write_datagram(dg);
+  dg.add_int8((int8_t)_shape->getUpAxis());
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type BulletShape is encountered in the Bam file.  It should create the
+ * BulletShape and extract its information from the file.
+ */
+TypedWritable *BulletCylinderShape::
+make_from_bam(const FactoryParams &params) {
+  // create a default BulletCylinderShape
+  BulletCylinderShape *param = new BulletCylinderShape;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new BulletShape.
+ */
+void BulletCylinderShape::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  BulletShape::fillin(scan, manager);
+  nassertv(_shape == nullptr);
+
+  PN_stdfloat margin = scan.get_stdfloat();
+
+  // parameters to serialize: radius, height, up
+  _half_extents.read_datagram(scan);
+  int up = (int) scan.get_int8();
+
+  btVector3 btHalfExtents = LVecBase3_to_btVector3(_half_extents);
+
+  switch (up) {
+  case X_up:
+    _shape = new btCylinderShapeX(btHalfExtents);
+    break;
+  case Y_up:
+    _shape = new btCylinderShape(btHalfExtents);
+    break;
+  case Z_up:
+    _shape = new btCylinderShapeZ(btHalfExtents);
+    break;
+  default:
+    bullet_cat.error() << "invalid up-axis:" << up << endl;
+    break;
+  }
+
+  _shape->setUserPointer(this);
+  _shape->setMargin(margin);
+}

+ 14 - 2
panda/src/bullet/bulletCylinderShape.h

@@ -24,10 +24,13 @@
  *
  */
 class EXPCL_PANDABULLET BulletCylinderShape : public BulletShape {
+private:
+  // Only used by make_from_bam
+  INLINE BulletCylinderShape();
 
 PUBLISHED:
-  BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up);
-  BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up=Z_up);
+  explicit BulletCylinderShape(PN_stdfloat radius, PN_stdfloat height, BulletUpAxis up=Z_up);
+  explicit BulletCylinderShape(const LVector3 &half_extents, BulletUpAxis up=Z_up);
   INLINE BulletCylinderShape(const BulletCylinderShape &copy);
   INLINE void operator = (const BulletCylinderShape &copy);
   INLINE ~BulletCylinderShape();
@@ -44,8 +47,17 @@ public:
   virtual btCollisionShape *ptr() const;
 
 private:
+  LVector3 _half_extents;
   btCylinderShape *_shape;
 
+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;

+ 1 - 2
panda/src/bullet/bulletDebugNode.h

@@ -23,9 +23,8 @@
  *
  */
 class EXPCL_PANDABULLET BulletDebugNode : public PandaNode {
-
 PUBLISHED:
-  BulletDebugNode(const char *name="debug");
+  explicit BulletDebugNode(const char *name="debug");
   INLINE ~BulletDebugNode();
 
   virtual void draw_mask_changed();

+ 8 - 9
panda/src/bullet/bulletGenericConstraint.h

@@ -31,16 +31,15 @@ class BulletRigidBodyNode;
  *
  */
 class EXPCL_PANDABULLET BulletGenericConstraint : public BulletConstraint {
-
 PUBLISHED:
-  BulletGenericConstraint(const BulletRigidBodyNode *node_a,
-                          const TransformState *frame_a,
-                          bool use_frame_a);
-  BulletGenericConstraint(const BulletRigidBodyNode *node_a,
-                          const BulletRigidBodyNode *node_b,
-                          const TransformState *frame_a,
-                          const TransformState *frame_b,
-                          bool use_frame_a);
+  explicit BulletGenericConstraint(const BulletRigidBodyNode *node_a,
+                                   const TransformState *frame_a,
+                                   bool use_frame_a);
+  explicit BulletGenericConstraint(const BulletRigidBodyNode *node_a,
+                                   const BulletRigidBodyNode *node_b,
+                                   const TransformState *frame_a,
+                                   const TransformState *frame_b,
+                                   bool use_frame_a);
   INLINE ~BulletGenericConstraint();
 
   // Geometry

+ 1 - 2
panda/src/bullet/bulletGhostNode.h

@@ -29,9 +29,8 @@ class BulletShape;
  *
  */
 class EXPCL_PANDABULLET BulletGhostNode : public BulletBodyNode {
-
 PUBLISHED:
-  BulletGhostNode(const char *name="ghost");
+  explicit BulletGhostNode(const char *name="ghost");
   INLINE ~BulletGhostNode();
 
   // Overlapping

+ 22 - 7
panda/src/bullet/bulletHeightfieldShape.I

@@ -11,12 +11,24 @@
  * @date 2010-02-05
  */
 
+/**
+ * Only used by make_from_bam
+ */
+INLINE BulletHeightfieldShape::
+BulletHeightfieldShape() :
+  _num_rows(0),
+  _num_cols(0),
+  _data(nullptr),
+  _shape(nullptr),
+  _max_height(0.0),
+  _up(Z_up) {
+}
+
 /**
  *
  */
 INLINE BulletHeightfieldShape::
 ~BulletHeightfieldShape() {
-
   delete _shape;
   delete [] _data;
 }
@@ -28,10 +40,13 @@ INLINE BulletHeightfieldShape::
 BulletHeightfieldShape(const BulletHeightfieldShape &copy) :
   _shape(copy._shape),
   _num_rows(copy._num_rows),
-  _num_cols(copy._num_cols) {
+  _num_cols(copy._num_cols),
+  _max_height(copy._max_height),
+  _up(copy._up) {
 
-  _data = new btScalar[_num_rows * _num_cols];
-  memcpy(_data, copy._data, _num_rows * _num_cols * sizeof(btScalar));
+  size_t size = (size_t)_num_rows * (size_t)_num_cols;
+  _data = new btScalar[size];
+  memcpy(_data, copy._data, size * sizeof(btScalar));
 }
 
 /**
@@ -39,11 +54,11 @@ BulletHeightfieldShape(const BulletHeightfieldShape &copy) :
  */
 INLINE void BulletHeightfieldShape::
 operator = (const BulletHeightfieldShape &copy) {
-
   _shape = copy._shape;
   _num_rows = copy._num_rows;
   _num_cols = copy._num_cols;
 
-  _data = new btScalar[_num_rows * _num_cols];
-  memcpy(_data, copy._data, _num_rows * _num_cols * sizeof(btScalar));
+  size_t size = (size_t)_num_rows * (size_t)_num_cols;
+  _data = new btScalar[size];
+  memcpy(_data, copy._data, size * sizeof(btScalar));
 }

+ 85 - 2
panda/src/bullet/bulletHeightfieldShape.cxx

@@ -21,7 +21,8 @@ TypeHandle BulletHeightfieldShape::_type_handle;
  *   while rotating it 90 degrees to the right.
  */
 BulletHeightfieldShape::
-BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up) {
+BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up) :
+  _max_height(max_height), _up(up) {
 
   _num_rows = image.get_x_size();
   _num_cols = image.get_y_size();
@@ -71,7 +72,8 @@ set_use_diamond_subdivision(bool flag) {
  *   that are non-power-of-two and/or rectangular.
  */
 BulletHeightfieldShape::
-BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) {
+BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) :
+  _max_height(max_height), _up(up) {
 
   _num_rows = tex->get_x_size() + 1;
   _num_cols = tex->get_y_size() + 1;
@@ -101,3 +103,84 @@ BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up) {
                                          true, false);
   _shape->setUserPointer(this);
 }
+
+/**
+ * Tells the BamReader how to create objects of type BulletShape.
+ */
+void BulletHeightfieldShape::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void BulletHeightfieldShape::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
+  dg.add_stdfloat(get_margin());
+
+  // parameters to serialize:_num_rows,_num_cols,_data,max_height,up,
+  dg.add_int8((int8_t)_up);
+  dg.add_stdfloat(_max_height);
+  dg.add_int32(_num_rows);
+  dg.add_int32(_num_cols);
+
+  size_t size = (size_t)_num_rows * (size_t)_num_cols;
+  for (size_t i = 0; i < size; ++i) {
+    dg.add_stdfloat(_data[i]);
+  }
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type BulletShape is encountered in the Bam file.  It should create the
+ * BulletShape and extract its information from the file.
+ */
+TypedWritable *BulletHeightfieldShape::
+make_from_bam(const FactoryParams &params) {
+  // create a default BulletHeightfieldShape
+  BulletHeightfieldShape *param = new BulletHeightfieldShape;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new BulletShape.
+ */
+void BulletHeightfieldShape::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  BulletShape::fillin(scan, manager);
+  nassertv(_shape == nullptr);
+
+  PN_stdfloat margin = scan.get_stdfloat();
+
+  // parameters to serialize: radius, height, up
+  _up = (BulletUpAxis) scan.get_int8();
+  _max_height = scan.get_stdfloat();
+  _num_rows = scan.get_int32();
+  _num_cols = scan.get_int32();
+
+  size_t size = (size_t)_num_rows * (size_t)_num_cols;
+  delete[] _data;
+  _data = new float[size];
+  for (size_t i = 0; i < size; ++i) {
+    _data[i]  = scan.get_stdfloat();
+  }
+
+  _shape = new btHeightfieldTerrainShape(_num_rows,
+                                         _num_cols,
+                                         _data,
+                                         _max_height,
+                                         _up,
+                                         true, false);
+  _shape->setUserPointer(this);
+  _shape->setMargin(margin);
+}

+ 14 - 2
panda/src/bullet/bulletHeightfieldShape.h

@@ -28,10 +28,12 @@
  *
  */
 class EXPCL_PANDABULLET BulletHeightfieldShape : public BulletShape {
+private:
+  INLINE BulletHeightfieldShape();
 
 PUBLISHED:
-  BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up=Z_up);
-  BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up=Z_up);
+  explicit BulletHeightfieldShape(const PNMImage &image, PN_stdfloat max_height, BulletUpAxis up=Z_up);
+  explicit BulletHeightfieldShape(Texture *tex, PN_stdfloat max_height, BulletUpAxis up=Z_up);
   INLINE BulletHeightfieldShape(const BulletHeightfieldShape &copy);
   INLINE void operator = (const BulletHeightfieldShape &copy);
   INLINE ~BulletHeightfieldShape();
@@ -46,6 +48,16 @@ private:
   int _num_cols;
   btScalar *_data;
   btHeightfieldTerrainShape *_shape;
+  PN_stdfloat _max_height;
+  BulletUpAxis _up;
+
+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() {

+ 20 - 21
panda/src/bullet/bulletHingeConstraint.h

@@ -29,28 +29,27 @@ class BulletRigidBodyNode;
  * adhering to specified limits.  It's motor can apply angular force to them.
  */
 class EXPCL_PANDABULLET BulletHingeConstraint : public BulletConstraint {
-
 PUBLISHED:
-  BulletHingeConstraint(const BulletRigidBodyNode *node_a,
-                        const LPoint3 &pivot_a,
-                        const LVector3 &axis_a,
-                        bool use_frame_a=false);
-  BulletHingeConstraint(const BulletRigidBodyNode *node_a,
-                        const BulletRigidBodyNode *node_b,
-                        const LPoint3 &pivot_a,
-                        const LPoint3 &pivot_b,
-                        const LVector3 &axis_a,
-                        const LVector3 &axis_b,
-                        bool use_frame_a=false);
-
-  BulletHingeConstraint(const BulletRigidBodyNode *node_a,
-                        const TransformState *ts_a,
-                        bool use_frame_a=false);
-  BulletHingeConstraint(const BulletRigidBodyNode *node_a,
-                        const BulletRigidBodyNode *node_b,
-                        const TransformState *ts_a,
-                        const TransformState *ts_b,
-                        bool use_frame_a=false);
+  explicit BulletHingeConstraint(const BulletRigidBodyNode *node_a,
+                                 const LPoint3 &pivot_a,
+                                 const LVector3 &axis_a,
+                                 bool use_frame_a=false);
+  explicit BulletHingeConstraint(const BulletRigidBodyNode *node_a,
+                                 const BulletRigidBodyNode *node_b,
+                                 const LPoint3 &pivot_a,
+                                 const LPoint3 &pivot_b,
+                                 const LVector3 &axis_a,
+                                 const LVector3 &axis_b,
+                                 bool use_frame_a=false);
+
+  explicit BulletHingeConstraint(const BulletRigidBodyNode *node_a,
+                                 const TransformState *ts_a,
+                                 bool use_frame_a=false);
+  explicit BulletHingeConstraint(const BulletRigidBodyNode *node_a,
+                                 const BulletRigidBodyNode *node_b,
+                                 const TransformState *ts_a,
+                                 const TransformState *ts_b,
+                                 bool use_frame_a=false);
 
   INLINE ~BulletHingeConstraint();
 

+ 10 - 9
panda/src/bullet/bulletMinkowskiSumShape.I

@@ -11,6 +11,16 @@
  * @date 2010-01-23
  */
 
+/**
+ * Only used by make_from_bam.
+ */
+INLINE BulletMinkowskiSumShape::
+BulletMinkowskiSumShape() :
+  _shape(nullptr),
+  _shape_a(nullptr),
+  _shape_b(nullptr) {
+}
+
 /**
  *
  */
@@ -95,12 +105,3 @@ get_shape_b() const {
 
   return _shape_b;
 }
-
-/**
- *
- */
-INLINE PN_stdfloat BulletMinkowskiSumShape::
-get_margin() const {
-
-  return (PN_stdfloat)_shape->getMargin();
-}

+ 102 - 4
panda/src/bullet/bulletMinkowskiSumShape.cxx

@@ -19,7 +19,9 @@ TypeHandle BulletMinkowskiSumShape::_type_handle;
  *
  */
 BulletMinkowskiSumShape::
-BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b) {
+BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b) :
+  _shape_a(shape_a),
+  _shape_b(shape_b) {
 
   nassertv(shape_a->is_convex());
   nassertv(shape_b->is_convex());
@@ -29,9 +31,6 @@ BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b)
 
   _shape = new btMinkowskiSumShape(ptr_a, ptr_b);
   _shape->setUserPointer(this);
-
-  _shape_a = shape_a;
-  _shape_b = shape_b;
 }
 
 /**
@@ -42,3 +41,102 @@ ptr() const {
 
   return _shape;
 }
+
+/**
+ * Tells the BamReader how to create objects of type BulletShape.
+ */
+void BulletMinkowskiSumShape::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void BulletMinkowskiSumShape::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
+  dg.add_stdfloat(get_margin());
+
+  // parameters to serialize: _shape_a, _shape_b, _transform_a, _transform_b
+  manager->write_pointer(dg, _shape_a);
+  manager->write_pointer(dg, _shape_b);
+  manager->write_pointer(dg, get_transform_a());
+  manager->write_pointer(dg, get_transform_b());
+}
+
+/**
+ * Receives an array of pointers, one for each time manager->read_pointer()
+ * was called in fillin(). Returns the number of pointers processed.
+ */
+int BulletMinkowskiSumShape::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = BulletShape::complete_pointers(p_list, manager);
+
+  _shape_a = DCAST(BulletShape, p_list[pi++]);
+  _shape_b = DCAST(BulletShape, p_list[pi++]);
+
+  const TransformState *transform_a = DCAST(TransformState, p_list[pi++]);
+  const TransformState *transform_b = DCAST(TransformState, p_list[pi++]);
+
+  const btConvexShape *ptr_a = (const btConvexShape *)_shape_a->ptr();
+  const btConvexShape *ptr_b = (const btConvexShape *)_shape_b->ptr();
+
+  _shape = new btMinkowskiSumShape(ptr_a, ptr_b);
+  _shape->setUserPointer(this);
+  _shape->setMargin(_margin);
+
+  set_transform_a(transform_a);
+  set_transform_b(transform_b);
+
+  return pi;
+}
+
+/**
+ * Some objects require all of their nested pointers to have been completed
+ * before the objects themselves can be completed.  If this is the case,
+ * override this method to return true, and be careful with circular
+ * references (which would make the object unreadable from a bam file).
+ */
+bool BulletMinkowskiSumShape::
+require_fully_complete() const {
+  // We require the shape pointers to be complete before we add them.
+  return true;
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type BulletShape is encountered in the Bam file.  It should create the
+ * BulletShape and extract its information from the file.
+ */
+TypedWritable *BulletMinkowskiSumShape::
+make_from_bam(const FactoryParams &params) {
+  // create a default BulletMinkowskiSumShape
+  BulletMinkowskiSumShape *param = new BulletMinkowskiSumShape;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new BulletShape.
+ */
+void BulletMinkowskiSumShape::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  BulletShape::fillin(scan, manager);
+  nassertv(_shape == nullptr);
+
+  _margin = scan.get_stdfloat();
+
+  // parameters to serialize: _shape_a, _shape_b, _transform_a, _transform_b
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
+}

+ 18 - 4
panda/src/bullet/bulletMinkowskiSumShape.h

@@ -26,9 +26,12 @@
  *
  */
 class EXPCL_PANDABULLET BulletMinkowskiSumShape : public BulletShape {
+private:
+  // Only used by make_from_bam
+  INLINE BulletMinkowskiSumShape();
 
 PUBLISHED:
-  BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b);
+  explicit BulletMinkowskiSumShape(const BulletShape *shape_a, const BulletShape *shape_b);
   INLINE BulletMinkowskiSumShape(const BulletMinkowskiSumShape &copy);
   INLINE void operator = (const BulletMinkowskiSumShape &copy);
   INLINE ~BulletMinkowskiSumShape();
@@ -41,13 +44,10 @@ PUBLISHED:
   INLINE const BulletShape *get_shape_a() const;
   INLINE const BulletShape *get_shape_b() const;
 
-  INLINE PN_stdfloat get_margin() const;
-
   MAKE_PROPERTY(transform_a, get_transform_a, set_transform_a);
   MAKE_PROPERTY(transform_b, get_transform_b, set_transform_b);
   MAKE_PROPERTY(shape_a, get_shape_a);
   MAKE_PROPERTY(shape_b, get_shape_b);
-  MAKE_PROPERTY(margin, get_margin);
 
 public:
   virtual btCollisionShape *ptr() const;
@@ -58,6 +58,20 @@ private:
   CPT(BulletShape) _shape_a;
   CPT(BulletShape) _shape_b;
 
+  // This is stored temporarily during read.
+  PN_stdfloat _margin;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+  virtual int complete_pointers(TypedWritable **plist,
+                                BamReader *manager);
+  virtual bool require_fully_complete() const;
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;

+ 76 - 0
panda/src/bullet/bulletMultiSphereShape.cxx

@@ -50,3 +50,79 @@ ptr() const {
 
   return _shape;
 }
+
+/**
+ * Tells the BamReader how to create objects of type BulletShape.
+ */
+void BulletMultiSphereShape::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void BulletMultiSphereShape::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
+  dg.add_stdfloat(get_margin());
+
+  // parameters to serialize: sphere count, points, radii
+  dg.add_int32(get_sphere_count());
+  for (int i = 0; i < get_sphere_count(); ++i){
+    get_sphere_pos(i).write_datagram(dg);
+  }
+
+  for (int i = 0; i < get_sphere_count(); ++i){
+    dg.add_stdfloat(get_sphere_radius(i));
+  }
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type BulletShape is encountered in the Bam file.  It should create the
+ * BulletShape and extract its information from the file.
+ */
+TypedWritable *BulletMultiSphereShape::
+make_from_bam(const FactoryParams &params) {
+  // create a default BulletMultiSphereShape
+  BulletMultiSphereShape *param = new BulletMultiSphereShape;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new BulletShape.
+ */
+void BulletMultiSphereShape::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  BulletShape::fillin(scan, manager);
+  nassertv(_shape == nullptr);
+
+  PN_stdfloat margin = scan.get_stdfloat();
+
+  // parameters to serialize: sphere count, points, radii
+  int sphereCount = scan.get_int32();
+  btVector3 *positions = new btVector3[sphereCount];
+  for (int i = 0; i < sphereCount; ++i){
+    LVector3 pos;
+    pos.read_datagram(scan);
+    positions[i] = LVecBase3_to_btVector3(pos);
+  }
+
+  btScalar *radii = new btScalar[sphereCount];
+  for (int i = 0; i < sphereCount; ++i){
+    radii[i] = scan.get_stdfloat();
+  }
+
+  _shape = new btMultiSphereShape(positions, radii, sphereCount);
+  _shape->setUserPointer(this);
+  _shape->setMargin(margin);
+}

+ 11 - 1
panda/src/bullet/bulletMultiSphereShape.h

@@ -26,9 +26,11 @@
  *
  */
 class EXPCL_PANDABULLET BulletMultiSphereShape : public BulletShape {
+private:
+  BulletMultiSphereShape() : _shape(nullptr) {}
 
 PUBLISHED:
-  BulletMultiSphereShape(const PTA_LVecBase3 &points, const PTA_stdfloat &radii);
+  explicit BulletMultiSphereShape(const PTA_LVecBase3 &points, const PTA_stdfloat &radii);
   INLINE BulletMultiSphereShape(const BulletMultiSphereShape &copy);
   INLINE void operator = (const BulletMultiSphereShape &copy);
   INLINE ~BulletMultiSphereShape();
@@ -47,6 +49,14 @@ public:
 private:
   btMultiSphereShape *_shape;
 
+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;

+ 4 - 1
panda/src/bullet/bulletPlaneShape.cxx

@@ -62,6 +62,8 @@ register_with_read_factory() {
  */
 void BulletPlaneShape::
 write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
+
   dg.add_stdfloat(get_margin());
   get_plane_normal().write_datagram(dg);
   dg.add_stdfloat(get_plane_constant());
@@ -90,7 +92,8 @@ make_from_bam(const FactoryParams &params) {
  */
 void BulletPlaneShape::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  nassertv(_shape == NULL);
+  BulletShape::fillin(scan, manager);
+  nassertv(_shape == nullptr);
 
   PN_stdfloat margin = scan.get_stdfloat();
 

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

@@ -32,7 +32,7 @@ private:
   INLINE BulletPlaneShape() : _shape(NULL) {};
 
 PUBLISHED:
-  BulletPlaneShape(const LVector3 &normal, PN_stdfloat constant);
+  explicit BulletPlaneShape(const LVector3 &normal, PN_stdfloat constant);
   INLINE BulletPlaneShape(const BulletPlaneShape &copy);
   INLINE void operator = (const BulletPlaneShape &copy);
   INLINE ~BulletPlaneShape();

+ 3 - 0
panda/src/bullet/bulletRigidBodyNode.cxx

@@ -612,6 +612,9 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   get_gravity().write_datagram(dg);
   get_linear_factor().write_datagram(dg);
   get_angular_factor().write_datagram(dg);
+  // dynamic state (?)
+  get_linear_velocity().write_datagram(dg);
+  get_angular_velocity().write_datagram(dg);
 }
 
 /**

+ 8 - 9
panda/src/bullet/bulletSliderConstraint.h

@@ -28,16 +28,15 @@ class BulletRigidBodyNode;
  *
  */
 class EXPCL_PANDABULLET BulletSliderConstraint : public BulletConstraint {
-
 PUBLISHED:
-  BulletSliderConstraint(const BulletRigidBodyNode *node_a,
-                         const TransformState *frame_a,
-                         bool useFrame_a);
-  BulletSliderConstraint(const BulletRigidBodyNode *node_a,
-                         const BulletRigidBodyNode *node_b,
-                         const TransformState *frame_a,
-                         const TransformState *frame_b,
-                         bool use_frame_a);
+  explicit BulletSliderConstraint(const BulletRigidBodyNode *node_a,
+                                  const TransformState *frame_a,
+                                  bool useFrame_a);
+  explicit BulletSliderConstraint(const BulletRigidBodyNode *node_a,
+                                  const BulletRigidBodyNode *node_b,
+                                  const TransformState *frame_a,
+                                  const TransformState *frame_b,
+                                  bool use_frame_a);
   INLINE ~BulletSliderConstraint();
 
   PN_stdfloat get_linear_pos() const;

+ 5 - 4
panda/src/bullet/bulletSphereShape.I

@@ -25,7 +25,8 @@ INLINE BulletSphereShape::
  */
 INLINE BulletSphereShape::
 BulletSphereShape(const BulletSphereShape &copy) :
-  _shape(copy._shape) {
+  _shape(copy._shape),
+  _radius(copy._radius) {
 }
 
 /**
@@ -34,13 +35,13 @@ BulletSphereShape(const BulletSphereShape &copy) :
 INLINE void BulletSphereShape::
 operator = (const BulletSphereShape &copy) {
   _shape = copy._shape;
+  _radius = copy._radius;
 }
 
 /**
- *
+ * Returns the radius that was used to construct this sphere.
  */
 INLINE PN_stdfloat BulletSphereShape::
 get_radius() const {
-
-  return _shape->getRadius();
+  return _radius;
 }

+ 8 - 4
panda/src/bullet/bulletSphereShape.cxx

@@ -19,7 +19,7 @@ TypeHandle BulletSphereShape::_type_handle;
  *
  */
 BulletSphereShape::
-BulletSphereShape(PN_stdfloat radius) {
+BulletSphereShape(PN_stdfloat radius) : _radius(radius) {
 
   _shape = new btSphereShape(radius);
   _shape->setUserPointer(this);
@@ -57,8 +57,10 @@ register_with_read_factory() {
  */
 void BulletSphereShape::
 write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
+
   dg.add_stdfloat(get_margin());
-  dg.add_stdfloat(get_radius());
+  dg.add_stdfloat(_radius);
 }
 
 /**
@@ -84,11 +86,13 @@ make_from_bam(const FactoryParams &params) {
  */
 void BulletSphereShape::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  nassertv(_shape == NULL);
+  BulletShape::fillin(scan, manager);
+  nassertv(_shape == nullptr);
 
   PN_stdfloat margin = scan.get_stdfloat();
+  _radius = scan.get_stdfloat();
 
-  _shape = new btSphereShape(scan.get_stdfloat());
+  _shape = new btSphereShape(_radius);
   _shape->setUserPointer(this);
   _shape->setMargin(margin);
 }

+ 2 - 1
panda/src/bullet/bulletSphereShape.h

@@ -31,7 +31,7 @@ private:
   INLINE BulletSphereShape() : _shape(NULL) {};
 
 PUBLISHED:
-  BulletSphereShape(PN_stdfloat radius);
+  explicit BulletSphereShape(PN_stdfloat radius);
   INLINE BulletSphereShape(const BulletSphereShape &copy);
   INLINE void operator = (const BulletSphereShape &copy);
   INLINE ~BulletSphereShape();
@@ -47,6 +47,7 @@ public:
 
 private:
   btSphereShape *_shape;
+  PN_stdfloat _radius;
 
 public:
   static void register_with_read_factory();

+ 6 - 7
panda/src/bullet/bulletSphericalConstraint.h

@@ -32,14 +32,13 @@ class BulletRigidBodyNode;
  * socket" joint.
  */
 class EXPCL_PANDABULLET BulletSphericalConstraint : public BulletConstraint {
-
 PUBLISHED:
-  BulletSphericalConstraint(const BulletRigidBodyNode *node_a,
-                            const LPoint3 &pivot_a);
-  BulletSphericalConstraint(const BulletRigidBodyNode *node_a,
-                            const BulletRigidBodyNode *node_b,
-                            const LPoint3 &pivot_a,
-                            const LPoint3 &pivot_b);
+  explicit BulletSphericalConstraint(const BulletRigidBodyNode *node_a,
+                                     const LPoint3 &pivot_a);
+  explicit BulletSphericalConstraint(const BulletRigidBodyNode *node_a,
+                                     const BulletRigidBodyNode *node_b,
+                                     const LPoint3 &pivot_a,
+                                     const LPoint3 &pivot_b);
   INLINE ~BulletSphericalConstraint();
 
   // Pivots

+ 7 - 1
panda/src/bullet/bulletTriangleMeshShape.cxx

@@ -124,6 +124,8 @@ register_with_read_factory() {
  */
 void BulletTriangleMeshShape::
 write_datagram(BamWriter *manager, Datagram &dg) {
+  BulletShape::write_datagram(manager, dg);
+
   dg.add_stdfloat(get_margin());
 
   manager->write_pointer(dg, _mesh);
@@ -152,9 +154,11 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
     _gimpact_shape = new btGImpactMeshShape(mesh_ptr);
     _gimpact_shape->updateBound();
     _gimpact_shape->setUserPointer(this);
+    _gimpact_shape->setMargin(_margin);
   } else {
     _bvh_shape = new btBvhTriangleMeshShape(mesh_ptr, _compress, _bvh);
     _bvh_shape->setUserPointer(this);
+    _bvh_shape->setMargin(_margin);
   }
 
   return pi;
@@ -183,7 +187,9 @@ make_from_bam(const FactoryParams &params) {
  */
 void BulletTriangleMeshShape::
 fillin(DatagramIterator &scan, BamReader *manager) {
-  PN_stdfloat margin = scan.get_stdfloat();
+  BulletShape::fillin(scan, manager);
+
+  _margin = scan.get_stdfloat();
 
   manager->read_pointer(scan);
 

+ 4 - 1
panda/src/bullet/bulletTriangleMeshShape.h

@@ -31,7 +31,7 @@ private:
   INLINE BulletTriangleMeshShape();
 
 PUBLISHED:
-  BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress=true, bool bvh=true);
+  explicit BulletTriangleMeshShape(BulletTriangleMesh *mesh, bool dynamic, bool compress=true, bool bvh=true);
   INLINE BulletTriangleMeshShape(const BulletTriangleMeshShape &copy);
   INLINE void operator = (const BulletTriangleMeshShape &copy);
   INLINE ~BulletTriangleMeshShape();
@@ -53,6 +53,9 @@ private:
 
   PT(BulletTriangleMesh) _mesh;
 
+  // Stored temporarily during bam read.
+  PN_stdfloat _margin;
+
   bool _dynamic : 1;
   bool _compress : 1;
   bool _bvh : 1;

+ 7 - 0
panda/src/bullet/config_bullet.cxx

@@ -188,6 +188,13 @@ init_libbullet() {
   BulletSphereShape::register_with_read_factory();
   BulletTriangleMesh::register_with_read_factory();
   BulletTriangleMeshShape::register_with_read_factory();
+  BulletCylinderShape::register_with_read_factory();
+  BulletCapsuleShape::register_with_read_factory();
+  BulletConeShape::register_with_read_factory();
+  BulletHeightfieldShape::register_with_read_factory();
+  BulletConvexPointCloudShape::register_with_read_factory();
+  BulletMinkowskiSumShape::register_with_read_factory();
+  BulletMultiSphereShape::register_with_read_factory();
 
   // Custom contact callbacks
   gContactAddedCallback = contact_added_callback;

+ 1 - 1
panda/src/chan/animBundle.h

@@ -31,7 +31,7 @@ protected:
   AnimBundle(AnimGroup *parent, const AnimBundle &copy);
 
 PUBLISHED:
-  INLINE AnimBundle(const string &name, PN_stdfloat fps, int num_frames);
+  INLINE explicit AnimBundle(const string &name, PN_stdfloat fps, int num_frames);
 
   PT(AnimBundle) copy_bundle() const;
 

+ 1 - 1
panda/src/chan/animBundleNode.h

@@ -28,7 +28,7 @@
  */
 class EXPCL_PANDA_CHAN AnimBundleNode : public PandaNode {
 PUBLISHED:
-  INLINE AnimBundleNode(const string &name, AnimBundle *bundle);
+  INLINE explicit AnimBundleNode(const string &name, AnimBundle *bundle);
 
 protected:
   INLINE AnimBundleNode();

+ 1 - 1
panda/src/chan/animChannelMatrixXfmTable.h

@@ -34,7 +34,7 @@ protected:
   AnimChannelMatrixXfmTable(AnimGroup *parent, const AnimChannelMatrixXfmTable &copy);
 
 PUBLISHED:
-  AnimChannelMatrixXfmTable(AnimGroup *parent, const string &name);
+  explicit AnimChannelMatrixXfmTable(AnimGroup *parent, const string &name);
   virtual ~AnimChannelMatrixXfmTable();
 
 public:

+ 1 - 1
panda/src/chan/animGroup.h

@@ -37,7 +37,7 @@ protected:
 
 PUBLISHED:
   // This is the normal AnimGroup constructor.
-  AnimGroup(AnimGroup *parent, const string &name);
+  explicit AnimGroup(AnimGroup *parent, const string &name);
   virtual ~AnimGroup();
 
   int get_num_children() const;

+ 7 - 7
panda/src/chan/bindAnimRequest.h

@@ -30,13 +30,13 @@ public:
   ALLOC_DELETED_CHAIN(BindAnimRequest);
 
 PUBLISHED:
-  BindAnimRequest(const string &name,
-                  const Filename &filename,
-                  const LoaderOptions &options,
-                  Loader *loader,
-                  AnimControl *control,
-                  int hierarchy_match_flags,
-                  const PartSubset &subset);
+  explicit BindAnimRequest(const string &name,
+                           const Filename &filename,
+                           const LoaderOptions &options,
+                           Loader *loader,
+                           AnimControl *control,
+                           int hierarchy_match_flags,
+                           const PartSubset &subset);
 
 protected:
   virtual DoneStatus do_task();

+ 1 - 1
panda/src/chan/partBundle.h

@@ -55,7 +55,7 @@ protected:
   PartBundle(const PartBundle &copy);
 
 PUBLISHED:
-  PartBundle(const string &name = "");
+  explicit PartBundle(const string &name = "");
   virtual PartGroup *make_copy() const;
 
   INLINE CPT(AnimPreloadTable) get_anim_preload() const;

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.