From 7e455e0fadd04d1fdb5624b380ed3fbefba6aef8 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 20 Feb 2014 13:58:23 +0000 Subject: [PATCH] Add files that belonged to the previous commit --- .../src/display/graphicsStateGuardian_ext.cxx | 55 ++++ panda/src/display/graphicsStateGuardian_ext.h | 40 +++ panda/src/egg/eggGroupNode_ext.cxx | 51 ++++ panda/src/egg/eggGroupNode_ext.h | 40 +++ panda/src/pgraph/nodePathCollection_ext.I | 51 ++++ panda/src/pgraph/nodePathCollection_ext.cxx | 48 ++++ panda/src/pgraph/nodePathCollection_ext.h | 44 +++ panda/src/pgraph/nodePath_ext.I | 197 ++++++++++++++ panda/src/pgraph/nodePath_ext.cxx | 232 ++++++++++++++++ panda/src/pgraph/nodePath_ext.h | 63 +++++ panda/src/putil/typedWritable_ext.cxx | 256 ++++++++++++++++++ panda/src/putil/typedWritable_ext.h | 49 ++++ 12 files changed, 1126 insertions(+) create mode 100644 panda/src/display/graphicsStateGuardian_ext.cxx create mode 100644 panda/src/display/graphicsStateGuardian_ext.h create mode 100644 panda/src/egg/eggGroupNode_ext.cxx create mode 100644 panda/src/egg/eggGroupNode_ext.h create mode 100644 panda/src/pgraph/nodePathCollection_ext.I create mode 100644 panda/src/pgraph/nodePathCollection_ext.cxx create mode 100644 panda/src/pgraph/nodePathCollection_ext.h create mode 100644 panda/src/pgraph/nodePath_ext.I create mode 100644 panda/src/pgraph/nodePath_ext.cxx create mode 100644 panda/src/pgraph/nodePath_ext.h create mode 100644 panda/src/putil/typedWritable_ext.cxx create mode 100644 panda/src/putil/typedWritable_ext.h diff --git a/panda/src/display/graphicsStateGuardian_ext.cxx b/panda/src/display/graphicsStateGuardian_ext.cxx new file mode 100644 index 0000000000..2f1c2d916e --- /dev/null +++ b/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:: +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 diff --git a/panda/src/display/graphicsStateGuardian_ext.h b/panda/src/display/graphicsStateGuardian_ext.h new file mode 100644 index 0000000000..cc1d9a3fe5 --- /dev/null +++ b/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 +// Description : This class defines the extension methods for +// Ramfile, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *get_prepared_textures() const; +}; + +#endif // HAVE_PYTHON + +#endif // GRAPHICSSTATEGUARDIAN_EXT_H diff --git a/panda/src/egg/eggGroupNode_ext.cxx b/panda/src/egg/eggGroupNode_ext.cxx new file mode 100644 index 0000000000..457ea73ba3 --- /dev/null +++ b/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:: +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 diff --git a/panda/src/egg/eggGroupNode_ext.h b/panda/src/egg/eggGroupNode_ext.h new file mode 100644 index 0000000000..ae29cc54b4 --- /dev/null +++ b/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 +// Description : This class defines the extension methods for +// EggGroupNode, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *get_children() const; +}; + +#endif // HAVE_PYTHON + +#endif // EGGGROUPNODE_EXT_H diff --git a/panda/src/pgraph/nodePathCollection_ext.I b/panda/src/pgraph/nodePathCollection_ext.I new file mode 100644 index 0000000000..d88ba08295 --- /dev/null +++ b/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::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:: +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; + } +} diff --git a/panda/src/pgraph/nodePathCollection_ext.cxx b/panda/src/pgraph/nodePathCollection_ext.cxx new file mode 100644 index 0000000000..324e746d47 --- /dev/null +++ b/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:: +__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 diff --git a/panda/src/pgraph/nodePathCollection_ext.h b/panda/src/pgraph/nodePathCollection_ext.h new file mode 100644 index 0000000000..96dc6f9e19 --- /dev/null +++ b/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 +// Description : This class defines the extension methods for +// NodePathCollection, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +public: + PyObject *__reduce__(PyObject *self) const; + + INLINE PyObject *get_tight_bounds() const; +}; + +#include "nodePathCollection_ext.I" + +#endif // HAVE_PYTHON + +#endif // NODEPATHCOLLECTION_EXT_H diff --git a/panda/src/pgraph/nodePath_ext.I b/panda/src/pgraph/nodePath_ext.I new file mode 100644 index 0000000000..a97a29fb59 --- /dev/null +++ b/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::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:: +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:: +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:: +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::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:: +set_python_tag(const string &key, PyObject *value) { + nassertv_always(!_this->is_empty()); + _this->node()->set_python_tag(key, value); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::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:: +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::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:: +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::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:: +clear_python_tag(const string &key) { + nassertv_always(!_this->is_empty()); + _this->node()->clear_python_tag(key); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::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:: +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::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:: +has_net_python_tag(const string &key) const { + return !find_net_python_tag(key).is_empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::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:: +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; + } +} diff --git a/panda/src/pgraph/nodePath_ext.cxx b/panda/src/pgraph/nodePath_ext.cxx new file mode 100644 index 0000000000..da66754c42 --- /dev/null +++ b/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::__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:: +__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::__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:: +__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::__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:: +__reduce__(PyObject *self) const { + return __reduce_persist__(self, NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: Extension::__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:: +__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::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::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::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:: +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 + diff --git a/panda/src/pgraph/nodePath_ext.h b/panda/src/pgraph/nodePath_ext.h new file mode 100644 index 0000000000..c91a462bff --- /dev/null +++ b/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 +// Description : This class defines the extension methods for +// NodePath, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +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 diff --git a/panda/src/putil/typedWritable_ext.cxx b/panda/src/putil/typedWritable_ext.cxx new file mode 100644 index 0000000000..ebe5f966d5 --- /dev/null +++ b/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:: +__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:: +__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:: +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 diff --git a/panda/src/putil/typedWritable_ext.h b/panda/src/putil/typedWritable_ext.h new file mode 100644 index 0000000000..b42092ab33 --- /dev/null +++ b/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 +// Description : This class defines the extension methods for +// StreamReader, which are called instead of +// any C++ methods with the same prototype. +//////////////////////////////////////////////////////////////////// +template<> +class Extension : public ExtensionBase { +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