ソースを参照

Add files that belonged to the previous commit

rdb 12 年 前
コミット
7e455e0fad

+ 55 - 0
panda/src/display/graphicsStateGuardian_ext.cxx

@@ -0,0 +1,55 @@
+// Filename: graphicsStateGuardian_ext.cxx
+// Created by:  rdb (10Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "graphicsStateGuardian_ext.h"
+#include "textureContext.h"
+
+#ifdef HAVE_PYTHON
+
+#ifndef CPPPARSER
+IMPORT_THIS struct Dtool_PyTypedObject Dtool_Texture;
+#endif
+
+static bool traverse_callback(TextureContext *tc, void *data) {
+  PT(Texture) tex = tc->get_texture();
+  PyObject *element =
+    DTool_CreatePyInstanceTyped(tex, Dtool_Texture,
+                                true, false, tex->get_type_index());
+  tex->ref();
+
+  PyObject *list = (PyObject *) data;
+  PyList_Append(list, element);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_prepared_textures
+//       Access: Published
+//  Description: Returns a Python list of all of the
+//               currently-prepared textures within the GSG.
+////////////////////////////////////////////////////////////////////
+PyObject *Extension<GraphicsStateGuardian>::
+get_prepared_textures() const {
+  PyObject *list = PyList_New(0);
+
+  if (list == NULL) {
+    return NULL;
+  }
+
+  _this->traverse_prepared_textures(&traverse_callback, (void *)list);
+  return list;
+}
+
+#endif

+ 40 - 0
panda/src/display/graphicsStateGuardian_ext.h

@@ -0,0 +1,40 @@
+// Filename: graphicsStateGuardian_ext.h
+// Created by:  rdb (10Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef GRAPHICSSTATEGUARDIAN_EXT_H
+#define GRAPHICSSTATEGUARDIAN_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "graphicsStateGuardian.h"
+#include "py_panda.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : Extension<GraphicsStateGuardian>
+// Description : This class defines the extension methods for
+//               Ramfile, which are called instead of
+//               any C++ methods with the same prototype.
+////////////////////////////////////////////////////////////////////
+template<>
+class Extension<GraphicsStateGuardian> : public ExtensionBase<GraphicsStateGuardian> {
+public:
+  PyObject *get_prepared_textures() const;
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // GRAPHICSSTATEGUARDIAN_EXT_H

+ 51 - 0
panda/src/egg/eggGroupNode_ext.cxx

@@ -0,0 +1,51 @@
+// Filename: eggGroupNode_ext.cxx
+// Created by:  rdb (09Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "eggGroupNode_ext.h"
+
+#ifdef HAVE_PYTHON
+
+IMPORT_THIS struct Dtool_PyTypedObject Dtool_EggNode;
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggGroupNode::get_children
+//       Access: Published
+//  Description: Returns a Python list containing the node's children.
+////////////////////////////////////////////////////////////////////
+PyObject *Extension<EggGroupNode>::
+get_children() const {
+  EggGroupNode::iterator it;
+
+  // Create the Python list object.
+  EggGroupNode::size_type len = _this->size();
+  PyObject *lst = PyList_New(len);
+  if (lst == NULL) {
+    return NULL;
+  }
+
+  // Fill in the list.
+  int i = 0;
+  for (it = _this->begin(); it != _this->end() && i < len; ++it) {
+    EggNode *node = *it;
+    node->ref();
+    PyObject *item =
+      DTool_CreatePyInstanceTyped((void *)node, Dtool_EggNode, true, false, node->get_type_index());
+
+    PyList_SET_ITEM(lst, i++, item);
+  }
+
+  return lst;
+}
+
+#endif  // HAVE_PYTHON

+ 40 - 0
panda/src/egg/eggGroupNode_ext.h

@@ -0,0 +1,40 @@
+// Filename: eggGroupNode_ext.h
+// Created by:  rdb (09Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGGROUPNODE_EXT_H
+#define EGGGROUPNODE_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "eggGroupNode.h"
+#include "py_panda.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : Extension<EggGroupNode>
+// Description : This class defines the extension methods for
+//               EggGroupNode, which are called instead of
+//               any C++ methods with the same prototype.
+////////////////////////////////////////////////////////////////////
+template<>
+class Extension<EggGroupNode> : public ExtensionBase<EggGroupNode> {
+public:
+  PyObject *get_children() const;
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // EGGGROUPNODE_EXT_H

+ 51 - 0
panda/src/pgraph/nodePathCollection_ext.I

@@ -0,0 +1,51 @@
+// Filename: nodePathCollection_ext.I
+// Created by:  rdb (09Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CPPPARSER
+#ifdef STDFLOAT_DOUBLE
+IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3d;
+#else
+IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3f;
+#endif
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePathCollection>::get_tight_bounds
+//       Access: Published
+//  Description: Returns the tight bounds as a 2-tuple of LPoint3
+//               objects.  This is a convenience function for Python
+//               users, among which the use of calc_tight_bounds
+//               may be confusing.
+//               Returns None if calc_tight_bounds returned false.
+////////////////////////////////////////////////////////////////////
+INLINE PyObject *Extension<NodePathCollection>::
+get_tight_bounds() const {
+  LPoint3 *min_point = new LPoint3;
+  LPoint3 *max_point = new LPoint3;
+
+  if (_this->calc_tight_bounds(*min_point, *max_point)) {
+#ifdef STDFLOAT_DOUBLE
+    PyObject *min_inst = DTool_CreatePyInstance((void*) min_point, Dtool_LPoint3d, true, false);
+    PyObject *max_inst = DTool_CreatePyInstance((void*) max_point, Dtool_LPoint3d, true, false);
+#else
+    PyObject *min_inst = DTool_CreatePyInstance((void*) min_point, Dtool_LPoint3f, true, false);
+    PyObject *max_inst = DTool_CreatePyInstance((void*) max_point, Dtool_LPoint3f, true, false);
+#endif
+    return Py_BuildValue("NN", min_inst, max_inst);
+
+  } else {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+}

+ 48 - 0
panda/src/pgraph/nodePathCollection_ext.cxx

@@ -0,0 +1,48 @@
+// Filename: nodePathCollection_ext.cxx
+// Created by:  rdb (09Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "nodePathCollection_ext.h"
+
+#ifdef HAVE_PYTHON
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePathCollection::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+////////////////////////////////////////////////////////////////////
+PyObject *Extension<NodePathCollection>::
+__reduce__(PyObject *self) const {
+  // Here we will return a 4-tuple: (Class, (args), None, iterator),
+  // where iterator is an iterator that will yield successive
+  // NodePaths.
+
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  // Since a NodePathCollection is itself an iterator, we can simply
+  // pass it as the fourth tuple component.
+  PyObject *result = Py_BuildValue("(O()OO)", this_class, Py_None, self);
+  Py_DECREF(this_class);
+  return result;
+}
+
+#endif

+ 44 - 0
panda/src/pgraph/nodePathCollection_ext.h

@@ -0,0 +1,44 @@
+// Filename: nodePathCollection_ext.h
+// Created by:  rdb (09Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef NODEPATHCOLLECTION_EXT_H
+#define NODEPATHCOLLECTION_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "nodePathCollection.h"
+#include "py_panda.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : Extension<NodePathCollection>
+// Description : This class defines the extension methods for
+//               NodePathCollection, which are called instead of
+//               any C++ methods with the same prototype.
+////////////////////////////////////////////////////////////////////
+template<>
+class Extension<NodePathCollection> : public ExtensionBase<NodePathCollection> {
+public:
+  PyObject *__reduce__(PyObject *self) const;
+
+  INLINE PyObject *get_tight_bounds() const;
+};
+
+#include "nodePathCollection_ext.I"
+
+#endif  // HAVE_PYTHON
+
+#endif  // NODEPATHCOLLECTION_EXT_H

+ 197 - 0
panda/src/pgraph/nodePath_ext.I

@@ -0,0 +1,197 @@
+// Filename: nodePath_ext.I
+// Created by:  rdb (09Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CPPPARSER
+#ifdef STDFLOAT_DOUBLE
+IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3d;
+#else
+IMPORT_THIS struct Dtool_PyTypedObject Dtool_LPoint3f;
+#endif
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::get_python_tag_keys
+//       Access: Published
+//  Description: Fills the given vector up with the
+//               list of Python tags on this PandaNode.
+//
+//               It is the user's responsibility to ensure that the
+//               keys vector is empty before making this call;
+//               otherwise, the new files will be appended to it.
+////////////////////////////////////////////////////////////////////
+INLINE void Extension<NodePath>::
+get_python_tag_keys(vector_string &keys) const {
+  nassertv_always(!_this->is_empty());
+  _this->node()->get_python_tag_keys(keys);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_tag_keys
+//       Access: Published
+//  Description: This variant on get_tag_keys returns a Python list
+//               of strings. Returns None if the NodePath is empty.
+////////////////////////////////////////////////////////////////////
+INLINE PyObject *Extension<NodePath>::
+get_tag_keys() const {
+  // An empty NodePath returns None
+  if (_this->is_empty()) {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+  return _this->node()->get_tag_keys();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_python_tag_keys
+//       Access: Published
+//  Description: This variant on get_python_tag_keys returns a
+//               Python list of strings.
+//               Returns None if the NodePath is empty.
+////////////////////////////////////////////////////////////////////
+INLINE PyObject *Extension<NodePath>::
+get_python_tag_keys() const {
+  // An empty NodePath returns None
+  if (_this->is_empty()) {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+  return _this->node()->get_python_tag_keys();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::set_python_tag
+//       Access: Published
+//  Description: Associates an arbitrary Python object with a
+//               user-defined key which is stored on the node.  This
+//               object has no meaning to Panda; but it is stored
+//               indefinitely on the node until it is requested again.
+//
+//               Each unique key stores a different Python object.
+//               There is no effective limit on the number of
+//               different keys that may be stored or on the nature of
+//               any one key's object.
+////////////////////////////////////////////////////////////////////
+INLINE void Extension<NodePath>::
+set_python_tag(const string &key, PyObject *value) {
+  nassertv_always(!_this->is_empty());
+  _this->node()->set_python_tag(key, value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::get_python_tag
+//       Access: Published
+//  Description: Retrieves the Python object that was previously
+//               set on this node for the particular key, if any.  If
+//               no object has been previously set, returns None.
+//               See also get_net_python_tag().
+////////////////////////////////////////////////////////////////////
+INLINE PyObject *Extension<NodePath>::
+get_python_tag(const string &key) const {
+  // An empty NodePath quietly returns no tags.  This makes
+  // get_net_python_tag() easier to implement.
+  if (_this->is_empty()) {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+  return _this->node()->get_python_tag(key);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::has_python_tag
+//       Access: Published
+//  Description: Returns true if a Python object has been defined on
+//               this node for the particular key (even if that value
+//               is the empty string), or false if no value has been
+//               set.  See also has_net_python_tag().
+////////////////////////////////////////////////////////////////////
+INLINE bool Extension<NodePath>::
+has_python_tag(const string &key) const {
+  // An empty NodePath quietly has no tags.  This makes has_net_python_tag()
+  // easier to implement.
+  if (_this->is_empty()) {
+    return false;
+  }
+  return _this->node()->has_python_tag(key);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::clear_python_tag
+//       Access: Published
+//  Description: Removes the Python object defined for this key on this
+//               particular node.  After a call to clear_python_tag(),
+//               has_python_tag() will return false for the indicated
+//               key.
+////////////////////////////////////////////////////////////////////
+INLINE void Extension<NodePath>::
+clear_python_tag(const string &key) {
+  nassertv_always(!_this->is_empty());
+  _this->node()->clear_python_tag(key);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::get_net_python_tag
+//       Access: Published
+//  Description: Returns the Python object that has been defined on
+//               this node, or the nearest ancestor node, for the
+//               indicated key.  If no value has been defined for the
+//               indicated key on any ancestor node, returns None.
+//               See also get_python_tag().
+////////////////////////////////////////////////////////////////////
+INLINE PyObject *Extension<NodePath>::
+get_net_python_tag(const string &key) const {
+  NodePath tag_np = find_net_python_tag(key);
+  return invoke_extension(&tag_np).get_python_tag(key);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::has_net_python_tag
+//       Access: Published
+//  Description: Returns true if the indicated Python object has been
+//               defined on this node or on any ancestor node, or
+//               false otherwise.  See also has_python_tag().
+////////////////////////////////////////////////////////////////////
+INLINE bool Extension<NodePath>::
+has_net_python_tag(const string &key) const {
+  return !find_net_python_tag(key).is_empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::get_tight_bounds
+//       Access: Published
+//  Description: Returns the tight bounds as a 2-tuple of LPoint3
+//               objects.  This is a convenience function for Python
+//               users, among which the use of calc_tight_bounds
+//               may be confusing.
+//               Returns None if calc_tight_bounds returned false.
+////////////////////////////////////////////////////////////////////
+INLINE PyObject *Extension<NodePath>::
+get_tight_bounds() const {
+  LPoint3 *min_point = new LPoint3;
+  LPoint3 *max_point = new LPoint3;
+
+  if (_this->calc_tight_bounds(*min_point, *max_point)) {
+#ifdef STDFLOAT_DOUBLE
+    PyObject *min_inst = DTool_CreatePyInstance((void*) min_point, Dtool_LPoint3d, true, false);
+    PyObject *max_inst = DTool_CreatePyInstance((void*) max_point, Dtool_LPoint3d, true, false);
+#else
+    PyObject *min_inst = DTool_CreatePyInstance((void*) min_point, Dtool_LPoint3f, true, false);
+    PyObject *max_inst = DTool_CreatePyInstance((void*) max_point, Dtool_LPoint3f, true, false);
+#endif
+    return Py_BuildValue("NN", min_inst, max_inst);
+
+  } else {
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+}

+ 232 - 0
panda/src/pgraph/nodePath_ext.cxx

@@ -0,0 +1,232 @@
+// Filename: nodePath_ext.cxx
+// Created by:  rdb (09Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "nodePath_ext.h"
+#include "typedWritable_ext.h"
+
+#ifdef HAVE_PYTHON
+
+#ifndef CPPPARSER
+extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamWriter;
+extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamReader;
+#endif  // CPPPARSER
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::__copy__
+//       Access: Published
+//  Description: A special Python method that is invoked by
+//               copy.copy(node).  Unlike the NodePath copy
+//               constructor, this makes a duplicate copy of the
+//               underlying PandaNode (but shares children, instead of
+//               copying them or omitting them).
+////////////////////////////////////////////////////////////////////
+NodePath Extension<NodePath>::
+__copy__() const {
+  if (_this->is_empty()) {
+    // Invoke the copy constructor if we have no node.
+    return *_this;
+  }
+
+  // If we do have a node, duplicate it, and wrap it in a new
+  // NodePath.
+  return NodePath(_this->node()->__copy__());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::__deepcopy__
+//       Access: Published
+//  Description: A special Python method that is invoked by
+//               copy.deepcopy(np).  This calls copy_to() unless the
+//               NodePath is already present in the provided
+//               dictionary.
+////////////////////////////////////////////////////////////////////
+PyObject *Extension<NodePath>::
+__deepcopy__(PyObject *self, PyObject *memo) const {
+  IMPORT_THIS struct Dtool_PyTypedObject Dtool_NodePath;
+
+  // Borrowed reference.
+  PyObject *dupe = PyDict_GetItem(memo, self);
+  if (dupe != NULL) {
+    // Already in the memo dictionary.
+    Py_INCREF(dupe);
+    return dupe;
+  }
+
+  NodePath *np_dupe;
+  if (_this->is_empty()) {
+    np_dupe = new NodePath(*_this);
+  } else {
+    np_dupe = new NodePath(_this->copy_to(NodePath()));
+  }
+
+  dupe = DTool_CreatePyInstance((void *)np_dupe, Dtool_NodePath,
+                                true, false);
+  if (PyDict_SetItem(memo, self, dupe) != 0) {
+    Py_DECREF(dupe);
+    return NULL;
+  }
+
+  return dupe;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+//
+//               This hooks into the native pickle and cPickle
+//               modules, but it cannot properly handle
+//               self-referential BAM objects.
+////////////////////////////////////////////////////////////////////
+PyObject *Extension<NodePath>::
+__reduce__(PyObject *self) const {
+  return __reduce_persist__(self, NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::__reduce_persist__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+//
+//               This is similar to __reduce__, but it provides
+//               additional support for the missing persistent-state
+//               object needed to properly support self-referential
+//               BAM objects written to the pickle stream.  This hooks
+//               into the pickle and cPickle modules implemented in
+//               direct/src/stdpy.
+////////////////////////////////////////////////////////////////////
+PyObject *Extension<NodePath>::
+__reduce_persist__(PyObject *self, PyObject *pickler) const {
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+
+  BamWriter *writer = NULL;
+  if (pickler != NULL) {
+    PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter");
+    if (py_writer == NULL) {
+      // It's OK if there's no bamWriter.
+      PyErr_Clear();
+    } else {
+      DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer);
+      Py_DECREF(py_writer);
+    }
+  }
+
+  // We have a non-empty NodePath.
+
+  string bam_stream;
+  if (!_this->encode_to_bam_stream(bam_stream, writer)) {
+    ostringstream stream;
+    stream << "Could not bamify " << _this;
+    string message = stream.str();
+    PyErr_SetString(PyExc_TypeError, message.c_str());
+    return NULL;
+  }
+
+  // Start by getting this class object.
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *func;
+  if (writer != NULL) {
+    // The modified pickle support: call the "persistent" version of
+    // this function, which receives the unpickler itself as an
+    // additional parameter.
+    func = Extension<TypedWritable>::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream_persist");
+    if (func == NULL) {
+      PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream_persist()");
+      Py_DECREF(this_class);
+      return NULL;
+    }
+
+  } else {
+    // The traditional pickle support: call the non-persistent version
+    // of this function.
+
+    func = Extension<TypedWritable>::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream");
+    if (func == NULL) {
+      PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream()");
+      Py_DECREF(this_class);
+      return NULL;
+    }
+  }
+
+  PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), bam_stream.size());
+  Py_DECREF(func);
+  Py_DECREF(this_class);
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Extension<NodePath>::find_net_python_tag
+//       Access: Published
+//  Description: Returns the lowest ancestor of this node that
+//               contains a tag definition with the indicated key, if
+//               any, or an empty NodePath if no ancestor of this node
+//               contains this tag definition.  See set_python_tag().
+////////////////////////////////////////////////////////////////////
+NodePath Extension<NodePath>::
+find_net_python_tag(const string &key) const {
+  if (_this->is_empty()) {
+    return NodePath::not_found();
+  }
+  if (has_python_tag(key)) {
+    return *_this;
+  }
+  NodePath parent = _this->get_parent();
+  return invoke_extension(&parent).find_net_python_tag(key);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: py_decode_NodePath_from_bam_stream
+//       Access: Published
+//  Description: This wrapper is defined as a global function to suit
+//               pickle's needs.
+////////////////////////////////////////////////////////////////////
+NodePath
+py_decode_NodePath_from_bam_stream(const string &data) {
+  return py_decode_NodePath_from_bam_stream_persist(NULL, data);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: py_decode_NodePath_from_bam_stream_persist
+//       Access: Published
+//  Description: This wrapper is defined as a global function to suit
+//               pickle's needs.
+////////////////////////////////////////////////////////////////////
+NodePath
+py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data) {
+  BamReader *reader = NULL;
+  if (unpickler != NULL) {
+    PyObject *py_reader = PyObject_GetAttrString(unpickler, "bamReader");
+    if (py_reader == NULL) {
+      // It's OK if there's no bamReader.
+      PyErr_Clear();
+    } else {
+      DTOOL_Call_ExtractThisPointerForType(py_reader, &Dtool_BamReader, (void **)&reader);
+      Py_DECREF(py_reader);
+    }
+  }
+
+  return NodePath::decode_from_bam_stream(data, reader);
+}
+
+#endif  // HAVE_PYTHON
+

+ 63 - 0
panda/src/pgraph/nodePath_ext.h

@@ -0,0 +1,63 @@
+// Filename: nodePath_ext.h
+// Created by:  rdb (09Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef NODEPATH_EXT_H
+#define NODEPATH_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "nodePath.h"
+#include "py_panda.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : Extension<NodePath>
+// Description : This class defines the extension methods for
+//               NodePath, which are called instead of
+//               any C++ methods with the same prototype.
+////////////////////////////////////////////////////////////////////
+template<>
+class Extension<NodePath> : public ExtensionBase<NodePath> {
+public:
+  NodePath __copy__() const;
+  PyObject *__deepcopy__(PyObject *self, PyObject *memo) const;
+  PyObject *__reduce__(PyObject *self) const;
+  PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const;
+
+  INLINE PyObject *get_tag_keys() const;
+  INLINE void set_python_tag(const string &key, PyObject *value);
+  INLINE PyObject *get_python_tag(const string &key) const;
+  INLINE void get_python_tag_keys(vector_string &keys) const;
+  INLINE PyObject *get_python_tag_keys() const;
+  INLINE bool has_python_tag(const string &key) const;
+  INLINE void clear_python_tag(const string &key);
+  INLINE PyObject *get_net_python_tag(const string &key) const;
+  INLINE bool has_net_python_tag(const string &key) const;
+  NodePath find_net_python_tag(const string &key) const;
+
+  INLINE PyObject *get_tight_bounds() const;
+};
+
+BEGIN_PUBLISH
+NodePath py_decode_NodePath_from_bam_stream(const string &data);
+NodePath py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, const string &data);
+END_PUBLISH
+
+#include "nodePath_ext.I"
+
+#endif  // HAVE_PYTHON
+
+#endif  // NODEPATH_EXT_H

+ 256 - 0
panda/src/putil/typedWritable_ext.cxx

@@ -0,0 +1,256 @@
+// Filename: typedWritable_ext.cxx
+// Created by:  rdb (10Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "typedWritable_ext.h"
+
+#ifdef HAVE_PYTHON
+
+#ifndef CPPPARSER
+extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_BamWriter;
+#endif  // CPPPARSER
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::__reduce__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+//
+//               This hooks into the native pickle and cPickle
+//               modules, but it cannot properly handle
+//               self-referential BAM objects.
+////////////////////////////////////////////////////////////////////
+PyObject *Extension<TypedWritable>::
+__reduce__(PyObject *self) const {
+  return __reduce_persist__(self, NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::__reduce_persist__
+//       Access: Published
+//  Description: This special Python method is implement to provide
+//               support for the pickle module.
+//
+//               This is similar to __reduce__, but it provides
+//               additional support for the missing persistent-state
+//               object needed to properly support self-referential
+//               BAM objects written to the pickle stream.  This hooks
+//               into the pickle and cPickle modules implemented in
+//               direct/src/stdpy.
+////////////////////////////////////////////////////////////////////
+PyObject *Extension<TypedWritable>::
+__reduce_persist__(PyObject *self, PyObject *pickler) const {
+  // We should return at least a 2-tuple, (Class, (args)): the
+  // necessary class object whose constructor we should call
+  // (e.g. this), and the arguments necessary to reconstruct this
+  // object.
+
+  // Check that we have a decode_from_bam_stream python method.  If not,
+  // we can't use this interface.
+  PyObject *method = PyObject_GetAttrString(self, "decode_from_bam_stream");
+  if (method == NULL) {
+    ostringstream stream;
+    stream << "Cannot pickle objects of type " << _this->get_type() << "\n";
+    string message = stream.str();
+    PyErr_SetString(PyExc_TypeError, message.c_str());
+    return NULL;
+  }
+  Py_DECREF(method);
+
+  BamWriter *writer = NULL;
+  if (pickler != NULL) {
+    PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter");
+    if (py_writer == NULL) {
+      // It's OK if there's no bamWriter.
+      PyErr_Clear();
+    } else {
+      DTOOL_Call_ExtractThisPointerForType(py_writer, &Dtool_BamWriter, (void **)&writer);
+      Py_DECREF(py_writer);
+    }
+  }
+
+  // First, streamify the object, if possible.
+  string bam_stream;
+  if (!_this->encode_to_bam_stream(bam_stream, writer)) {
+    ostringstream stream;
+    stream << "Could not bamify object of type " << _this->get_type() << "\n";
+    string message = stream.str();
+    PyErr_SetString(PyExc_TypeError, message.c_str());
+    return NULL;
+  }
+
+  // Start by getting this class object.
+  PyObject *this_class = PyObject_Type(self);
+  if (this_class == NULL) {
+    return NULL;
+  }
+
+  PyObject *func;
+  if (writer != NULL) {
+    // The modified pickle support: call the "persistent" version of
+    // this function, which receives the unpickler itself as an
+    // additional parameter.
+    func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream_persist");
+    if (func == NULL) {
+      PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream_persist()");
+      Py_DECREF(this_class);
+      return NULL;
+    }
+
+  } else {
+    // The traditional pickle support: call the non-persistent version
+    // of this function.
+
+    func = find_global_decode(this_class, "py_decode_TypedWritable_from_bam_stream");
+    if (func == NULL) {
+      PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_TypedWritable_from_bam_stream()");
+      Py_DECREF(this_class);
+      return NULL;
+    }
+  }
+
+  PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), bam_stream.size());
+  Py_DECREF(func);
+  Py_DECREF(this_class);
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypedWritable::find_global_decode
+//       Access: Public, Static
+//  Description: This is a support function for __reduce__().  It
+//               searches for the global function
+//               py_decode_TypedWritable_from_bam_stream() in this
+//               class's module, or in the module for any base class.
+//               (It's really looking for the libpanda module, but we
+//               can't be sure what name that module was loaded under,
+//               so we search upwards this way.)
+//
+//               Returns: new reference on success, or NULL on failure.
+////////////////////////////////////////////////////////////////////
+PyObject *Extension<TypedWritable>::
+find_global_decode(PyObject *this_class, const char *func_name) {
+  PyObject *module_name = PyObject_GetAttrString(this_class, "__module__");
+  if (module_name != NULL) {
+    // borrowed reference
+    PyObject *sys_modules = PyImport_GetModuleDict();
+    if (sys_modules != NULL) {
+      // borrowed reference
+      PyObject *module = PyDict_GetItem(sys_modules, module_name);
+      if (module != NULL) {
+        PyObject *func = PyObject_GetAttrString(module, (char *)func_name);
+        if (func != NULL) {
+          Py_DECREF(module_name);
+          return func;
+        }
+      }
+    }
+  }
+  Py_DECREF(module_name);
+
+  PyObject *bases = PyObject_GetAttrString(this_class, "__bases__");
+  if (bases != NULL) {
+    if (PySequence_Check(bases)) {
+      Py_ssize_t size = PySequence_Size(bases);
+      for (Py_ssize_t i = 0; i < size; ++i) {
+        PyObject *base = PySequence_GetItem(bases, i);
+        if (base != NULL) {
+          PyObject *func = find_global_decode(base, func_name);
+          Py_DECREF(base);
+          if (func != NULL) {
+            Py_DECREF(bases);
+            return func;
+          }
+        }
+      }
+    }
+    Py_DECREF(bases);
+  }
+
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: py_decode_TypedWritable_from_bam_stream
+//       Access: Published
+//  Description: This wrapper is defined as a global function to suit
+//               pickle's needs.
+//
+//               This hooks into the native pickle and cPickle
+//               modules, but it cannot properly handle
+//               self-referential BAM objects.
+////////////////////////////////////////////////////////////////////
+PyObject *
+py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data) {
+  return py_decode_TypedWritable_from_bam_stream_persist(NULL, this_class, data);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: py_decode_TypedWritable_from_bam_stream_persist
+//       Access: Published
+//  Description: This wrapper is defined as a global function to suit
+//               pickle's needs.
+//
+//               This is similar to
+//               py_decode_TypedWritable_from_bam_stream, but it
+//               provides additional support for the missing
+//               persistent-state object needed to properly support
+//               self-referential BAM objects written to the pickle
+//               stream.  This hooks into the pickle and cPickle
+//               modules implemented in direct/src/stdpy.
+////////////////////////////////////////////////////////////////////
+PyObject *
+py_decode_TypedWritable_from_bam_stream_persist(PyObject *pickler, PyObject *this_class, const string &data) {
+
+  PyObject *py_reader = NULL;
+  if (pickler != NULL) {
+    py_reader = PyObject_GetAttrString(pickler, "bamReader");
+    if (py_reader == NULL) {
+      // It's OK if there's no bamReader.
+      PyErr_Clear();
+    }
+  }
+
+  // We need the function PandaNode::decode_from_bam_stream or
+  // TypedWritableReferenceCount::decode_from_bam_stream, which
+  // invokes the BamReader to reconstruct this object.  Since we use
+  // the specific object's class as the pointer, we get the particular
+  // instance of decode_from_bam_stream appropriate to this class.
+
+  PyObject *func = PyObject_GetAttrString(this_class, "decode_from_bam_stream");
+  if (func == NULL) {
+    return NULL;
+  }
+
+  PyObject *result;
+  if (py_reader != NULL){
+    result = PyObject_CallFunction(func, (char *)"(s#O)", data.data(), data.size(), py_reader);
+    Py_DECREF(py_reader);
+  } else {
+    result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), data.size());
+  }
+
+  if (result == NULL) {
+    return NULL;
+  }
+
+  if (result == Py_None) {
+    Py_DECREF(result);
+    PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream");
+    return NULL;
+  }
+
+  return result;
+}
+
+#endif

+ 49 - 0
panda/src/putil/typedWritable_ext.h

@@ -0,0 +1,49 @@
+// Filename: typedWritable_ext.h
+// Created by:  rdb (10Dec13)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TYPEDWRITABLE_EXT_H
+#define TYPEDWRITABLE_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "typedWritable.h"
+#include "py_panda.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : Extension<TypedWritable>
+// Description : This class defines the extension methods for
+//               StreamReader, which are called instead of
+//               any C++ methods with the same prototype.
+////////////////////////////////////////////////////////////////////
+template<>
+class Extension<TypedWritable> : public ExtensionBase<TypedWritable> {
+public:
+  PyObject *__reduce__(PyObject *self) const;
+  PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const;
+
+  static PyObject *find_global_decode(PyObject *this_class, const char *func_name);
+
+};
+
+BEGIN_PUBLISH
+PyObject *py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data);
+PyObject *py_decode_TypedWritable_from_bam_stream_persist(PyObject *unpickler, PyObject *this_class, const string &data);
+END_PUBLISH
+
+#endif  // HAVE_PYTHON
+
+#endif  // TYPEDWRITABLE_EXT_H