Merge branch 'release/1.10.x'
This commit is contained in:
commit
e099d9e787
|
|
@ -21,8 +21,12 @@ shared context between all objects written by that Pickler.
|
|||
Unfortunately, cPickle cannot be supported, because it does not
|
||||
support extensions of this nature. """
|
||||
|
||||
__all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler",
|
||||
"Unpickler", "dump", "dumps", "load", "loads",
|
||||
"HIGHEST_PROTOCOL", "DEFAULT_PROTOCOL"]
|
||||
|
||||
import sys
|
||||
from panda3d.core import BamWriter, BamReader
|
||||
from panda3d.core import BamWriter, BamReader, TypedObject
|
||||
from copyreg import dispatch_table
|
||||
|
||||
|
||||
|
|
@ -30,17 +34,29 @@ from copyreg import dispatch_table
|
|||
# with the local pickle.py.
|
||||
pickle = __import__('pickle')
|
||||
|
||||
HIGHEST_PROTOCOL = pickle.HIGHEST_PROTOCOL
|
||||
DEFAULT_PROTOCOL = pickle.DEFAULT_PROTOCOL
|
||||
|
||||
PickleError = pickle.PickleError
|
||||
PicklingError = pickle.PicklingError
|
||||
UnpicklingError = pickle.UnpicklingError
|
||||
|
||||
BasePickler = pickle._Pickler
|
||||
BaseUnpickler = pickle._Unpickler
|
||||
|
||||
|
||||
class _Pickler(BasePickler):
|
||||
class Pickler(BasePickler):
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
self.bamWriter = BamWriter()
|
||||
self._canonical = {}
|
||||
BasePickler.__init__(self, *args, **kw)
|
||||
|
||||
def clear_memo(self):
|
||||
BasePickler.clear_memo(self)
|
||||
self._canonical.clear()
|
||||
self.bamWriter = BamWriter()
|
||||
|
||||
# We have to duplicate most of the save() method, so we can add
|
||||
# support for __reduce_persist__().
|
||||
|
||||
|
|
@ -54,6 +70,21 @@ class _Pickler(BasePickler):
|
|||
self.save_pers(pid)
|
||||
return
|
||||
|
||||
# Check if this is a Panda type that we've already saved; if so, store
|
||||
# a mapping to the canonical copy, so that Python's memoization system
|
||||
# works properly. This is needed because Python uses id(obj) for
|
||||
# memoization, but there may be multiple Python wrappers for the same
|
||||
# C++ pointer, and we don't want that to result in duplication.
|
||||
t = type(obj)
|
||||
if issubclass(t, TypedObject.__base__):
|
||||
canonical = self._canonical.get(obj.this)
|
||||
if canonical is not None:
|
||||
obj = canonical
|
||||
else:
|
||||
# First time we're seeing this C++ pointer; save it as the
|
||||
# "canonical" version.
|
||||
self._canonical[obj.this] = obj
|
||||
|
||||
# Check the memo
|
||||
x = self.memo.get(id(obj))
|
||||
if x:
|
||||
|
|
@ -61,7 +92,6 @@ class _Pickler(BasePickler):
|
|||
return
|
||||
|
||||
# Check the type dispatch table
|
||||
t = type(obj)
|
||||
f = self.dispatch.get(t)
|
||||
if f:
|
||||
f(self, obj) # Call unbound method with explicit self
|
||||
|
|
@ -146,25 +176,6 @@ class Unpickler(BaseUnpickler):
|
|||
BaseUnpickler.dispatch[pickle.REDUCE[0]] = load_reduce
|
||||
|
||||
|
||||
if sys.version_info >= (3, 8):
|
||||
# In Python 3.8 and up, we can use the C implementation of Pickler, which
|
||||
# supports a reducer_override method.
|
||||
class Pickler(pickle.Pickler):
|
||||
def __init__(self, *args, **kw):
|
||||
self.bamWriter = BamWriter()
|
||||
pickle.Pickler.__init__(self, *args, **kw)
|
||||
|
||||
def reducer_override(self, obj):
|
||||
reduce = getattr(obj, "__reduce_persist__", None)
|
||||
if reduce:
|
||||
return reduce(self)
|
||||
|
||||
return NotImplemented
|
||||
else:
|
||||
# Otherwise, we have to use our custom version that overrides save().
|
||||
Pickler = _Pickler
|
||||
|
||||
|
||||
# Shorthands
|
||||
from io import BytesIO
|
||||
|
||||
|
|
|
|||
|
|
@ -920,6 +920,9 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
|
|||
|| fname == "__delattr__") {
|
||||
// Just to prevent these from getting keyword arguments.
|
||||
|
||||
} else if (fname == "__setstate__") {
|
||||
_args_type = InterfaceMaker::AT_single_arg;
|
||||
|
||||
} else {
|
||||
if (_args_type == InterfaceMaker::AT_varargs) {
|
||||
// Every other method can take keyword arguments, if they take more
|
||||
|
|
|
|||
|
|
@ -3637,18 +3637,23 @@ write_function_for_name(ostream &out, Object *obj,
|
|||
std::string ClassName = make_safe_name(obj->_itype.get_scoped_name());
|
||||
std::string cClassName = obj->_itype.get_true_name();
|
||||
// string class_name = remap->_cpptype->get_simple_name();
|
||||
CPPStructType *struct_type = obj->_itype._cpptype->as_struct_type();
|
||||
|
||||
// If this is a non-static __setstate__, we run the default constructor.
|
||||
if (remap->_cppfunc->get_local_name() == "__setstate__") {
|
||||
out << " if (DtoolInstance_VOID_PTR(self) != nullptr) {\n"
|
||||
<< " Dtool_Raise_TypeError(\"C++ object is already constructed.\");\n";
|
||||
error_return(out, 4, return_flags);
|
||||
out << " }\n"
|
||||
<< " " << cClassName << " *local_this = new " << cClassName << ";\n"
|
||||
<< " DTool_PyInit_Finalize(self, local_this, &Dtool_" << ClassName
|
||||
<< ", false, false);\n"
|
||||
<< " if (local_this == nullptr) {\n"
|
||||
<< " PyErr_NoMemory();\n";
|
||||
if (remap->_cppfunc->get_local_name() == "__setstate__" &&
|
||||
!struct_type->is_abstract()) {
|
||||
out << " " << cClassName << " *local_this = nullptr;\n"
|
||||
<< " if (DtoolInstance_VOID_PTR(self) == nullptr) {\n"
|
||||
<< " local_this = new " << cClassName << ";\n"
|
||||
<< " DTool_PyInit_Finalize(self, local_this, &Dtool_" << ClassName
|
||||
<< ", true, false);\n"
|
||||
<< " if (local_this == nullptr) {\n"
|
||||
<< " PyErr_NoMemory();\n";
|
||||
error_return(out, 6, return_flags);
|
||||
out << " }\n"
|
||||
<< " } else if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", "
|
||||
<< "(void **)&local_this, \"" << classNameFromCppName(cClassName, false)
|
||||
<< "." << methodNameFromCppName(remap, cClassName, false) << "\")) {\n";
|
||||
}
|
||||
else if (all_nonconst) {
|
||||
// All remaps are non-const. Also check that this object isn't const.
|
||||
|
|
@ -3685,6 +3690,14 @@ write_function_for_name(ostream &out, Object *obj,
|
|||
args_type = AT_varargs;
|
||||
}
|
||||
|
||||
// If this is a __setstate__ taking multiple arguments, and we're given a
|
||||
// tuple as argument, unpack it.
|
||||
if (args_type == AT_single_arg && max_required_args > 1 &&
|
||||
remap->_cppfunc->get_local_name() == "__setstate__") {
|
||||
out << " PyObject *args = arg;\n";
|
||||
args_type = AT_varargs;
|
||||
}
|
||||
|
||||
if (args_type == AT_keyword_args || args_type == AT_varargs) {
|
||||
max_required_args = collapse_default_remaps(map_sets, max_required_args);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4397,7 +4397,7 @@ if not PkgSkip("EGG"):
|
|||
IGATEFILES.remove("parser.h")
|
||||
TargetAdd('libp3egg.in', opts=OPTS, input=IGATEFILES)
|
||||
TargetAdd('libp3egg.in', opts=['IMOD:panda3d.egg', 'ILIB:libp3egg', 'SRCDIR:panda/src/egg'])
|
||||
PyTargetAdd('p3egg_eggGroupNode_ext.obj', opts=OPTS, input='eggGroupNode_ext.cxx')
|
||||
PyTargetAdd('p3egg_ext_composite.obj', opts=OPTS, input='p3egg_ext_composite.cxx')
|
||||
|
||||
#
|
||||
# DIRECTORY: panda/src/egg2pg/
|
||||
|
|
@ -4486,7 +4486,7 @@ if not PkgSkip("EGG"):
|
|||
PyTargetAdd('egg_module.obj', opts=['IMOD:panda3d.egg', 'ILIB:egg', 'IMPORT:panda3d.core'])
|
||||
|
||||
PyTargetAdd('egg.pyd', input='egg_module.obj')
|
||||
PyTargetAdd('egg.pyd', input='p3egg_eggGroupNode_ext.obj')
|
||||
PyTargetAdd('egg.pyd', input='p3egg_ext_composite.obj')
|
||||
PyTargetAdd('egg.pyd', input='libp3egg_igate.obj')
|
||||
PyTargetAdd('egg.pyd', input='libp3egg2pg_igate.obj')
|
||||
PyTargetAdd('egg.pyd', input='libpandaegg.dll')
|
||||
|
|
|
|||
|
|
@ -153,6 +153,52 @@ flush() {
|
|||
end_group();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this object, to implement pickle support.
|
||||
*/
|
||||
void CollisionHandlerEvent::
|
||||
write_datagram(Datagram &dg) const {
|
||||
dg.add_uint32(_in_patterns.size());
|
||||
for (const std::string &pattern : _in_patterns) {
|
||||
dg.add_string(pattern);
|
||||
}
|
||||
|
||||
dg.add_uint32(_again_patterns.size());
|
||||
for (const std::string &pattern : _again_patterns) {
|
||||
dg.add_string(pattern);
|
||||
}
|
||||
|
||||
dg.add_uint32(_out_patterns.size());
|
||||
for (const std::string &pattern : _out_patterns) {
|
||||
dg.add_string(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the object state from the given datagram, previously obtained using
|
||||
* __getstate__.
|
||||
*/
|
||||
void CollisionHandlerEvent::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
_in_patterns.clear();
|
||||
size_t num_in_patterns = scan.get_uint32();
|
||||
for (size_t i = 0; i < num_in_patterns; ++i) {
|
||||
add_in_pattern(scan.get_string());
|
||||
}
|
||||
|
||||
_again_patterns.clear();
|
||||
size_t num_again_patterns = scan.get_uint32();
|
||||
for (size_t i = 0; i < num_again_patterns; ++i) {
|
||||
add_again_pattern(scan.get_string());
|
||||
}
|
||||
|
||||
_out_patterns.clear();
|
||||
size_t num_out_patterns = scan.get_uint32();
|
||||
for (size_t i = 0; i < num_out_patterns; ++i) {
|
||||
add_out_pattern(scan.get_string());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws whatever events are suggested by the list of patterns.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "vector_string.h"
|
||||
#include "pointerTo.h"
|
||||
#include "extension.h"
|
||||
|
||||
/**
|
||||
* A specialized kind of CollisionHandler that throws an event for each
|
||||
|
|
@ -61,12 +62,19 @@ PUBLISHED:
|
|||
MAKE_SEQ(get_out_patterns, get_num_out_patterns, get_out_pattern);
|
||||
|
||||
MAKE_SEQ_PROPERTY(in_patterns, get_num_in_patterns, get_in_pattern);
|
||||
MAKE_SEQ_PROPERTY(again_patterns, get_num_again_patterns, get_out_pattern);
|
||||
MAKE_SEQ_PROPERTY(again_patterns, get_num_again_patterns, get_again_pattern);
|
||||
MAKE_SEQ_PROPERTY(out_patterns, get_num_out_patterns, get_out_pattern);
|
||||
|
||||
void clear();
|
||||
void flush();
|
||||
|
||||
// These help implement Python pickle support.
|
||||
EXTENSION(PyObject *__reduce__(PyObject *self) const);
|
||||
EXTENSION(void __setstate__(PyObject *self, vector_uchar data));
|
||||
|
||||
void write_datagram(Datagram &destination) const;
|
||||
void read_datagram(DatagramIterator &source);
|
||||
|
||||
protected:
|
||||
void throw_event_for(const vector_string &patterns, CollisionEntry *entry);
|
||||
void throw_event_pattern(const std::string &pattern, CollisionEntry *entry);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file collisionHandlerEvent_ext.cxx
|
||||
* @author rdb
|
||||
* @date 2020-12-31
|
||||
*/
|
||||
|
||||
#include "collisionHandlerEvent_ext.h"
|
||||
#include "collisionHandlerFloor.h"
|
||||
#include "collisionHandlerGravity.h"
|
||||
#include "collisionHandlerPusher.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Implements pickling behavior.
|
||||
*/
|
||||
PyObject *Extension<CollisionHandlerEvent>::
|
||||
__reduce__(PyObject *self) const {
|
||||
extern struct Dtool_PyTypedObject Dtool_Datagram;
|
||||
|
||||
// Call the write_datagram method via Python, since it's not a virtual method
|
||||
// on the C++ end.
|
||||
PyObject *method_name = PyUnicode_FromString("write_datagram");
|
||||
|
||||
Datagram dg;
|
||||
PyObject *destination = DTool_CreatePyInstance(&dg, Dtool_Datagram, false, false);
|
||||
|
||||
PyObject *retval = PyObject_CallMethodOneArg(self, method_name, destination);
|
||||
Py_DECREF(method_name);
|
||||
Py_DECREF(destination);
|
||||
if (retval == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
Py_DECREF(retval);
|
||||
|
||||
const char *data = (const char *)dg.get_data();
|
||||
Py_ssize_t size = dg.get_length();
|
||||
return Py_BuildValue("O()y#", Py_TYPE(self), data, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the value returned by __getstate__ and uses it to freshly initialize
|
||||
* this CollisionHandlerEvent object.
|
||||
*/
|
||||
void Extension<CollisionHandlerEvent>::
|
||||
__setstate__(PyObject *self, vector_uchar data) {
|
||||
extern struct Dtool_PyTypedObject Dtool_DatagramIterator;
|
||||
|
||||
// Call the read_datagram method via Python, since it's not a virtual method
|
||||
// on the C++ end.
|
||||
PyObject *method_name = PyUnicode_FromString("read_datagram");
|
||||
|
||||
Datagram dg(std::move(data));
|
||||
DatagramIterator scan(dg);
|
||||
PyObject *source = DTool_CreatePyInstance(&scan, Dtool_DatagramIterator, false, false);
|
||||
|
||||
PyObject *retval = PyObject_CallMethodOneArg(self, method_name, source);
|
||||
Py_DECREF(method_name);
|
||||
Py_DECREF(source);
|
||||
Py_XDECREF(retval);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file collisionHandlerEvent_ext.h
|
||||
* @author rdb
|
||||
* @date 2020-12-31
|
||||
*/
|
||||
|
||||
#ifndef COLLISIONHANDLEREVENT_EXT_H
|
||||
#define COLLISIONHANDLEREVENT_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#include "extension.h"
|
||||
#include "collisionHandlerEvent.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for CollisionHandlerEvent, which are
|
||||
* called instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<CollisionHandlerEvent> : public ExtensionBase<CollisionHandlerEvent> {
|
||||
public:
|
||||
PyObject *__reduce__(PyObject *self) const;
|
||||
void __setstate__(PyObject *self, vector_uchar data);
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#endif // COLLISIONHANDLEREVENT_EXT_H
|
||||
|
|
@ -41,18 +41,31 @@ CollisionHandlerFloor::
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Serializes this object, to implement pickle support.
|
||||
*/
|
||||
void CollisionHandlerFloor::
|
||||
write_datagram(Datagram &dg) const {
|
||||
CollisionHandlerPhysical::write_datagram(dg);
|
||||
|
||||
*
|
||||
dg.add_float64(_offset);
|
||||
dg.add_float64(_reach);
|
||||
dg.add_float64(_max_velocity);
|
||||
}
|
||||
|
||||
*
|
||||
/**
|
||||
* Restores the object state from the given datagram, previously obtained using
|
||||
* __getstate__.
|
||||
*/
|
||||
void CollisionHandlerFloor::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
CollisionHandlerPhysical::read_datagram(scan);
|
||||
|
||||
*
|
||||
|
||||
*
|
||||
|
||||
*
|
||||
_offset = scan.get_float64();
|
||||
_reach = scan.get_float64();
|
||||
_max_velocity = scan.get_float64();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
PN_stdfloat CollisionHandlerFloor::
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ PUBLISHED:
|
|||
MAKE_PROPERTY(reach, get_reach, set_reach);
|
||||
MAKE_PROPERTY(max_velocity, get_max_velocity, set_max_velocity);
|
||||
|
||||
void write_datagram(Datagram &destination) const;
|
||||
void read_datagram(DatagramIterator &source);
|
||||
|
||||
protected:
|
||||
PN_stdfloat set_highest_collision(const NodePath &target_node_path, const NodePath &from_node_path, const Entries &entries);
|
||||
virtual bool handle_entries();
|
||||
|
|
|
|||
|
|
@ -46,6 +46,35 @@ CollisionHandlerGravity::
|
|||
~CollisionHandlerGravity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this object, to implement pickle support.
|
||||
*/
|
||||
void CollisionHandlerGravity::
|
||||
write_datagram(Datagram &dg) const {
|
||||
CollisionHandlerPhysical::write_datagram(dg);
|
||||
|
||||
dg.add_float64(_offset);
|
||||
dg.add_float64(_reach);
|
||||
dg.add_float64(_max_velocity);
|
||||
dg.add_float64(_gravity);
|
||||
dg.add_bool(_legacy_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the object state from the given datagram, previously obtained using
|
||||
* __getstate__.
|
||||
*/
|
||||
void CollisionHandlerGravity::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
CollisionHandlerPhysical::read_datagram(scan);
|
||||
|
||||
_offset = scan.get_float64();
|
||||
_reach = scan.get_float64();
|
||||
_max_velocity = scan.get_float64();
|
||||
_gravity = scan.get_float64();
|
||||
_legacy_mode = scan.get_bool();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ PUBLISHED:
|
|||
MAKE_PROPERTY(gravity, get_gravity, set_gravity);
|
||||
MAKE_PROPERTY(max_velocity, get_max_velocity, set_max_velocity);
|
||||
|
||||
void write_datagram(Datagram &destination) const;
|
||||
void read_datagram(DatagramIterator &source);
|
||||
|
||||
protected:
|
||||
PN_stdfloat set_highest_collision(const NodePath &target_node_path, const NodePath &from_node_path, const Entries &entries);
|
||||
virtual bool handle_entries();
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ PUBLISHED:
|
|||
PUBLISHED:
|
||||
MAKE_PROPERTY2(center, has_center, get_center, set_center, clear_center);
|
||||
|
||||
EXTENSION(PyObject *__reduce__(PyObject *self) const);
|
||||
EXTENSION(void __setstate__(PyObject *self, vector_uchar data, PyObject *nodepaths));
|
||||
|
||||
protected:
|
||||
bool _has_contact; // Are we in contact with anything?
|
||||
|
||||
|
|
@ -83,6 +86,8 @@ protected:
|
|||
|
||||
NodePath _center;
|
||||
|
||||
friend class Extension<CollisionHandlerPhysical>;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file collisionHandlerPhysical_ext.cxx
|
||||
* @author rdb
|
||||
* @date 2020-12-31
|
||||
*/
|
||||
|
||||
#include "collisionHandlerPhysical_ext.h"
|
||||
#include "collisionHandlerEvent_ext.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Implements pickling behavior.
|
||||
*/
|
||||
PyObject *Extension<CollisionHandlerPhysical>::
|
||||
__reduce__(PyObject *self) const {
|
||||
extern struct Dtool_PyTypedObject Dtool_Datagram;
|
||||
extern struct Dtool_PyTypedObject Dtool_NodePath;
|
||||
|
||||
// Create a tuple with all the NodePath pointers.
|
||||
PyObject *nodepaths = PyTuple_New(_this->_colliders.size() * 2 + 1);
|
||||
Py_ssize_t i = 0;
|
||||
|
||||
if (_this->has_center()) {
|
||||
const NodePath *center = &(_this->get_center());
|
||||
PyTuple_SET_ITEM(nodepaths, i++,
|
||||
DTool_CreatePyInstance((void *)center, Dtool_NodePath, false, true));
|
||||
} else {
|
||||
PyTuple_SET_ITEM(nodepaths, i++, Py_None);
|
||||
Py_INCREF(Py_None);
|
||||
}
|
||||
|
||||
CollisionHandlerPhysical::Colliders::const_iterator it;
|
||||
for (it = _this->_colliders.begin(); it != _this->_colliders.end(); ++it) {
|
||||
const NodePath *collider = &(it->first);
|
||||
const NodePath *target = &(it->second._target);
|
||||
PyTuple_SET_ITEM(nodepaths, i++,
|
||||
DTool_CreatePyInstance((void *)collider, Dtool_NodePath, false, true));
|
||||
PyTuple_SET_ITEM(nodepaths, i++,
|
||||
DTool_CreatePyInstance((void *)target, Dtool_NodePath, false, true));
|
||||
}
|
||||
|
||||
// Call the write_datagram method via Python, since it's not a virtual method
|
||||
// on the C++ end.
|
||||
PyObject *method_name = PyUnicode_FromString("write_datagram");
|
||||
|
||||
Datagram dg;
|
||||
PyObject *destination = DTool_CreatePyInstance(&dg, Dtool_Datagram, false, false);
|
||||
|
||||
PyObject *retval = PyObject_CallMethodOneArg(self, method_name, destination);
|
||||
Py_DECREF(method_name);
|
||||
Py_DECREF(destination);
|
||||
if (retval == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
Py_DECREF(retval);
|
||||
|
||||
const char *data = (const char *)dg.get_data();
|
||||
Py_ssize_t size = dg.get_length();
|
||||
return Py_BuildValue("O()(y#N)", Py_TYPE(self), data, size, nodepaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the value returned by __getstate__ and uses it to freshly initialize
|
||||
* this CollisionHandlerPhysical object.
|
||||
*/
|
||||
void Extension<CollisionHandlerPhysical>::
|
||||
__setstate__(PyObject *self, vector_uchar data, PyObject *nodepaths) {
|
||||
extern struct Dtool_PyTypedObject Dtool_DatagramIterator;
|
||||
|
||||
// Call the read_datagram method via Python, since it's not a virtual method
|
||||
// on the C++ end.
|
||||
PyObject *method_name = PyUnicode_FromString("read_datagram");
|
||||
|
||||
{
|
||||
Datagram dg(std::move(data));
|
||||
DatagramIterator scan(dg);
|
||||
PyObject *source = DTool_CreatePyInstance(&scan, Dtool_DatagramIterator, false, false);
|
||||
|
||||
PyObject *retval = PyObject_CallMethodOneArg(self, method_name, source);
|
||||
Py_DECREF(method_name);
|
||||
Py_DECREF(source);
|
||||
Py_XDECREF(retval);
|
||||
}
|
||||
|
||||
PyObject *center = PyTuple_GET_ITEM(nodepaths, 0);
|
||||
if (center != Py_None) {
|
||||
_this->set_center(*(NodePath *)DtoolInstance_VOID_PTR(center));
|
||||
} else {
|
||||
_this->clear_center();
|
||||
}
|
||||
|
||||
size_t num_nodepaths = Py_SIZE(nodepaths);
|
||||
for (size_t i = 1; i < num_nodepaths;) {
|
||||
NodePath *collider = (NodePath *)DtoolInstance_VOID_PTR(PyTuple_GET_ITEM(nodepaths, i++));
|
||||
NodePath *target = (NodePath *)DtoolInstance_VOID_PTR(PyTuple_GET_ITEM(nodepaths, i++));
|
||||
_this->add_collider(*collider, *target);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file collisionHandlerPhysical_ext.h
|
||||
* @author rdb
|
||||
* @date 2020-12-31
|
||||
*/
|
||||
|
||||
#ifndef COLLISIONHANDLERPHYSICAL_EXT_H
|
||||
#define COLLISIONHANDLERPHYSICAL_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#include "extension.h"
|
||||
#include "collisionHandlerPhysical.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for CollisionHandlerPhysical, which are
|
||||
* called instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<CollisionHandlerPhysical> : public ExtensionBase<CollisionHandlerPhysical> {
|
||||
public:
|
||||
PyObject *__reduce__(PyObject *self) const;
|
||||
void __setstate__(PyObject *self, vector_uchar data, PyObject *nodepaths);
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#endif // COLLISIONHANDLERPHYSICAL_EXT_H
|
||||
|
|
@ -49,6 +49,27 @@ CollisionHandlerPusher::
|
|||
~CollisionHandlerPusher() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this object, to implement pickle support.
|
||||
*/
|
||||
void CollisionHandlerPusher::
|
||||
write_datagram(Datagram &dg) const {
|
||||
CollisionHandlerPhysical::write_datagram(dg);
|
||||
|
||||
dg.add_bool(_horizontal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the object state from the given datagram, previously obtained using
|
||||
* __getstate__.
|
||||
*/
|
||||
void CollisionHandlerPusher::
|
||||
read_datagram(DatagramIterator &scan) {
|
||||
CollisionHandlerPhysical::read_datagram(scan);
|
||||
|
||||
_horizontal = scan.get_bool();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the parent class after all collisions have been detected, this
|
||||
* manages the various collisions and moves around the nodes as necessary.
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ PUBLISHED:
|
|||
PUBLISHED:
|
||||
MAKE_PROPERTY(horizontal, get_horizontal, set_horizontal);
|
||||
|
||||
void write_datagram(Datagram &destination) const;
|
||||
void read_datagram(DatagramIterator &source);
|
||||
|
||||
protected:
|
||||
virtual bool handle_entries();
|
||||
virtual void apply_net_shove(
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "collisionHandler.h"
|
||||
#include "collisionEntry.h"
|
||||
#include "extension.h"
|
||||
|
||||
/**
|
||||
* A special kind of CollisionHandler that does nothing except remember the
|
||||
|
|
@ -45,6 +46,8 @@ PUBLISHED:
|
|||
void output(std::ostream &out) const;
|
||||
void write(std::ostream &out, int indent_level = 0) const;
|
||||
|
||||
EXTENSION(PyObject *__reduce__(PyObject *self) const);
|
||||
|
||||
private:
|
||||
typedef pvector< PT(CollisionEntry) > Entries;
|
||||
Entries _entries;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file collisionHandlerQueue_ext.cxx
|
||||
* @author rdb
|
||||
* @date 2020-12-31
|
||||
*/
|
||||
|
||||
#include "collisionHandlerQueue_ext.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Implements pickling behavior.
|
||||
*/
|
||||
PyObject *Extension<CollisionHandlerQueue>::
|
||||
__reduce__(PyObject *self) const {
|
||||
// CollisionHandlerQueue has no interesting properties.
|
||||
return Py_BuildValue("(O())", Py_TYPE(self));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file collisionHandler_ext.h
|
||||
* @author rdb
|
||||
* @date 2020-12-31
|
||||
*/
|
||||
|
||||
#ifndef COLLISIONHANDLERQUEUE_EXT_H
|
||||
#define COLLISIONHANDLERQUEUE_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#include "extension.h"
|
||||
#include "collisionHandlerQueue.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for CollisionHandlerQueue, which are
|
||||
* called instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<CollisionHandlerQueue> : public ExtensionBase<CollisionHandlerQueue> {
|
||||
public:
|
||||
PyObject *__reduce__(PyObject *self) const;
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#endif // COLLISIONHANDLERQUEUE_EXT_H
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "pset.h"
|
||||
#include "register_type.h"
|
||||
#include "extension.h"
|
||||
|
||||
class CollisionNode;
|
||||
class CollisionRecorder;
|
||||
|
|
@ -79,6 +80,9 @@ PUBLISHED:
|
|||
void output(std::ostream &out) const;
|
||||
void write(std::ostream &out, int indent_level) const;
|
||||
|
||||
EXTENSION(PyObject *__getstate__() const);
|
||||
EXTENSION(void __setstate__(PyObject *state));
|
||||
|
||||
private:
|
||||
typedef pvector<CollisionLevelStateSingle> LevelStatesSingle;
|
||||
void prepare_colliders_single(LevelStatesSingle &level_states, const NodePath &root);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file collisionTraverser_ext.cxx
|
||||
* @author rdb
|
||||
* @date 2020-12-31
|
||||
*/
|
||||
|
||||
#include "collisionTraverser_ext.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Implements pickling behavior.
|
||||
*/
|
||||
PyObject *Extension<CollisionTraverser>::
|
||||
__getstate__() const {
|
||||
extern struct Dtool_PyTypedObject Dtool_CollisionHandler;
|
||||
extern struct Dtool_PyTypedObject Dtool_CollisionTraverser;
|
||||
extern struct Dtool_PyTypedObject Dtool_NodePath;
|
||||
|
||||
const std::string &name = _this->get_name();
|
||||
size_t num_colliders = _this->get_num_colliders();
|
||||
|
||||
PyObject *state = PyTuple_New(num_colliders * 2 + 3);
|
||||
PyTuple_SET_ITEM(state, 0, PyUnicode_FromStringAndSize(name.data(), name.size()));
|
||||
PyTuple_SET_ITEM(state, 1, PyBool_FromLong(_this->get_respect_prev_transform()));
|
||||
PyTuple_SET_ITEM(state, 2, PyLong_FromLong((long)num_colliders));
|
||||
|
||||
for (size_t i = 0; i < num_colliders; ++i) {
|
||||
NodePath *collider = new NodePath(_this->get_collider(i));
|
||||
PyTuple_SET_ITEM(state, i * 2 + 3,
|
||||
DTool_CreatePyInstance((void *)collider, Dtool_NodePath, true, false));
|
||||
|
||||
PT(CollisionHandler) handler = _this->get_handler(*collider);
|
||||
handler->ref();
|
||||
PyTuple_SET_ITEM(state, i * 2 + 4,
|
||||
DTool_CreatePyInstanceTyped((void *)handler.p(), Dtool_CollisionHandler, true, false, handler->get_type_index()));
|
||||
handler.cheat() = nullptr;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the value returned by __getstate__ and uses it to freshly initialize
|
||||
* this CollisionTraverser object.
|
||||
*/
|
||||
void Extension<CollisionTraverser>::
|
||||
__setstate__(PyObject *state) {
|
||||
_this->clear_colliders();
|
||||
|
||||
Py_ssize_t len = 0;
|
||||
const char *data = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(state, 0), &len);
|
||||
_this->set_name(std::string(data, len));
|
||||
|
||||
_this->set_respect_prev_transform(PyTuple_GET_ITEM(state, 1) != Py_False);
|
||||
size_t num_colliders = (ssize_t)PyLong_AsLong(PyTuple_GET_ITEM(state, 2));
|
||||
|
||||
for (size_t i = 0; i < num_colliders; ++i) {
|
||||
NodePath *collider = (NodePath *)DtoolInstance_VOID_PTR(PyTuple_GET_ITEM(state, i * 2 + 3));
|
||||
CollisionHandler *handler = (CollisionHandler *)DtoolInstance_VOID_PTR(PyTuple_GET_ITEM(state, i * 2 + 4));
|
||||
|
||||
_this->add_collider(*collider, handler);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file collisionTraverser_ext.h
|
||||
* @author rdb
|
||||
* @date 2020-12-31
|
||||
*/
|
||||
|
||||
#ifndef COLLISIONTRAVERSER_EXT_H
|
||||
#define COLLISIONTRAVERSER_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#include "extension.h"
|
||||
#include "collisionTraverser.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for CollisionTraverser, which are
|
||||
* called instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<CollisionTraverser> : public ExtensionBase<CollisionTraverser> {
|
||||
public:
|
||||
PyObject *__getstate__() const;
|
||||
void __setstate__(PyObject *state);
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#endif // COLLISIONTRAVERSER_EXT_H
|
||||
|
|
@ -1 +1,5 @@
|
|||
#include "collisionHandlerEvent_ext.cxx"
|
||||
#include "collisionHandlerPhysical_ext.cxx"
|
||||
#include "collisionHandlerQueue_ext.cxx"
|
||||
#include "collisionPolygon_ext.cxx"
|
||||
#include "collisionTraverser_ext.cxx"
|
||||
|
|
|
|||
|
|
@ -25,10 +25,11 @@ static bool traverse_callback(TextureContext *tc, void *data) {
|
|||
PyObject *element =
|
||||
DTool_CreatePyInstanceTyped(tex, Dtool_Texture,
|
||||
true, false, tex->get_type_index());
|
||||
tex->ref();
|
||||
tex.cheat() = nullptr;
|
||||
|
||||
PyObject *list = (PyObject *) data;
|
||||
PyList_Append(list, element);
|
||||
Py_DECREF(element);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ PUBLISHED:
|
|||
INLINE bool has_num_frames() const;
|
||||
INLINE int get_num_frames() const;
|
||||
|
||||
MAKE_PROPERTY2(fps, has_fps, get_fps, set_fps, clear_fps);
|
||||
MAKE_PROPERTY2(num_frames, has_num_frames, get_num_frames, set_num_frames, clear_num_frames);
|
||||
|
||||
virtual void write(std::ostream &out, int indent_level) const;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ PUBLISHED:
|
|||
|
||||
virtual void write(std::ostream &out, int indent_level) const;
|
||||
|
||||
EXTENSION(PyObject *__reduce__() const);
|
||||
|
||||
private:
|
||||
std::string _comment;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggComment_ext.cxx
|
||||
* @author rdb
|
||||
* @date 2021-01-01
|
||||
*/
|
||||
|
||||
#include "eggComment_ext.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Implements pickle support.
|
||||
*/
|
||||
PyObject *Extension<EggComment>::
|
||||
__reduce__() const {
|
||||
extern struct Dtool_PyTypedObject Dtool_EggComment;
|
||||
|
||||
std::string node_name = _this->get_name();
|
||||
std::string comment = _this->get_comment();
|
||||
return Py_BuildValue("O(s#s#)", (PyObject *)&Dtool_EggComment,
|
||||
node_name.data(), (Py_ssize_t)node_name.length(),
|
||||
comment.data(), (Py_ssize_t)comment.length());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggComment_ext.h
|
||||
* @author rdb
|
||||
* @date 2021-01-01
|
||||
*/
|
||||
|
||||
#ifndef EGGCOMMENT_EXT_H
|
||||
#define EGGCOMMENT_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#include "extension.h"
|
||||
#include "eggComment.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for EggComment, which are
|
||||
* called instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<EggComment> : public ExtensionBase<EggComment> {
|
||||
public:
|
||||
PyObject *__reduce__() const;
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#endif // EGGCOMMENT_EXT_H
|
||||
|
|
@ -36,6 +36,8 @@ PUBLISHED:
|
|||
|
||||
virtual void write(std::ostream &out, int indent_level) const;
|
||||
|
||||
EXTENSION(PyObject *__reduce__() const);
|
||||
|
||||
private:
|
||||
CoordinateSystem _value;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggCoordinateSystem_ext.cxx
|
||||
* @author rdb
|
||||
* @date 2021-01-01
|
||||
*/
|
||||
|
||||
#include "eggCoordinateSystem_ext.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Implements pickle support.
|
||||
*/
|
||||
PyObject *Extension<EggCoordinateSystem>::
|
||||
__reduce__() const {
|
||||
extern struct Dtool_PyTypedObject Dtool_EggCoordinateSystem;
|
||||
|
||||
// We can't use the regular EggNode handling for EggCoordinateSystem, because
|
||||
// the <CoordinateSystem> node is removed from the EggData after reading.
|
||||
// Oh well, this is more efficient anyway.
|
||||
int value = _this->get_value();
|
||||
return Py_BuildValue("O(i)", (PyObject *)&Dtool_EggCoordinateSystem, value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggCoordinateSystem_ext.h
|
||||
* @author rdb
|
||||
* @date 2021-01-01
|
||||
*/
|
||||
|
||||
#ifndef EGGCOORDINATESYSTEM_EXT_H
|
||||
#define EGGCOORDINATESYSTEM_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#include "extension.h"
|
||||
#include "eggCoordinateSystem.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for EggCoordinateSystem, which are
|
||||
* called instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<EggCoordinateSystem> : public ExtensionBase<EggCoordinateSystem> {
|
||||
public:
|
||||
PyObject *__reduce__() const;
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#endif // EGGCOORDINATESYSTEM_EXT_H
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
#include "lmatrix.h"
|
||||
#include "pointerTo.h"
|
||||
#include "referenceCount.h"
|
||||
#include "extension.h"
|
||||
|
||||
class EggGroupNode;
|
||||
class EggRenderMode;
|
||||
|
|
@ -90,6 +91,7 @@ PUBLISHED:
|
|||
void test_under_integrity() const { }
|
||||
#endif // _DEBUG
|
||||
|
||||
EXTENSION(PyObject *__reduce__() const);
|
||||
|
||||
protected:
|
||||
enum UnderFlags {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggNode_ext.cxx
|
||||
* @author rdb
|
||||
* @date 2021-01-01
|
||||
*/
|
||||
|
||||
#include "eggNode_ext.h"
|
||||
#include "eggData.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
/**
|
||||
* Implements pickle support.
|
||||
*/
|
||||
PyObject *Extension<EggNode>::
|
||||
__reduce__() const {
|
||||
extern struct Dtool_PyTypedObject Dtool_EggNode;
|
||||
|
||||
// Find the parse_egg_node function in this module.
|
||||
PyObject *sys_modules = PyImport_GetModuleDict();
|
||||
nassertr_always(sys_modules != nullptr, nullptr);
|
||||
|
||||
PyObject *module_name = PyObject_GetAttrString((PyObject *)&Dtool_EggNode, "__module__");
|
||||
nassertr_always(module_name != nullptr, nullptr);
|
||||
|
||||
PyObject *module = PyDict_GetItem(sys_modules, module_name);
|
||||
Py_DECREF(module_name);
|
||||
nassertr_always(module != nullptr, nullptr);
|
||||
|
||||
PyObject *func;
|
||||
if (_this->is_of_type(EggData::get_class_type())) {
|
||||
func = PyObject_GetAttrString(module, "parse_egg_data");
|
||||
} else {
|
||||
func = PyObject_GetAttrString(module, "parse_egg_node");
|
||||
}
|
||||
nassertr_always(func != nullptr, nullptr);
|
||||
|
||||
// Get the egg syntax to pass to the parse_egg_node function.
|
||||
std::ostringstream stream;
|
||||
_this->write(stream, INT_MIN);
|
||||
std::string data = stream.str();
|
||||
size_t length = data.size();
|
||||
|
||||
// Trim trailing whitespace.
|
||||
while (length > 0 && isspace(data[length - 1])) {
|
||||
--length;
|
||||
}
|
||||
|
||||
return Py_BuildValue("N(s#)", func, data.data(), (Py_ssize_t)length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an EggData from the raw egg syntax.
|
||||
*/
|
||||
PT(EggData) parse_egg_data(const std::string &egg_syntax) {
|
||||
PT(EggData) data = new EggData;
|
||||
data->set_auto_resolve_externals(false);
|
||||
|
||||
std::istringstream in(egg_syntax);
|
||||
|
||||
if (!data->read(in)) {
|
||||
PyErr_Format(PyExc_RuntimeError, "failed to parse egg data");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a single egg node from the raw egg syntax.
|
||||
*/
|
||||
PT(EggNode) parse_egg_node(const std::string &egg_syntax) {
|
||||
PT(EggData) data = parse_egg_data(egg_syntax);
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (data->size() != 1) {
|
||||
PyErr_Format(PyExc_RuntimeError, "expected exactly one node");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return data->remove_child(data->get_first_child());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggNode_ext.h
|
||||
* @author rdb
|
||||
* @date 2021-01-01
|
||||
*/
|
||||
|
||||
#ifndef EGGNODE_EXT_H
|
||||
#define EGGNODE_EXT_H
|
||||
|
||||
#include "dtoolbase.h"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#include "extension.h"
|
||||
#include "eggNode.h"
|
||||
#include "py_panda.h"
|
||||
|
||||
/**
|
||||
* This class defines the extension methods for EggNode, which are
|
||||
* called instead of any C++ methods with the same prototype.
|
||||
*/
|
||||
template<>
|
||||
class Extension<EggNode> : public ExtensionBase<EggNode> {
|
||||
public:
|
||||
PyObject *__reduce__() const;
|
||||
};
|
||||
|
||||
BEGIN_PUBLISH
|
||||
PT(EggData) parse_egg_data(const std::string &egg_syntax);
|
||||
PT(EggNode) parse_egg_node(const std::string &egg_syntax);
|
||||
END_PUBLISH
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
||||
#endif // EGGNODE_EXT_H
|
||||
|
|
@ -971,6 +971,7 @@ extern "C" int eggyywrap(void); // declared below.
|
|||
|
||||
static int yyinput(void); // declared by flex.
|
||||
|
||||
int eggyylex_destroy(void);
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Static variables
|
||||
|
|
@ -1018,6 +1019,15 @@ egg_init_lexer(istream &in, const string &filename) {
|
|||
initial_token = START_EGG;
|
||||
}
|
||||
|
||||
void
|
||||
egg_cleanup_lexer() {
|
||||
// Reset the lexer state.
|
||||
eggyylex_destroy();
|
||||
|
||||
input_p = nullptr;
|
||||
egg_filename.clear();
|
||||
}
|
||||
|
||||
void
|
||||
egg_start_group_body() {
|
||||
/* Set the initial state to begin within a group_body context,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ extern "C" int eggyywrap(void); // declared below.
|
|||
|
||||
static int yyinput(void); // declared by flex.
|
||||
|
||||
int eggyylex_destroy(void);
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Static variables
|
||||
|
|
@ -73,6 +74,15 @@ egg_init_lexer(istream &in, const string &filename) {
|
|||
initial_token = START_EGG;
|
||||
}
|
||||
|
||||
void
|
||||
egg_cleanup_lexer() {
|
||||
// Reset the lexer state.
|
||||
yylex_destroy();
|
||||
|
||||
input_p = nullptr;
|
||||
egg_filename.clear();
|
||||
}
|
||||
|
||||
void
|
||||
egg_start_group_body() {
|
||||
/* Set the initial state to begin within a group_body context,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <string>
|
||||
|
||||
void egg_init_lexer(std::istream &in, const std::string &filename);
|
||||
void egg_cleanup_lexer();
|
||||
void egg_start_group_body();
|
||||
void egg_start_texture_body();
|
||||
void egg_start_primitive_body();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
#include "eggComment_ext.cxx"
|
||||
#include "eggCoordinateSystem_ext.cxx"
|
||||
#include "eggGroupNode_ext.cxx"
|
||||
#include "eggNode_ext.cxx"
|
||||
|
|
@ -223,6 +223,8 @@ egg_cleanup_parser() {
|
|||
textures.clear();
|
||||
materials.clear();
|
||||
groups.clear();
|
||||
|
||||
egg_cleanup_lexer();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,8 @@ egg_cleanup_parser() {
|
|||
textures.clear();
|
||||
materials.clear();
|
||||
groups.clear();
|
||||
|
||||
egg_cleanup_lexer();
|
||||
}
|
||||
|
||||
%}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,8 @@ PUBLISHED:
|
|||
INLINE size_t count(const Element &) const;
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
EXTENSION(PyObject *__reduce__(PyObject *self) const);
|
||||
|
||||
EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags));
|
||||
EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
|
||||
#endif
|
||||
|
|
@ -273,6 +275,8 @@ PUBLISHED:
|
|||
INLINE size_t count(const Element &) const;
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
EXTENSION(PyObject *__reduce__(PyObject *self) const);
|
||||
|
||||
EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const);
|
||||
EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -77,6 +77,12 @@ protected:
|
|||
|
||||
PUBLISHED:
|
||||
INLINE ~PointerToArrayBase();
|
||||
|
||||
#ifdef CPPPARSER
|
||||
// These are implemented in PointerToVoid, but expose them here.
|
||||
INLINE bool operator == (const PointerToArrayBase<Element> &other) const;
|
||||
INLINE bool operator != (const PointerToArrayBase<Element> &other) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "pointerToArrayBase.I"
|
||||
|
|
|
|||
|
|
@ -219,6 +219,25 @@ get_subdata(size_t n, size_t count) const {
|
|||
return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements pickle support.
|
||||
*/
|
||||
template<class Element>
|
||||
INLINE PyObject *Extension<PointerToArray<Element> >::
|
||||
__reduce__(PyObject *self) const {
|
||||
// This preserves the distinction between a null vs. an empty PTA, though I'm
|
||||
// not sure that this distinction matters to anyone.
|
||||
if (this->_this->is_null()) {
|
||||
return Py_BuildValue("O()", Py_TYPE(self));
|
||||
}
|
||||
else if (this->_this->empty()) {
|
||||
return Py_BuildValue("O(())", Py_TYPE(self));
|
||||
}
|
||||
else {
|
||||
return Py_BuildValue("O(N)", Py_TYPE(self), get_data());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as get_element(), this returns the nth element of the array.
|
||||
*/
|
||||
|
|
@ -256,6 +275,22 @@ get_subdata(size_t n, size_t count) const {
|
|||
return PyBytes_FromStringAndSize((char *)(this->_this->p() + n), sizeof(Element) * count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements pickle support.
|
||||
*/
|
||||
template<class Element>
|
||||
INLINE PyObject *Extension<ConstPointerToArray<Element> >::
|
||||
__reduce__(PyObject *self) const {
|
||||
// This preserves the distinction between a null vs. an empty PTA, though I'm
|
||||
// not sure that this distinction matters to anyone.
|
||||
if (!this->_this->is_null() && this->_this->empty()) {
|
||||
return Py_BuildValue("O([])", Py_TYPE(self));
|
||||
}
|
||||
else {
|
||||
return Py_BuildValue("O(N)", Py_TYPE(self), get_data());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to implement the buffer protocol, in order to allow efficient
|
||||
* access to the array data through a Python multiview object.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ public:
|
|||
INLINE void set_data(PyObject *data);
|
||||
INLINE PyObject *get_subdata(size_t n, size_t count) const;
|
||||
|
||||
INLINE PyObject *__reduce__(PyObject *self) const;
|
||||
|
||||
INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags);
|
||||
INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
|
||||
};
|
||||
|
|
@ -75,6 +77,8 @@ public:
|
|||
INLINE PyObject *get_data() const;
|
||||
INLINE PyObject *get_subdata(size_t n, size_t count) const;
|
||||
|
||||
INLINE PyObject *__reduce__(PyObject *self) const;
|
||||
|
||||
INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const;
|
||||
INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ PUBLISHED:
|
|||
|
||||
static LoaderFileTypeRegistry *get_global_ptr();
|
||||
|
||||
EXTENSION(PyObject *__reduce__() const);
|
||||
|
||||
private:
|
||||
void record_extension(const std::string &extension, LoaderFileType *type);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "pythonLoaderFileType.h"
|
||||
|
||||
extern struct Dtool_PyTypedObject Dtool_LoaderFileType;
|
||||
extern struct Dtool_PyTypedObject Dtool_LoaderFileTypeRegistry;
|
||||
|
||||
/**
|
||||
* Registers a loader file type that is implemented in Python.
|
||||
|
|
@ -106,4 +107,13 @@ unregister_type(PyObject *type) {
|
|||
Py_XDECREF(save_func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements pickle support.
|
||||
*/
|
||||
PyObject *Extension<LoaderFileTypeRegistry>::
|
||||
__reduce__() const {
|
||||
PyObject *func = PyObject_GetAttrString((PyObject *)&Dtool_LoaderFileTypeRegistry, "get_global_ptr");
|
||||
return Py_BuildValue("N()", func);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ public:
|
|||
void register_deferred_type(PyObject *entry_point);
|
||||
|
||||
void unregister_type(PyObject *type);
|
||||
|
||||
PyObject *__reduce__() const;
|
||||
};
|
||||
|
||||
#endif // HAVE_PYTHON
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
from direct.stdpy.pickle import dumps, loads
|
||||
from panda3d.core import NodePath, CollisionNode
|
||||
|
||||
|
||||
def test_collision_handler_event_pickle():
|
||||
from panda3d.core import CollisionHandlerEvent
|
||||
|
||||
handler = CollisionHandlerEvent()
|
||||
handler.add_in_pattern("abcdefg")
|
||||
handler.add_in_pattern("test")
|
||||
handler.add_again_pattern("again pattern")
|
||||
handler.add_again_pattern("another again pattern")
|
||||
handler.add_out_pattern("out pattern")
|
||||
|
||||
handler = loads(dumps(handler, -1))
|
||||
|
||||
assert tuple(handler.in_patterns) == ("abcdefg", "test")
|
||||
assert tuple(handler.again_patterns) == ("again pattern", "another again pattern")
|
||||
assert tuple(handler.out_patterns) == ("out pattern",)
|
||||
|
||||
|
||||
def test_collision_handler_queue_pickle():
|
||||
from panda3d.core import CollisionHandlerQueue
|
||||
|
||||
handler = CollisionHandlerQueue()
|
||||
handler = loads(dumps(handler, -1))
|
||||
assert type(handler) == CollisionHandlerQueue
|
||||
|
||||
|
||||
def test_collision_handler_floor_pickle():
|
||||
from panda3d.core import CollisionHandlerFloor
|
||||
|
||||
collider1 = NodePath(CollisionNode("collider1"))
|
||||
collider2 = NodePath(CollisionNode("collider2"))
|
||||
target1 = NodePath("target1")
|
||||
target2 = NodePath("target2")
|
||||
center = NodePath("center")
|
||||
|
||||
handler = CollisionHandlerFloor()
|
||||
handler.add_out_pattern("out pattern")
|
||||
handler.add_collider(collider1, target1)
|
||||
handler.add_collider(collider2, target2)
|
||||
handler.center = center
|
||||
handler.offset = 1.0
|
||||
handler.reach = 2.0
|
||||
handler.max_velocity = 3.0
|
||||
|
||||
handler = loads(dumps(handler, -1))
|
||||
|
||||
assert tuple(handler.in_patterns) == ()
|
||||
assert tuple(handler.again_patterns) == ()
|
||||
assert tuple(handler.out_patterns) == ("out pattern",)
|
||||
assert handler.center.name == "center"
|
||||
assert handler.offset == 1.0
|
||||
assert handler.reach == 2.0
|
||||
assert handler.max_velocity == 3.0
|
||||
|
||||
|
||||
def test_collision_handler_gravity_pickle():
|
||||
from panda3d.core import CollisionHandlerGravity
|
||||
|
||||
collider1 = NodePath(CollisionNode("collider1"))
|
||||
collider2 = NodePath(CollisionNode("collider2"))
|
||||
target1 = NodePath("target1")
|
||||
target2 = NodePath("target2")
|
||||
|
||||
handler = CollisionHandlerGravity()
|
||||
handler.add_out_pattern("out pattern")
|
||||
handler.add_collider(collider1, target1)
|
||||
handler.add_collider(collider2, target2)
|
||||
handler.offset = 1.0
|
||||
handler.reach = 2.0
|
||||
handler.max_velocity = 3.0
|
||||
handler.gravity = -4.0
|
||||
|
||||
handler = loads(dumps(handler, -1))
|
||||
|
||||
assert tuple(handler.in_patterns) == ()
|
||||
assert tuple(handler.again_patterns) == ()
|
||||
assert tuple(handler.out_patterns) == ("out pattern",)
|
||||
assert handler.center == None
|
||||
assert handler.offset == 1.0
|
||||
assert handler.reach == 2.0
|
||||
assert handler.max_velocity == 3.0
|
||||
assert handler.gravity == -4.0
|
||||
|
||||
|
||||
def test_collision_handler_pusher_pickle():
|
||||
from panda3d.core import CollisionHandlerPusher
|
||||
|
||||
collider1 = NodePath(CollisionNode("collider1"))
|
||||
collider2 = NodePath(CollisionNode("collider2"))
|
||||
target1 = NodePath("target1")
|
||||
target2 = NodePath("target2")
|
||||
|
||||
handler = CollisionHandlerPusher()
|
||||
handler.add_again_pattern("again pattern")
|
||||
handler.add_collider(collider1, target1)
|
||||
handler.add_collider(collider2, target2)
|
||||
handler.horizontal = True
|
||||
|
||||
handler = loads(dumps(handler, -1))
|
||||
|
||||
assert tuple(handler.in_patterns) == ()
|
||||
assert tuple(handler.again_patterns) == ("again pattern",)
|
||||
assert tuple(handler.out_patterns) == ()
|
||||
assert not handler.has_center()
|
||||
assert handler.horizontal
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
from panda3d.core import CollisionTraverser, CollisionHandlerQueue
|
||||
from panda3d.core import NodePath, CollisionNode
|
||||
|
||||
|
||||
|
||||
def test_collision_traverser_pickle():
|
||||
from direct.stdpy.pickle import dumps, loads
|
||||
|
||||
handler = CollisionHandlerQueue()
|
||||
|
||||
collider1 = NodePath(CollisionNode("collider1"))
|
||||
collider2 = NodePath(CollisionNode("collider2"))
|
||||
|
||||
trav = CollisionTraverser("test123")
|
||||
trav.respect_prev_transform = True
|
||||
trav.add_collider(collider1, handler)
|
||||
trav.add_collider(collider2, handler)
|
||||
|
||||
trav = loads(dumps(trav, -1))
|
||||
assert trav.respect_prev_transform is True
|
||||
|
||||
assert trav.name == "test123"
|
||||
assert trav.get_num_colliders() == 2
|
||||
collider1 = trav.get_collider(0)
|
||||
collider2 = trav.get_collider(1)
|
||||
assert collider1.name == "collider1"
|
||||
assert collider2.name == "collider2"
|
||||
|
||||
# Two colliders must still be the same object; this only works with our own
|
||||
# version of the pickle module, in direct.stdpy.pickle.
|
||||
assert trav.get_handler(collider1) == trav.get_handler(collider2)
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
def test_pta_float_compare():
|
||||
from panda3d.core import PTA_float, CPTA_float
|
||||
|
||||
# Two null PTAs
|
||||
assert PTA_float() == PTA_float()
|
||||
assert not (PTA_float() != PTA_float())
|
||||
|
||||
# Two non-null PTAs
|
||||
assert PTA_float([1]) != PTA_float([1])
|
||||
assert not (PTA_float([1]) == PTA_float([1]))
|
||||
|
||||
# A copy of a PTA
|
||||
pta = PTA_float([1])
|
||||
assert pta == PTA_float(pta)
|
||||
assert not (pta != PTA_float(pta))
|
||||
|
||||
# A const copy of a PTA
|
||||
pta = PTA_float([1])
|
||||
cpta = CPTA_float(pta)
|
||||
assert pta == cpta
|
||||
assert not (pta != cpta)
|
||||
|
||||
|
||||
def test_pta_float_pickle():
|
||||
from panda3d.core import PTA_float
|
||||
from direct.stdpy.pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||
|
||||
null_pta = PTA_float()
|
||||
|
||||
empty_pta = PTA_float([])
|
||||
|
||||
data_pta = PTA_float([1.0, 2.0, 3.0])
|
||||
data = data_pta.get_data()
|
||||
|
||||
for proto in range(1, HIGHEST_PROTOCOL + 1):
|
||||
null_pta2 = loads(dumps(null_pta, proto))
|
||||
assert null_pta2.is_null()
|
||||
assert len(null_pta2) == 0
|
||||
|
||||
empty_pta2 = loads(dumps(empty_pta, proto))
|
||||
assert not empty_pta2.is_null()
|
||||
assert len(empty_pta2) == 0
|
||||
|
||||
data_pta2 = loads(dumps(data_pta, proto))
|
||||
assert tuple(data_pta2) == (1.0, 2.0, 3.0)
|
||||
assert data_pta2.get_data() == data_pta.get_data()
|
||||
|
||||
|
||||
def test_cpta_float_pickle():
|
||||
from panda3d.core import PTA_float, CPTA_float
|
||||
from direct.stdpy.pickle import dumps, loads, HIGHEST_PROTOCOL
|
||||
|
||||
null_pta = CPTA_float(PTA_float())
|
||||
|
||||
empty_pta = CPTA_float([])
|
||||
|
||||
data_pta = CPTA_float([1.0, 2.0, 3.0])
|
||||
data = data_pta.get_data()
|
||||
|
||||
for proto in range(1, HIGHEST_PROTOCOL + 1):
|
||||
null_pta2 = loads(dumps(null_pta, proto))
|
||||
assert null_pta2.is_null()
|
||||
assert len(null_pta2) == 0
|
||||
|
||||
empty_pta2 = loads(dumps(empty_pta, proto))
|
||||
assert not empty_pta2.is_null()
|
||||
assert len(empty_pta2) == 0
|
||||
|
||||
data_pta2 = loads(dumps(data_pta, proto))
|
||||
assert tuple(data_pta2) == (1.0, 2.0, 3.0)
|
||||
assert data_pta2.get_data() == data_pta.get_data()
|
||||
|
|
@ -3,6 +3,7 @@ import pytest
|
|||
import tempfile
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
import sys
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -218,3 +219,11 @@ def test_loader_ram_cache(test_filename):
|
|||
assert model1 == model2
|
||||
|
||||
ModelPool.release_model(model2)
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 4), reason="Requires Python 3.4")
|
||||
def test_loader_file_type_registry_pickle():
|
||||
from direct.stdpy.pickle import dumps, loads
|
||||
|
||||
registry = LoaderFileTypeRegistry.get_global_ptr()
|
||||
assert loads(dumps(registry, -1)) == registry
|
||||
|
|
|
|||
|
|
@ -86,3 +86,13 @@ def test_bitmask_pickle():
|
|||
data = pickle.dumps(mask1, -1)
|
||||
mask2 = pickle.loads(data)
|
||||
assert mask1 == mask2
|
||||
|
||||
mask1 = DoubleBitMaskNative(0x7fffffffffffffff)
|
||||
data = pickle.dumps(mask1, -1)
|
||||
mask2 = pickle.loads(data)
|
||||
assert mask1 == mask2
|
||||
|
||||
mask1 = DoubleBitMaskNative(1 << (double_num_bits - 1))
|
||||
data = pickle.dumps(mask1, -1)
|
||||
mask2 = pickle.loads(data)
|
||||
assert mask1 == mask2
|
||||
|
|
|
|||
|
|
@ -12,6 +12,20 @@ def test_reduce_persist():
|
|||
assert tuple(parent2.children) == (child2,)
|
||||
|
||||
|
||||
def test_pickle_copy():
|
||||
from panda3d.core import PandaNode, NodePath
|
||||
|
||||
# Make two Python wrappers pointing to the same node
|
||||
node1 = PandaNode("node")
|
||||
node2 = NodePath(node1).node()
|
||||
assert node1.this == node2.this
|
||||
assert id(node1) != id(node2)
|
||||
|
||||
# Test that pickling and loading still results in the same node object.
|
||||
node1, node2 = loads(dumps([node1, node2]))
|
||||
assert node1 == node2
|
||||
|
||||
|
||||
def test_pickle_error():
|
||||
class ErroneousPickleable(object):
|
||||
def __reduce__(self):
|
||||
|
|
|
|||
Loading…
Reference in New Issue