| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- /**
- * 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 nodePath_ext.cxx
- * @author rdb
- * @date 2013-12-09
- */
- #include "nodePath_ext.h"
- #include "typedWritable_ext.h"
- #include "shaderInput_ext.h"
- #include "shaderAttrib.h"
- #include "collisionNode.h"
- #ifdef HAVE_PYTHON
- #ifndef CPPPARSER
- extern struct Dtool_PyTypedObject Dtool_BamWriter;
- extern struct Dtool_PyTypedObject Dtool_BamReader;
- #ifdef STDFLOAT_DOUBLE
- extern struct Dtool_PyTypedObject Dtool_LPoint3d;
- #else
- extern struct Dtool_PyTypedObject Dtool_LPoint3f;
- #endif
- extern struct Dtool_PyTypedObject Dtool_NodePath;
- extern struct Dtool_PyTypedObject Dtool_PandaNode;
- #endif // CPPPARSER
- /**
- * A special Python method that is invoked by copy.copy(node). Unlike the
- * NodePath copy constructor, this makes a duplicate copy of the underlying
- * PandaNode (but shares children, instead of copying them or omitting them).
- */
- NodePath Extension<NodePath>::
- __copy__() const {
- if (_this->is_empty()) {
- // Invoke the copy constructor if we have no node.
- return *_this;
- }
- // If we do have a node, duplicate it, and wrap it in a new NodePath.
- return NodePath(invoke_extension(_this->node()).__copy__());
- }
- /**
- * A special Python method that is invoked by copy.deepcopy(np). This calls
- * copy_to() unless the NodePath is already present in the provided
- * dictionary.
- */
- PyObject *Extension<NodePath>::
- __deepcopy__(PyObject *self, PyObject *memo) const {
- extern struct Dtool_PyTypedObject Dtool_NodePath;
- // Borrowed reference.
- PyObject *dupe;
- if (PyDict_GetItemRef(memo, self, &dupe) > 0) {
- // Already in the memo dictionary.
- 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 nullptr;
- }
- return dupe;
- }
- /**
- * This special Python method is implement to provide support for the pickle
- * module.
- *
- * This hooks into the native pickle and cPickle modules, but it cannot
- * properly handle self-referential BAM objects.
- */
- PyObject *Extension<NodePath>::
- __reduce__(PyObject *self) const {
- return __reduce_persist__(self, nullptr);
- }
- /**
- * This special Python method is implement to provide support for the pickle
- * module.
- *
- * This is similar to __reduce__, but it provides additional support for the
- * missing persistent-state object needed to properly support self-referential
- * BAM objects written to the pickle stream. This hooks into the pickle and
- * cPickle modules implemented in direct/src/stdpy.
- */
- PyObject *Extension<NodePath>::
- __reduce_persist__(PyObject *self, PyObject *pickler) const {
- // We should return at least a 2-tuple, (Class, (args)): the necessary class
- // object whose constructor we should call (e.g. this), and the arguments
- // necessary to reconstruct this object.
- BamWriter *writer = nullptr;
- if (pickler != nullptr) {
- PyObject *py_writer = PyObject_GetAttrString(pickler, "bamWriter");
- if (py_writer == nullptr) {
- // It's OK if there's no bamWriter.
- PyErr_Clear();
- } else {
- DtoolInstance_GetPointer(py_writer, writer, Dtool_BamWriter);
- Py_DECREF(py_writer);
- }
- }
- // We have a non-empty NodePath.
- vector_uchar bam_stream;
- if (!_this->encode_to_bam_stream(bam_stream, writer)) {
- std::ostringstream stream;
- stream << "Could not bamify " << _this;
- std::string message = stream.str();
- PyErr_SetString(PyExc_TypeError, message.c_str());
- return nullptr;
- }
- // Start by getting this class object.
- PyObject *this_class = (PyObject *)Py_TYPE(self);
- if (this_class == nullptr) {
- return nullptr;
- }
- PyObject *func;
- if (writer != nullptr) {
- // The modified pickle support: call the "persistent" version of this
- // function, which receives the unpickler itself as an additional
- // parameter.
- func = Extension<TypedWritable>::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream_persist");
- if (func == nullptr) {
- PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream_persist()");
- return nullptr;
- }
- } else {
- // The traditional pickle support: call the non-persistent version of this
- // function.
- func = Extension<TypedWritable>::find_global_decode(this_class, "py_decode_NodePath_from_bam_stream");
- if (func == nullptr) {
- PyErr_SetString(PyExc_TypeError, "Couldn't find py_decode_NodePath_from_bam_stream()");
- return nullptr;
- }
- }
- // PyTuple_SET_ITEM conveniently borrows the reference it is passed.
- PyObject *args = PyTuple_New(1);
- PyTuple_SET_ITEM(args, 0, Dtool_WrapValue(bam_stream));
- PyObject *tuple = PyTuple_New(2);
- PyTuple_SET_ITEM(tuple, 0, func);
- PyTuple_SET_ITEM(tuple, 1, args);
- return tuple;
- }
- /**
- * Returns the associated node's tags.
- */
- PyObject *Extension<NodePath>::
- get_tags() const {
- // An empty NodePath returns None
- if (_this->is_empty()) {
- return Py_NewRef(Py_None);
- }
- // Just call PandaNode.tags rather than defining a whole new interface.
- PT(PandaNode) node = _this->node();
- PyObject *py_node = DTool_CreatePyInstanceTyped
- ((void *)node.p(), Dtool_PandaNode, true, false, node->get_type_index());
- // DTool_CreatePyInstanceTyped() steals a C++ reference.
- node.cheat() = nullptr;
- PyObject *result = PyObject_GetAttrString(py_node, "tags");
- Py_DECREF(py_node);
- return result;
- }
- /**
- * Returns the lowest ancestor of this node that contains a tag definition
- * with the indicated key, if any, or an empty NodePath if no ancestor of this
- * node contains this tag definition. See set_python_tag().
- */
- NodePath Extension<NodePath>::
- find_net_python_tag(PyObject *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);
- }
- /**
- * This wrapper is defined as a global function to suit pickle's needs.
- */
- NodePath
- py_decode_NodePath_from_bam_stream(vector_uchar data) {
- return py_decode_NodePath_from_bam_stream_persist(nullptr, std::move(data));
- }
- /**
- * This wrapper is defined as a global function to suit pickle's needs.
- */
- NodePath
- py_decode_NodePath_from_bam_stream_persist(PyObject *unpickler, vector_uchar data) {
- BamReader *reader = nullptr;
- if (unpickler != nullptr) {
- PyObject *py_reader = PyObject_GetAttrString(unpickler, "bamReader");
- if (py_reader == nullptr) {
- // It's OK if there's no bamReader.
- PyErr_Clear();
- } else {
- DtoolInstance_GetPointer(py_reader, reader, Dtool_BamReader);
- Py_DECREF(py_reader);
- }
- }
- return NodePath::decode_from_bam_stream(std::move(data), reader);
- }
- /**
- * Sets a single shader input.
- */
- void Extension<NodePath>::
- set_shader_input(CPT_InternalName name, PyObject *value, int priority) {
- PT(PandaNode) node = _this->node();
- CPT(RenderAttrib) prev_attrib = node->get_attrib(ShaderAttrib::get_class_slot());
- PT(ShaderAttrib) attrib;
- if (prev_attrib == nullptr) {
- attrib = new ShaderAttrib();
- } else {
- attrib = new ShaderAttrib(*(const ShaderAttrib *)prev_attrib.p());
- }
- ShaderInput &input = attrib->_inputs[name];
- invoke_extension(&input).__init__(std::move(name), value, priority);
- if (!PyErr_Occurred()) {
- node->set_attrib(ShaderAttrib::return_new(attrib));
- }
- }
- /**
- * Sets multiple shader inputs at the same time. This can be significantly
- * more efficient if many inputs need to be set at the same time.
- */
- void Extension<NodePath>::
- set_shader_inputs(PyObject *args, PyObject *kwargs) {
- if (PyObject_Size(args) > 0) {
- Dtool_Raise_TypeError("NodePath.set_shader_inputs takes only keyword arguments");
- return;
- }
- PT(PandaNode) node = _this->node();
- CPT(RenderAttrib) prev_attrib = node->get_attrib(ShaderAttrib::get_class_slot());
- PT(ShaderAttrib) attrib;
- if (prev_attrib == nullptr) {
- attrib = new ShaderAttrib();
- } else {
- attrib = new ShaderAttrib(*(const ShaderAttrib *)prev_attrib.p());
- }
- PyObject *key, *value;
- Py_ssize_t pos = 0;
- Py_BEGIN_CRITICAL_SECTION(kwargs);
- while (PyDict_Next(kwargs, &pos, &key, &value)) {
- char *buffer;
- Py_ssize_t length;
- buffer = (char *)PyUnicode_AsUTF8AndSize(key, &length);
- if (buffer == nullptr) {
- Dtool_Raise_TypeError("NodePath.set_shader_inputs accepts only string keywords");
- break;
- }
- CPT_InternalName name(std::string(buffer, length));
- ShaderInput &input = attrib->_inputs[name];
- invoke_extension(&input).__init__(std::move(name), value);
- }
- Py_END_CRITICAL_SECTION();
- if (!PyErr_Occurred()) {
- node->set_attrib(ShaderAttrib::return_new(attrib));
- }
- }
- /**
- * 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.
- */
- PyObject *Extension<NodePath>::
- get_tight_bounds(const NodePath &other) const {
- LPoint3 *min_point = new LPoint3;
- LPoint3 *max_point = new LPoint3;
- if (_this->calc_tight_bounds(*min_point, *max_point, other)) {
- #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 {
- return Py_NewRef(Py_None);
- }
- }
- /**
- * Recursively assigns a weak reference to the given owner object to all
- * collision nodes at this level and below.
- *
- * You may pass in None to clear all owners below this level.
- *
- * Note that there is no corresponding get_collide_owner(), since there may be
- * multiple nodes below this level with different owners.
- */
- void Extension<NodePath>::
- set_collide_owner(PyObject *owner) {
- if (owner != Py_None) {
- PyObject *ref = PyWeakref_NewRef(owner, nullptr);
- if (ref != nullptr) {
- r_set_collide_owner(_this->node(), ref);
- Py_DECREF(ref);
- }
- } else {
- r_clear_collide_owner(_this->node());
- }
- }
- /**
- * Recursive implementation of set_collide_owner. weakref must be a weak ref
- * object.
- */
- void Extension<NodePath>::
- r_set_collide_owner(PandaNode *node, PyObject *weakref) {
- if (node->is_collision_node()) {
- CollisionNode *cnode = (CollisionNode *)node;
- cnode->set_owner(Py_NewRef(weakref),
- [](void *obj) { Py_DECREF((PyObject *)obj); });
- }
- PandaNode::Children cr = node->get_children();
- int num_children = cr.get_num_children();
- for (int i = 0; i < num_children; i++) {
- r_set_collide_owner(cr.get_child(i), weakref);
- }
- }
- /**
- * Recursive implementation of set_collide_owner(None).
- */
- void Extension<NodePath>::
- r_clear_collide_owner(PandaNode *node) {
- if (node->is_collision_node()) {
- CollisionNode *cnode = (CollisionNode *)node;
- cnode->clear_owner();
- }
- PandaNode::Children cr = node->get_children();
- int num_children = cr.get_num_children();
- for (int i = 0; i < num_children; i++) {
- r_clear_collide_owner(cr.get_child(i));
- }
- }
- #endif // HAVE_PYTHON
|