/** * 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 pandaNode.h * @author drose * @date 2002-02-20 */ #ifndef PANDANODE_H #define PANDANODE_H #include "pandabase.h" #include "cycleData.h" #include "cycleDataReader.h" #include "cycleDataWriter.h" #include "cycleDataLockedStageReader.h" #include "cycleDataStageReader.h" #include "cycleDataStageWriter.h" #include "pipelineCycler.h" #include "renderState.h" #include "renderEffects.h" #include "transformState.h" #include "drawMask.h" #include "typedWritable.h" #include "collideMask.h" #include "namable.h" #include "referenceCount.h" #include "luse.h" #include "ordered_vector.h" #include "pointerTo.h" #include "nodePointerTo.h" #include "pointerToArray.h" #include "pnotify.h" #include "updateSeq.h" #include "deletedChain.h" #include "pandaNodeChain.h" #include "pStatCollector.h" #include "copyOnWriteObject.h" #include "copyOnWritePointer.h" #include "lightReMutex.h" #include "extension.h" #include "simpleHashMap.h" class NodePathComponent; class CullTraverser; class CullTraverserData; class Light; class FactoryParams; class AccumulatedAttribs; class GeomTransformer; class GraphicsStateGuardianBase; /** * A basic node of the scene graph or data graph. This is the base class of * all specialized nodes, and also serves as a generic node with no special * properties. */ class EXPCL_PANDA_PGRAPH PandaNode : public TypedWritableReferenceCount, public Namable, public LinkedListNode { PUBLISHED: explicit PandaNode(const std::string &name); virtual ~PandaNode(); // published so that characters can be combined. virtual PandaNode *combine_with(PandaNode *other); protected: PandaNode(const PandaNode ©); PandaNode &operator = (const PandaNode ©) = delete; public: virtual PandaNode *dupe_for_flatten() const; virtual bool safe_to_flatten() const; virtual bool safe_to_transform() const; virtual bool safe_to_modify_transform() const; virtual bool safe_to_combine() const; virtual bool safe_to_combine_children() const; virtual bool safe_to_flatten_below() const; virtual bool preserve_name() const; virtual int get_unsafe_to_apply_attribs() const; virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types, GeomTransformer &transformer); virtual void xform(const LMatrix4 &mat); virtual CPT(TransformState) calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any, const TransformState *transform, Thread *current_thread = Thread::get_current_thread()) const; virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); virtual bool has_selective_visibility() const; virtual int get_first_visible_child() const; virtual int get_next_visible_child(int n) const; virtual bool has_single_child_visibility() const; virtual int get_visible_child() const; virtual bool is_renderable() const; virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); PUBLISHED: virtual PandaNode *make_copy() const; PT(PandaNode) copy_subgraph(Thread *current_thread = Thread::get_current_thread()) const; EXTENSION(PT(PandaNode) __copy__() const); EXTENSION(PyObject *__deepcopy__(PyObject *self, PyObject *memo) const); INLINE int get_num_parents(Thread *current_thread = Thread::get_current_thread()) const; INLINE PandaNode *get_parent(int n, Thread *current_thread = Thread::get_current_thread()) const; INLINE int find_parent(PandaNode *node, Thread *current_thread = Thread::get_current_thread()) const; MAKE_SEQ(get_parents, get_num_parents, get_parent); INLINE int get_num_children(Thread *current_thread = Thread::get_current_thread()) const; INLINE PandaNode *get_child(int n, Thread *current_thread = Thread::get_current_thread()) const; INLINE int get_child_sort(int n, Thread *current_thread = Thread::get_current_thread()) const; INLINE int find_child(PandaNode *node, Thread *current_thread = Thread::get_current_thread()) const; MAKE_SEQ(get_children, get_num_children, get_child); int count_num_descendants() const; void add_child(PandaNode *child_node, int sort = 0, Thread *current_thread = Thread::get_current_thread()); void remove_child(int child_index, Thread *current_thread = Thread::get_current_thread()); bool remove_child(PandaNode *child_node, Thread *current_thread = Thread::get_current_thread()); bool replace_child(PandaNode *orig_child, PandaNode *new_child, Thread *current_thread = Thread::get_current_thread()); INLINE bool stash_child(PandaNode *child_node, Thread *current_thread = Thread::get_current_thread()); void stash_child(int child_index, Thread *current_thread = Thread::get_current_thread()); INLINE bool unstash_child(PandaNode *child_node, Thread *current_thread = Thread::get_current_thread()); void unstash_child(int stashed_index, Thread *current_thread = Thread::get_current_thread()); INLINE int get_num_stashed(Thread *current_thread = Thread::get_current_thread()) const; INLINE PandaNode *get_stashed(int n, Thread *current_thread = Thread::get_current_thread()) const; INLINE int get_stashed_sort(int n, Thread *current_thread = Thread::get_current_thread()) const; INLINE int find_stashed(PandaNode *node, Thread *current_thread = Thread::get_current_thread()) const; MAKE_SEQ(get_stashed, get_num_stashed, get_stashed); void add_stashed(PandaNode *child_node, int sort = 0, Thread *current_thread = Thread::get_current_thread()); void remove_stashed(int child_index, Thread *current_thread = Thread::get_current_thread()); void remove_all_children(Thread *current_thread = Thread::get_current_thread()); void steal_children(PandaNode *other, Thread *current_thread = Thread::get_current_thread()); void copy_children(PandaNode *other, Thread *current_thread = Thread::get_current_thread()); void set_attrib(const RenderAttrib *attrib, int override = 0); INLINE CPT(RenderAttrib) get_attrib(TypeHandle type) const; INLINE CPT(RenderAttrib) get_attrib(int slot) const; INLINE bool has_attrib(TypeHandle type) const; INLINE bool has_attrib(int slot) const; INLINE void clear_attrib(TypeHandle type); void clear_attrib(int slot); void set_effect(const RenderEffect *effect); INLINE CPT(RenderEffect) get_effect(TypeHandle type) const; INLINE bool has_effect(TypeHandle type) const; void clear_effect(TypeHandle type); void set_state(const RenderState *state, Thread *current_thread = Thread::get_current_thread()); INLINE CPT(RenderState) get_state(Thread *current_thread = Thread::get_current_thread()) const; INLINE void clear_state(Thread *current_thread = Thread::get_current_thread()); MAKE_PROPERTY(state, get_state, set_state); void set_effects(const RenderEffects *effects, Thread *current_thread = Thread::get_current_thread()); INLINE CPT(RenderEffects) get_effects(Thread *current_thread = Thread::get_current_thread()) const; INLINE void clear_effects(Thread *current_thread = Thread::get_current_thread()); MAKE_PROPERTY(effects, get_effects, set_effects); void set_transform(const TransformState *transform, Thread *current_thread = Thread::get_current_thread()); INLINE CPT(TransformState) get_transform(Thread *current_thread = Thread::get_current_thread()) const; INLINE void clear_transform(Thread *current_thread = Thread::get_current_thread()); MAKE_PROPERTY(transform, get_transform, set_transform); void set_prev_transform(const TransformState *transform, Thread *current_thread = Thread::get_current_thread()); INLINE CPT(TransformState) get_prev_transform(Thread *current_thread = Thread::get_current_thread()) const; void reset_prev_transform(Thread *current_thread = Thread::get_current_thread()); INLINE bool has_dirty_prev_transform() const; static void reset_all_prev_transform(Thread *current_thread = Thread::get_current_thread()); MAKE_PROPERTY(prev_transform, get_prev_transform); void set_tag(const std::string &key, const std::string &value, Thread *current_thread = Thread::get_current_thread()); INLINE std::string get_tag(const std::string &key, Thread *current_thread = Thread::get_current_thread()) const; INLINE bool has_tag(const std::string &key, Thread *current_thread = Thread::get_current_thread()) const; void clear_tag(const std::string &key, Thread *current_thread = Thread::get_current_thread()); public: void get_tag_keys(vector_string &keys) const; INLINE size_t get_num_tags() const; INLINE std::string get_tag_key(size_t i) const; PUBLISHED: MAKE_MAP_PROPERTY(tags, has_tag, get_tag, set_tag, clear_tag); MAKE_MAP_KEYS_SEQ(tags, get_num_tags, get_tag_key); EXTENSION(PyObject *get_tag_keys() const); EXTENSION(PyObject *get_python_tags()); EXTENSION(void set_python_tag(PyObject *key, PyObject *value)); EXTENSION(PyObject *get_python_tag(PyObject *key) const); EXTENSION(bool has_python_tag(PyObject *key) const); EXTENSION(void clear_python_tag(PyObject *key)); EXTENSION(PyObject *get_python_tag_keys() const); MAKE_PROPERTY(python_tags, get_python_tags); EXTENSION(int __traverse__(visitproc visit, void *arg)); INLINE bool has_tags() const; void copy_tags(PandaNode *other); void list_tags(std::ostream &out, const std::string &separator = "\n") const; int compare_tags(const PandaNode *other) const; void copy_all_properties(PandaNode *other); void replace_node(PandaNode *other); enum UnexpectedChange { UC_parents = 0x001, UC_children = 0x002, UC_transform = 0x004, UC_state = 0x008, UC_draw_mask = 0x010, }; void set_unexpected_change(unsigned int flags); unsigned int get_unexpected_change(unsigned int flags) const; void clear_unexpected_change(unsigned int flags); INLINE static DrawMask get_overall_bit(); INLINE static DrawMask get_all_camera_mask(); INLINE bool is_overall_hidden() const; INLINE void set_overall_hidden(bool overall_hidden); MAKE_PROPERTY(overall_bit, get_overall_bit); MAKE_PROPERTY(all_camera_mask, get_all_camera_mask); MAKE_PROPERTY(overall_hidden, is_overall_hidden, set_overall_hidden); void adjust_draw_mask(DrawMask show_mask, DrawMask hide_mask, DrawMask clear_mask); INLINE DrawMask get_draw_control_mask() const; INLINE DrawMask get_draw_show_mask() const; MAKE_PROPERTY(draw_control_mask, get_draw_control_mask); MAKE_PROPERTY(draw_show_mask, get_draw_show_mask); DrawMask get_net_draw_control_mask() const; DrawMask get_net_draw_show_mask() const; void set_into_collide_mask(CollideMask mask); INLINE CollideMask get_into_collide_mask() const; virtual CollideMask get_legal_collide_mask() const; MAKE_PROPERTY(into_collide_mask, get_into_collide_mask, set_into_collide_mask); MAKE_PROPERTY(legal_collide_mask, get_legal_collide_mask); CollideMask get_net_collide_mask(Thread *current_thread = Thread::get_current_thread()) const; CPT(RenderAttrib) get_off_clip_planes(Thread *current_thread = Thread::get_current_thread()) const; void prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state); bool is_scene_root() const; bool is_under_scene_root() const; virtual void output(std::ostream &out) const; virtual void write(std::ostream &out, int indent_level) const; INLINE void ls(std::ostream &out, int indent_level) const; // A node has three bounding volumes: an "external" bounding volume that // represents the node and all of its children, an "internal" bounding // volume which represents only the node itself (and is usually empty, // unless a specific node type sets it otherwise), and a "user" bounding // volume which is specified by the user. // We define set_bounds() and get_bounds() functions so that set_bounds() // sets the user bounding volume, while get_bounds() returns the external // bounding volume. Although it might seem strange and confusing to do // this, this is actually the natural way the user thinks about nodes and // bounding volumes. void set_bounds_type(BoundingVolume::BoundsType bounds_type); BoundingVolume::BoundsType get_bounds_type() const; MAKE_PROPERTY(bounds_type, get_bounds_type); void set_bounds(const BoundingVolume *volume); void set_bound(const BoundingVolume *volume); INLINE void clear_bounds(); CPT(BoundingVolume) get_bounds(Thread *current_thread = Thread::get_current_thread()) const; CPT(BoundingVolume) get_bounds(UpdateSeq &seq, Thread *current_thread = Thread::get_current_thread()) const; int get_nested_vertices(Thread *current_thread = Thread::get_current_thread()) const; INLINE CPT(BoundingVolume) get_internal_bounds(Thread *current_thread = Thread::get_current_thread()) const; INLINE int get_internal_vertices(Thread *current_thread = Thread::get_current_thread()) const; MAKE_PROPERTY(nested_vertices, get_nested_vertices); MAKE_PROPERTY(internal_bounds, get_internal_bounds); MAKE_PROPERTY(internal_vertices, get_internal_vertices); void mark_bounds_stale(Thread *current_thread = Thread::get_current_thread()) const; void mark_internal_bounds_stale(Thread *current_thread = Thread::get_current_thread()); INLINE bool is_bounds_stale() const; MAKE_PROPERTY(bounds_stale, is_bounds_stale); INLINE void set_final(bool flag); INLINE bool is_final(Thread *current_thread = Thread::get_current_thread()) const; MAKE_PROPERTY(final, is_final, set_final); virtual bool is_geom_node() const; virtual bool is_lod_node() const; virtual bool is_collision_node() const; virtual Light *as_light(); virtual bool is_ambient_light() const; enum FancyBits { FB_transform = 0x0001, FB_state = 0x0002, FB_effects = 0x0004, FB_tag = 0x0010, FB_draw_mask = 0x0020, FB_cull_callback = 0x0040, }; INLINE int get_fancy_bits(Thread *current_thread = Thread::get_current_thread()) const; PUBLISHED: static PT(PandaNode) decode_from_bam_stream(vector_uchar data, BamReader *reader = nullptr); protected: class BoundsData; INLINE CPT(BoundingVolume) get_user_bounds(int pipeline_stage, Thread *current_thread) const; CPT(BoundingVolume) get_internal_bounds(int pipeline_stage, Thread *current_thread) const; int get_internal_vertices(int pipeline_stage, Thread *current_thread) const; void set_internal_bounds(const BoundingVolume *volume); INLINE void mark_bounds_stale(int pipeline_stage, Thread *current_thread) const; void force_bounds_stale(Thread *current_thread = Thread::get_current_thread()); void force_bounds_stale(int pipeline_stage, Thread *current_thread); INLINE void mark_internal_bounds_stale(int pipeline_stage, Thread *current_thread); virtual void r_mark_geom_bounds_stale(Thread *current_thread); virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, int &internal_vertices, int pipeline_stage, Thread *current_thread) const; virtual void parents_changed(); virtual void children_changed(); virtual void transform_changed(); virtual void state_changed(); virtual void draw_mask_changed(); typedef pmap InstanceMap; virtual PT(PandaNode) r_copy_subgraph(InstanceMap &inst_map, Thread *current_thread) const; virtual void r_copy_children(const PandaNode *from, InstanceMap &inst_map, Thread *current_thread); void set_cull_callback(); void disable_cull_callback(); public: virtual void r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, GeomTransformer &transformer, Thread *current_thread); protected: // This is a base class of CData, defined below. It contains just the // protected (not private) part of CData that will be needed by derived // classes to implement compute_internal_bounds(). class EXPCL_PANDA_PGRAPH BoundsData : public CycleData { protected: INLINE BoundsData(); INLINE BoundsData(const BoundsData ©); INLINE void copy_bounds(const BoundsData ©); public: // This is the "internal" bounding volume, which is normally empty, but // which a particular PandaNode subclass may define to be any arbitrary // volume, by calling set_internal_bounds() or by overriding // compute_internal_bounds(). CPT(BoundingVolume) _internal_bounds; int _internal_vertices; UpdateSeq _internal_bounds_mark; // incremented on mark_stale UpdateSeq _internal_bounds_computed; // set to above when computing }; private: class CData; INLINE int do_find_parent(PandaNode *node, const CData *cdata) const; bool stage_remove_child(PandaNode *child_node, int pipeline_stage, Thread *current_thread); bool stage_replace_child(PandaNode *orig_child, PandaNode *new_child, int pipeline_stage, Thread *current_thread); void quick_add_new_child(PandaNode *child_node, int sort, Thread *current_thread); INLINE bool verify_child_no_cycles(PandaNode *child_node); void report_cycle(PandaNode *node); bool find_node_above(PandaNode *node); // parent-child manipulation for NodePath support. Don't try to call these // directly. static PT(NodePathComponent) attach(NodePathComponent *parent, PandaNode *child, int sort, int pipeline_stage, Thread *current_thread); static void detach(NodePathComponent *child, int pipeline_stage, Thread *current_thread); static void detach_one_stage(NodePathComponent *child, int pipeline_stage, Thread *current_thread); static bool reparent(NodePathComponent *new_parent, NodePathComponent *child, int sort, bool as_stashed, int pipeline_stage, Thread *current_thread); static bool reparent_one_stage(NodePathComponent *new_parent, NodePathComponent *child, int sort, bool as_stashed, int pipeline_stage, Thread *current_thread); static PT(NodePathComponent) get_component(NodePathComponent *parent, PandaNode *child, int pipeline_stage, Thread *current_thread); static PT(NodePathComponent) get_top_component(PandaNode *child, bool force, int pipeline_stage, Thread *current_thread); PT(NodePathComponent) get_generic_component(bool accept_ambiguity, int pipeline_stage, Thread *current_thread); PT(NodePathComponent) r_get_generic_component(bool accept_ambiguity, bool &ambiguity_detected, int pipeline_stage, Thread *current_thread); void delete_component(NodePathComponent *component); static void sever_connection(PandaNode *parent_node, PandaNode *child_node, int pipeline_stage, Thread *current_thread); static void new_connection(PandaNode *parent_node, PandaNode *child_node, int pipeline_stage, Thread *current_thread); void fix_path_lengths(int pipeline_stage, Thread *current_thread); void r_list_descendants(std::ostream &out, int indent_level) const; INLINE void do_set_dirty_prev_transform(); INLINE void do_clear_dirty_prev_transform(); public: // This must be declared public so that VC6 will allow the nested CData // class to access it. class EXPCL_PANDA_PGRAPH DownConnection { public: INLINE DownConnection(PandaNode *child, int sort); INLINE bool operator < (const DownConnection &other) const; INLINE PandaNode *get_child() const; INLINE void set_child(PandaNode *child); INLINE int get_sort() const; private: // Child pointers are reference counted. That way, holding a pointer to // the root of a subgraph keeps the entire subgraph around. PT(PandaNode) _child; int _sort; }; private: typedef ov_multiset DownList; typedef CopyOnWriteObj1< DownList, TypeHandle > Down; // Store a pointer to the down_list during the bam read pass. class EXPCL_PANDA_PGRAPH BamReaderAuxDataDown : public BamReaderAuxData { public: INLINE BamReaderAuxDataDown(); Down _down_list; public: virtual TypeHandle get_type() const { return get_class_type(); } virtual TypeHandle force_init_type() {init_type(); return get_class_type();} static TypeHandle get_class_type() { return _type_handle; } public: static void init_type() { BamReaderAuxData::init_type(); register_type(_type_handle, "BamReaderAuxDataDown", BamReaderAuxData::get_class_type()); } private: static TypeHandle _type_handle; }; class EXPCL_PANDA_PGRAPH UpConnection { public: INLINE UpConnection(PandaNode *child); INLINE bool operator < (const UpConnection &other) const; INLINE PandaNode *get_parent() const; private: // Parent pointers are not reference counted. That way, parents and // children do not circularly reference each other. PandaNode *_parent; }; typedef ov_set UpList; typedef CopyOnWriteObj1< UpList, TypeHandle > Up; // We also maintain a set of NodePathComponents in the node. This // represents the set of instances of this node that we have requested a // NodePath for. We don't keep reference counts; when each // NodePathComponent destructs, it removes itself from this set. typedef phash_set Paths; // We don't cycle the set of Paths, since these are across all threads. A // NodePathComponent, once created, is always associated with the same node. // We do, however, protect the Paths under a mutex. Paths _paths; LightReMutex _paths_lock; bool _dirty_prev_transform; static PandaNodeChain _dirty_prev_transforms; // This is used to maintain a table of keyed data on each node, for the // user's purposes. typedef SimpleHashMap TagData; // This is actually implemented in pandaNode_ext.h, but defined here so // that we can destruct it from the C++ side. Note that it isn't cycled, // because I imagine it's rare to see a Python thread on a stage other than // stage 0, and let's not make things unnecessarily complicated. class PythonTagData : public ReferenceCount { public: virtual ~PythonTagData() {}; }; PT(PythonTagData) _python_tag_data; #ifndef NDEBUG unsigned int _unexpected_change_flags; #endif // !NDEBUG // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA_PGRAPH CData : public BoundsData { public: CData(); CData(const CData ©); virtual ~CData(); ALLOC_DELETED_CHAIN(CData); virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; void update_bam_nested(BamWriter *manager) const; virtual int complete_pointers(TypedWritable **plist, BamReader *manager); virtual void fillin(DatagramIterator &scan, BamReader *manager); virtual TypeHandle get_parent_type() const { return PandaNode::get_class_type(); } public: // This section contains the lightweight parts of the node that are likely // to change fairly often: transform and state. NCPT(RenderState) _state; NCPT(TransformState) _transform; NCPT(TransformState) _prev_transform; public: // This section contains the heavierweight parts of the node that are less // likely to change as often: tags, collide mask. INLINE void set_fancy_bit(int bits, bool value); #ifdef HAVE_PYTHON void inc_py_refs(); void dec_py_refs(); #endif CPT(RenderEffects) _effects; TagData _tag_data; // These two together determine the per-camera visibility of this node. // See adjust_draw_mask() for details. DrawMask _draw_control_mask, _draw_show_mask; // This is the mask that indicates which CollisionNodes may detect a // collision with this particular node. By default it is zero for an // ordinary PandaNode, and all bits on for a CollisionNode or GeomNode. CollideMask _into_collide_mask; // The requested bounding volume type. BoundingVolume::BoundsType _bounds_type; // This is the user bounding volume, which is only specified by a user. // It defaults to NULL, which means an empty volume. CPT(BoundingVolume) _user_bounds; // See BoundsData, above, for _internal_bounds. // This is true if the external bounds of this node should be deemed // "final". See set_final(). bool _final_bounds; // This bitmask is maintained automatically by the internal PandaNode // code; it contains a 1 for each "fancy" attribute that is set on the // node. See enum FancyBits, above. int _fancy_bits; public: // This section contains the data that is accumulated upward from the // node's children: that is, the external bounding volume, and // conceptually similar things like the net_collide_mask, etc. None of // the data in this object is preserved in a bam file. // This is the union of all into_collide_mask bits for any nodes at and // below this level. CollideMask _net_collide_mask; // These are similar, for the draw mask. DrawMask _net_draw_control_mask, _net_draw_show_mask; // This is a ClipPlaneAttrib that represents the union of all clip planes // that have been turned *off* at and below this level. TODO: fix the // circular reference counts involved here. CPT(RenderAttrib) _off_clip_planes; // The number of vertices rendered by this node and all child nodes. int _nested_vertices; // This is the bounding volume around the _user_bounds, the // _internal_bounds, and all of the children's external bounding volumes. CPT(BoundingVolume) _external_bounds; // When _last_update != _next_update, this cache is stale. UpdateSeq _last_update, _next_update; // We don't always update the bounding volume and number of nested // vertices. This indicates the last time they were changed. It is never // higher than _last_update. UpdateSeq _last_bounds_update; public: // This section stores the links to other nodes above and below this node // in the graph. void write_up_list(const Up &up_list, BamWriter *manager, Datagram &dg) const; void write_down_list(const Down &down_list, BamWriter *manager, Datagram &dg) const; void update_up_list(const Up &up_list, BamWriter *manager) const; void update_down_list(const Down &down_list, BamWriter *manager) const; int complete_up_list(Up &up_list, const std::string &tag, TypedWritable **p_list, BamReader *manager); int complete_down_list(Down &down_list, const std::string &tag, TypedWritable **p_list, BamReader *manager); void fillin_up_list(Up &up_list, const std::string &tag, DatagramIterator &scan, BamReader *manager); void fillin_down_list(Down &down_list, const std::string &tag, DatagramIterator &scan, BamReader *manager); INLINE CPT(Down) get_down() const; INLINE PT(Down) modify_down(); INLINE CPT(Down) get_stashed() const; INLINE PT(Down) modify_stashed(); INLINE CPT(Up) get_up() const; INLINE PT(Up) modify_up(); private: // We store the child lists by reference, so we can copy them quickly. We // perform copy-on-write when necessary. COWPT(Down) _down; COWPT(Down) _stashed; COWPT(Up) _up; public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { register_type(_type_handle, "PandaNode::CData"); } private: static TypeHandle _type_handle; }; PipelineCycler _cycler; typedef CycleDataReader CDReader; typedef CycleDataWriter CDWriter; typedef CycleDataLockedStageReader CDLockedStageReader; typedef CycleDataStageReader CDStageReader; typedef CycleDataStageWriter CDStageWriter; int do_find_child(PandaNode *node, const Down *down) const; CDStageWriter update_cached(bool update_bounds, int pipeline_stage, CDLockedStageReader &cdata); static DrawMask _overall_bit; static PStatCollector _reset_prev_pcollector; static PStatCollector _update_bounds_pcollector; PUBLISHED: // This class is returned from get_children(). Use it to walk through the // list of children. This is faster, and safer, than walking through the // children one at a time via get_num_children()/get_child(), since the list // of children is saved out ahead of time, rather than having to reacquire // the lock with each iteration, or to keep the lock held for the entire // pass. class EXPCL_PANDA_PGRAPH Children { public: INLINE Children(); INLINE Children(const CData *cdata); INLINE Children(const Children ©); INLINE Children(Children &&from) noexcept; INLINE void operator = (const Children ©); INLINE void operator = (Children &&from) noexcept; INLINE size_t get_num_children() const; INLINE PandaNode *get_child(size_t n) const; INLINE int get_child_sort(size_t n) const; PUBLISHED: INLINE PandaNode *operator [](size_t n) const { return get_child(n); } INLINE size_t size() const { return get_num_children(); } private: CPT(Down) _down; }; // Similarly for stashed children. class EXPCL_PANDA_PGRAPH Stashed { public: INLINE Stashed(); INLINE Stashed(const CData *cdata); INLINE Stashed(const Stashed ©); INLINE Stashed(Stashed &&from) noexcept; INLINE void operator = (const Stashed ©); INLINE void operator = (Stashed &&from) noexcept; INLINE size_t get_num_stashed() const; INLINE PandaNode *get_stashed(size_t n) const; INLINE int get_stashed_sort(size_t n) const; PUBLISHED: INLINE PandaNode *operator [](size_t n) const { return get_stashed(n); } INLINE size_t size() const { return get_num_stashed(); } private: CPT(Down) _stashed; }; // This class is returned from get_parents(). class EXPCL_PANDA_PGRAPH Parents { public: INLINE Parents(); INLINE Parents(const CData *cdata); INLINE Parents(const Parents ©); INLINE Parents(Parents &&from) noexcept; INLINE void operator = (const Parents ©); INLINE void operator = (Parents &&from) noexcept; INLINE size_t get_num_parents() const; INLINE PandaNode *get_parent(size_t n) const; PUBLISHED: INLINE PandaNode *operator [](size_t n) const { return get_parent(n); } INLINE size_t size() const { return get_num_parents(); } private: CPT(Up) _up; }; public: INLINE Children get_children(Thread *current_thread = Thread::get_current_thread()) const; INLINE Stashed get_stashed(Thread *current_thread = Thread::get_current_thread()) const; INLINE Parents get_parents(Thread *current_thread = Thread::get_current_thread()) const; typedef bool SceneRootFunc(const PandaNode *); static void set_scene_root_func(SceneRootFunc *func); PUBLISHED: MAKE_PROPERTY(children, get_children); MAKE_PROPERTY(stashed, get_stashed); MAKE_PROPERTY(parents, get_parents); private: static SceneRootFunc *_scene_root_func; public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &dg); virtual void update_bam_nested(BamWriter *manager); void write_recorder(BamWriter *manager, Datagram &dg); protected: static TypedWritable *make_from_bam(const FactoryParams ¶ms); void fillin(DatagramIterator &scan, BamReader *manager); void fillin_recorder(DatagramIterator &scan, BamReader *manager); public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { TypedWritableReferenceCount::init_type(); Namable::init_type(); register_type(_type_handle, "PandaNode", TypedWritableReferenceCount::get_class_type(), Namable::get_class_type()); CData::init_type(); Down::init_type(); Up::init_type(); BamReaderAuxDataDown::init_type(); } virtual TypeHandle get_type() const { return get_class_type(); } virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; #ifndef DO_PIPELINING friend class PandaNode::Children; friend class PandaNode::Stashed; #endif friend class NodePath; friend class NodePathComponent; friend class WorkingNodePath; friend class PandaNodePipelineReader; friend class EggLoader; friend class Extension; friend class CullTraverserData; }; /** * Encapsulates the data from a PandaNode, pre-fetched for one stage of the * pipeline. */ class EXPCL_PANDA_PGRAPH PandaNodePipelineReader { public: INLINE PandaNodePipelineReader(const PandaNode *object, Thread *current_thread); INLINE PandaNodePipelineReader(const PandaNodePipelineReader ©); INLINE void operator = (const PandaNodePipelineReader ©); public: INLINE ~PandaNodePipelineReader(); ALLOC_DELETED_CHAIN(PandaNodePipelineReader); INLINE const PandaNode *get_node() const; INLINE Thread *get_current_thread() const; INLINE void release(); void check_cached(bool update_bounds) const; INLINE void compose_draw_mask(DrawMask &running_draw_mask) const; INLINE bool compare_draw_mask(DrawMask running_draw_mask, DrawMask camera_mask) const; INLINE int get_num_parents() const; INLINE PandaNode *get_parent(int n) const; INLINE int find_parent(PandaNode *node) const; INLINE int get_num_children() const; INLINE PandaNode *get_child(int n) const; INLINE int get_child_sort(int n) const; INLINE int find_child(PandaNode *node) const; INLINE int get_num_stashed() const; INLINE PandaNode *get_stashed(int n) const; INLINE int get_stashed_sort(int n) const; INLINE int find_stashed(PandaNode *node) const; INLINE const RenderState *get_state() const; INLINE const RenderEffects *get_effects() const; INLINE const TransformState *get_transform() const; INLINE const TransformState *get_prev_transform() const; INLINE std::string get_tag(const std::string &key) const; INLINE bool has_tag(const std::string &key) const; INLINE CollideMask get_net_collide_mask() const; INLINE const RenderAttrib *get_off_clip_planes() const; INLINE const BoundingVolume *get_bounds() const; INLINE int get_nested_vertices() const; INLINE bool is_final() const; INLINE int get_fancy_bits() const; INLINE PandaNode::Children get_children() const; INLINE PandaNode::Stashed get_stashed() const; INLINE PandaNode::Parents get_parents() const; private: const PandaNode *_node; Thread *_current_thread; const PandaNode::CData *_cdata; public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { register_type(_type_handle, "PandaNodePipelineReader"); } private: static TypeHandle _type_handle; }; // We can safely redefine this as a no-op. template<> INLINE void PointerToBase::update_type(To *ptr) {} INLINE std::ostream &operator << (std::ostream &out, const PandaNode &node) { node.output(out); return out; } #include "pandaNode.I" #endif